Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PullToRefreshLayout - Refresher does not hide after set IsRefreshing to false #49

Closed
viniciusmaia opened this issue Apr 23, 2018 · 16 comments

Comments

@viniciusmaia
Copy link

I'm using PullToRefreshLayout with Prism MVVM, I have a bool property in ViewModel to bind for IsRefreshing called IsBusy and when I set IsBusy to false, the refresher still appear on the page.

@JunkyXL86
Copy link

JunkyXL86 commented Apr 25, 2018

I faced the same issue. I think it's because the IsRefreshing property does not recognize an immediate reset of the current value.
It seems that the refresher spins forever because it never got notified about the false value. So I inserted a await Task.Delay(50) so that the UI has enough time to reflect the property change.
->

IsRefreshing = true;
await Task.Delay(50);
IsRefreshing = false;

@jamesmontemagno
Copy link
Owner

Would need a sample app to see what your issue is.

@jochembroekhoff
Copy link

In my case, it was resolved by using Device.BeginInvokeOnMainThread: Xamarin.Forms.Device.BeginInvokeOnMainThread(() => IsRefreshing = false);

@meauris
Copy link

meauris commented Jun 25, 2018

Same issue here, I am using PullToRefreshLayout in the same situation as @viniciusmaia describes.
Both the 'solutions' of @JunkyXL86 and @jochembroekhoff provided me a workaround.

@KristofferBerge
Copy link

KristofferBerge commented Jul 13, 2018

There is definitely something up with the IsRefreshing property. In my case I am only able to update it if I directly reference the control. On Android

Xaml

            <refresh:PullToRefreshLayout x:Name="PullToRefreshLayout" IsPullToRefreshEnabled="True"
                                         RefreshCommand="{Binding RefreshCommand}"
                                         IsRefreshing="{Binding IsRefreshing}">
                <wwr:FormsWebView x:Name="WebViewElement" OnContentLoaded="OnContentLoaded"/>
            </refresh:PullToRefreshLayout>

Codebehind

        private ICommand refreshCommand;
        public ICommand RefreshCommand
        {
            get { return refreshCommand; }
            set
            {
                if (value == refreshCommand)
                    return;

                refreshCommand = value;
                OnPropertyChanged(nameof(RefreshCommand));
            }
        }

        private bool isRefreshing;
        public bool IsRefreshing
        {
            get { return isRefreshing; }
            set
            {
                //note that it's raising the property change event even when the value set is the same
                isRefreshing = value;
                OnPropertyChanged(nameof(IsRefreshing));
            }
        }
        public MyComponent()
        {
            InitializeComponent();
            RefreshCommand = new Command(RefreshPage, CanRefresh);
        }
        
        async void RefreshPage()
        {
            WebViewElement.Refresh();

            // Only way to remove the loading
            //PullToRefreshLayout.IsRefreshing = false;
            
            // Still nothing
            await Task.Delay(50);
            Device.BeginInvokeOnMainThread(() =>
            {
                IsRefreshing = false;
            });
        }

        // Even when webview has finished loading, updating the boolean has no effect
        private void OnContentLoaded(object sender, EventArgs e)
        {
            IsRefreshing = false;
        }

@SunnyMukherjee
Copy link

SunnyMukherjee commented Sep 26, 2018

Even though I am not a fan of using the MessagingCenter to message from the viewmodel class to the code-behind, I get around the IsRefreshing bug in this way.

I used the MainThread class in the Xamarin.Essentials package like below.

HomeViewModel.cs
MainThread.BeginInvokeOnMainThread ( () => { MessagingCenter.Send<HomeViewModel>(this, "RefreshFinished"); } );

HomePage.xaml.cs
MessagingCenter.Subscribe<HomeViewModel>(this, "RefreshFinished", (viewModel) => { MainThread.BeginInvokeOnMainThread(() => { this.PullToRefreshLayoutControl.IsRefreshing = false; }); });

@RonnyBansemer
Copy link

RonnyBansemer commented Dec 14, 2018

I'm facing the same problem:

XAML:

<ptr:PullToRefreshLayout IsPullToRefreshEnabled="{Binding Path=CloudService.IsReachable}"
                         RefreshCommand="{Binding Path=RefreshMasterDataCommand}"
                         IsRefreshing="{Binding Path=MasterDataIsRefreshing}">
   <ScrollView> 
      <!-- some stuff -->
   </ScrollView>
