Closed viniciusmaia closed 4 years ago
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;
Would need a sample app to see what your issue is.
In my case, it was resolved by using Device.BeginInvokeOnMainThread: Xamarin.Forms.Device.BeginInvokeOnMainThread(() => IsRefreshing = false);
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.
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;
}
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; }); });
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; }
}
same problem here
Same problem :-)
This in the ViewModel solved the problem, Thanks @jochembroekhoff for the tip
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => IsRefreshing = false);
Same issue and same fix as above
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!
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.
Hello author, can you tell when this problem will be solved into NugetPackage?
Can someone create an ultra simple fully working sample app that reproduces this behavior?
Please use the RefreshView as this is officially deprecated now https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/refreshview
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}">
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.