</ptr:PullToRefreshLayout>

ViewModel:

class MyVM : INotifyPropertyChanged
{
      DelegateCommand _refreshMasterDataCommand;
      public DelegateCommand RefreshMasterDataCommand => _refreshMasterDataCommand ?? (_refreshMasterDataCommand = new DelegateCommand(OnRefreshMasterDataExecuted));

      void OnRefreshMasterDataExecuted()
      {
         MasterDataIsRefreshing = true;
         NavigateAsync($"{nameof(Views.MasterDataUpdateModalPage)}", useModalNavigation: true);
         MasterDataIsRefreshing = false;
      }

      public bool MasterDataIsRefreshing { get; set; }
}

OnPropertyChanged is implemented using PropertyChanged.Fody and works fine for all other Properties.

ViewModel (even not working):

class MyVM : INotifyPropertyChanged
{
      DelegateCommand _refreshMasterDataCommand;
      public DelegateCommand RefreshMasterDataCommand => _refreshMasterDataCommand ?? (_refreshMasterDataCommand = new DelegateCommand(OnRefreshMasterDataExecuted));

      void OnRefreshMasterDataExecuted()
      {
         MasterDataIsRefreshing = true;
         NavigateAsync($"{nameof(Views.MasterDataUpdateModalPage)}", useModalNavigation: true);
         MainThread.BeginInvokeOnMainThread(() => MasterDataIsRefreshing = false);
      }

      public bool MasterDataIsRefreshing { get; set; }
}

@nielscup
Copy link

same problem here

@draco961
Copy link

draco961 commented Jan 29, 2019

Same problem :-)
This in the ViewModel solved the problem, Thanks @jochembroekhoff for the tip
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => IsRefreshing = false);

@wint100
Copy link

wint100 commented Feb 16, 2019

Same issue and same fix as above

@phlashdev
Copy link

I've used the layout in multiple views in our project. The same problem occurs only on one view, on the others everything works as expected. But I wasn't able yet to determine what is causing the issue.

Note: the "BeginInvokeOnMainThread"-Fix worked for me too!

@gaocan1992
Copy link

First I thought it is because the binding mode issue. So we cannot change value and let it propagate from view controller to layout itself. I modified the code like this:

/// <summary>
        /// The is refreshing property.
        /// </summary>
        public static readonly BindableProperty IsRefreshingProperty =
            BindableProperty.Create(nameof(IsRefreshing), typeof(bool), typeof(PullToRefreshLayout), false, BindingMode.TwoWay, propertyChanged: OnIsRefreshingChanged);

        protected static void OnIsRefreshingChanged(BindableObject bindable, object oldValue, object newValue) {
            var pullToRefreshLayout = bindable as PullToRefreshLayout;
            if (pullToRefreshLayout == null) return;
            pullToRefreshLayout.IsRefreshing = (bool)newValue;
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is refreshing.
        /// </summary>
        /// <value><c>true</c> if this instance is refreshing; otherwise, <c>false</c>.</value>
        public bool IsRefreshing {
            get { return (bool)GetValue(IsRefreshingProperty); }
            set { SetValue(IsRefreshingProperty, value); }
        }

However it doesn't work (it should but I could figure out why)

<controls:PullToRefreshLayout x:Name="PullToRefreshLayout"
                                  IsPullToRefreshEnabled="True"
                                  RefreshCommand="{Binding RefreshCommand}">
PullToRefreshLayout.IsRefreshing = true;
await LoadAsync();
PullToRefreshLayout.IsRefreshing = false;

This is what I have done to make it works.

@scriptBoris
Copy link

Hello author, can you tell when this problem will be solved into NugetPackage?

@JunkyXL86
Copy link

Can someone create an ultra simple fully working sample app that reproduces this behavior?

@jamesmontemagno
Copy link
Owner

Please use the RefreshView as this is officially deprecated now https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/refreshview

@predalpha
Copy link

predalpha commented Oct 15, 2022

I've recently encountered the same issue with the refreshView. I dont use prism but i use MVVM with the community control toolkit.
Solved by explicitely setting the binding mode to OneWay :

<RefreshView x:DataType="local:myViewModel" Padding="20,10" Command="{Binding PageAppearingCommand}" IsRefreshing="{Binding IsBusy, Mode=OneWay}">

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests