Open AndreasReitberger opened 2 years ago
hi @AndreasReitberger, would you like to provide a sample project?
hi @AndreasReitberger, would you like to provide a sample project?
Sure, I'll create a sample and link it here.
I created a small repo showing the behavior. I cannot reproduce the issue in my app that the Command
gets called on startup, however it fires twice. Maybe this is the problem at all. I do call the ConnectCommand in code behind on startup (in my ported app, then also the RefreshView executes the command (called twice then).
Just set a breaking point to LoadingPageViewModel
=> Task ConnectAction()
Then click the "Refresh" Button
. The Command
get fires twice, if the same Command
is bound to the RefreshView
.
I'm also not able to pull to refresh in this case.
using Issue_6455.Models;
using System.Diagnostics;
namespace Issue_6455.ViewModels
{
public class LoadingPageViewModel : BaseViewModel
{
#region Properties
int _counter = 0;
public int Counter
{
get => _counter;
set
{
if (_counter == value) return;
_counter = value;
OnPropertyChanged();
}
}
#endregion
#region Constructor
public LoadingPageViewModel()
{
ConnectCommand = new Command(async () => await ConnectAction(), ConnectCommand_CanExcecute);
}
#endregion
#region Commands
public Command ConnectCommand { get; set; }
bool ConnectCommand_CanExcecute()
{
return !IsConnecting;
}
async Task ConnectAction()
{
try
{
Counter++;
IsConnecting = true;
await Task.Delay(2500);
}
catch (Exception exc)
{
// Log error
Debug.WriteLine(exc.Message);
}
IsConnecting = false;
}
#endregion
}
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Issue_6455.Views.LoadingPage"
xmlns:viewModels="clr-namespace:Issue_6455.ViewModels"
>
<ContentPage.BindingContext>
<viewModels:LoadingPageViewModel x:Name="ViewModel" />
</ContentPage.BindingContext>
<Grid>
<RefreshView
IsRefreshing="{Binding IsConnecting}"
Command="{Binding ConnectCommand}"
>
<Grid
RowDefinitions="*,80"
>
<Label
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
>
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="Command fired: " />
<Span Text="{Binding Counter}" />
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
<Button
Grid.Row="1"
Text="Refresh"
Command="{Binding ConnectCommand}"
Margin="20,10"
/>
</Grid>
</RefreshView>
</Grid>
</ContentPage>
Repo: Issue_6456.zip
Verified repro on iOS 15.4 with VS 17.3.0 Preview 1.0 [32414.199.main]. Repro project: Issue_6456.zip
Any news when this will be fixed? Thank you!
Reproduced on Windows with VS17.3.0 Preview 2.0. I'm using Shell navigation and I call the RefreshCommand on OnNavigatedTo.
Call stack of first call:
[Namespace].Maui.dll![Namespace].Maui.PageModels.EntitiesPageViewModel<[Namespace].Common.Models.TimeEntry, int, [Namespace].Maui.Models.TimeEntryViewModel>.Refresh() Line 35
at [ProjectPath]\PageModels\EntitiesPageViewModel.cs(35)
CommunityToolkit.Mvvm.dll!CommunityToolkit.Mvvm.Input.AsyncRelayCommand.ExecuteAsync(object parameter)
[Namespace].Maui.dll![Namespace].Maui.Pages.TimeEntriesPage.OnNavigatedTo(Microsoft.Maui.Controls.NavigatedToEventArgs args) Line 21
at [ProjectPath]\Pages\TimeEntriesPage.xaml.cs(21)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Page.SendNavigatedTo(Microsoft.Maui.Controls.NavigatedToEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Shell.SendNavigated(Microsoft.Maui.Controls.ShellNavigatedEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Shell..ctor.AnonymousMethod__169_0(object _, Microsoft.Maui.Controls.ShellNavigatedEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellNavigationManager.HandleNavigated.__FireNavigatedEvents|1(Microsoft.Maui.Controls.ShellNavigatedEventArgs a, Microsoft.Maui.Controls.Shell shell)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellNavigationManager.HandleNavigated.AnonymousMethod__0()
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BaseShellItem.OnAppearing(System.Action action)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellNavigationManager.HandleNavigated(Microsoft.Maui.Controls.ShellNavigatedEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Shell.Microsoft.Maui.Controls.IShellController.UpdateCurrentState(Microsoft.Maui.Controls.ShellNavigationSource source)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellItem.OnCurrentItemChanged(Microsoft.Maui.Controls.BindableObject bindable, object oldValue, object newValue)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValueActual(Microsoft.Maui.Controls.BindableProperty property, Microsoft.Maui.Controls.BindableObject.BindablePropertyContext context, object value, bool currentlyApplying, Microsoft.Maui.Controls.Internals.SetValueFlags attributes, bool silent)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValueCore(Microsoft.Maui.Controls.BindableProperty property, object value, Microsoft.Maui.Controls.Internals.SetValueFlags attributes, Microsoft.Maui.Controls.BindableObject.SetValuePrivateFlags privateAttributes)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValue(Microsoft.Maui.Controls.BindableProperty property, object value, bool fromStyle, bool checkAccess)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValue(Microsoft.Maui.Controls.BindableProperty property, object value)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellItem.CurrentItem.set(Microsoft.Maui.Controls.ShellSection value)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellItem.CreateFromShellSection(Microsoft.Maui.Controls.ShellSection shellSection)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellItem.implicit operator Microsoft.Maui.Controls.ShellItem(Microsoft.Maui.Controls.ShellSection shellSection)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Handlers.ShellItemHandler.OnNavigationTabChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
Microsoft.WinUI.dll!WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_global__Microsoft_UI_Xaml_Controls_NavigationView__global__Microsoft_UI_Xaml_Controls_NavigationViewSelectionChangedEventArgs_.EventState.GetEventInvoke.AnonymousMethod__1_0(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
Microsoft.Windows.SDK.NET.dll!ABI.Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.NavigationView, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs>.Do_Abi_Invoke<System.IntPtr, System.IntPtr>(void* thisPtr, System.IntPtr sender, System.IntPtr args)
[Native to Managed Transition]
[Managed to Native Transition]
Microsoft.WinUI.dll!ABI.Microsoft.UI.Xaml.IApplicationStaticsMethods.Start(WinRT.IObjectReference _obj, Microsoft.UI.Xaml.ApplicationInitializationCallback callback)
Microsoft.WinUI.dll!Microsoft.UI.Xaml.Application.Start(Microsoft.UI.Xaml.ApplicationInitializationCallback callback)
[Namespace].Maui.dll![Namespace].Maui.WinUI.Program.Main(string[] args) Line 31
at [ProjectPath]\obj\Debug\net6.0-windows10.0.19041.0\win10-x64\Platforms\Windows\App.g.i.cs(31)
Call stack of second call:
[Namespace].Maui.dll![Namespace].Maui.PageModels.EntitiesPageViewModel<[Namespace].Common.Models.TimeEntry, int, [Namespace].Maui.Models.TimeEntryViewModel>.Refresh() Line 35
at [ProjectPath]\PageModels\EntitiesPageViewModel.cs(35)
CommunityToolkit.Mvvm.dll!CommunityToolkit.Mvvm.Input.AsyncRelayCommand.ExecuteAsync(object parameter)
CommunityToolkit.Mvvm.dll!CommunityToolkit.Mvvm.Input.AsyncRelayCommand.Execute(object parameter)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.RefreshView.OnIsRefreshingPropertyChanged(Microsoft.Maui.Controls.BindableObject bindable, object oldValue, object newValue)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValueActual(Microsoft.Maui.Controls.BindableProperty property, Microsoft.Maui.Controls.BindableObject.BindablePropertyContext context, object value, bool currentlyApplying, Microsoft.Maui.Controls.Internals.SetValueFlags attributes, bool silent)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValueCore(Microsoft.Maui.Controls.BindableProperty property, object value, Microsoft.Maui.Controls.Internals.SetValueFlags attributes, Microsoft.Maui.Controls.BindableObject.SetValuePrivateFlags privateAttributes)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindingExpression.ApplyCore(object sourceObject, Microsoft.Maui.Controls.BindableObject target, Microsoft.Maui.Controls.BindableProperty property, bool fromTarget)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindingExpression.Apply(bool fromTarget)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindingExpression.BindingExpressionPart.PropertyChanged.AnonymousMethod__49_0()
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.DispatcherExtensions.DispatchIfRequired(Microsoft.Maui.Dispatching.IDispatcher dispatcher, System.Action action)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindingExpression.BindingExpressionPart.PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindingExpression.WeakPropertyChangedProxy.OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
CommunityToolkit.Mvvm.dll!CommunityToolkit.Mvvm.Input.AsyncRelayCommand.ExecutionTask.set(System.Threading.Tasks.Task value)
CommunityToolkit.Mvvm.dll!CommunityToolkit.Mvvm.Input.AsyncRelayCommand.ExecuteAsync(object parameter)
[Namespace].Maui.dll![Namespace].Maui.Pages.TimeEntriesPage.OnNavigatedTo(Microsoft.Maui.Controls.NavigatedToEventArgs args) Line 21
at [ProjectPath]\Pages\TimeEntriesPage.xaml.cs(21)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Page.SendNavigatedTo(Microsoft.Maui.Controls.NavigatedToEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Shell.SendNavigated(Microsoft.Maui.Controls.ShellNavigatedEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Shell..ctor.AnonymousMethod__169_0(object _, Microsoft.Maui.Controls.ShellNavigatedEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellNavigationManager.HandleNavigated.__FireNavigatedEvents|1(Microsoft.Maui.Controls.ShellNavigatedEventArgs a, Microsoft.Maui.Controls.Shell shell)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellNavigationManager.HandleNavigated.AnonymousMethod__0()
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BaseShellItem.OnAppearing(System.Action action)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellNavigationManager.HandleNavigated(Microsoft.Maui.Controls.ShellNavigatedEventArgs args)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Shell.Microsoft.Maui.Controls.IShellController.UpdateCurrentState(Microsoft.Maui.Controls.ShellNavigationSource source)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellItem.OnCurrentItemChanged(Microsoft.Maui.Controls.BindableObject bindable, object oldValue, object newValue)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValueActual(Microsoft.Maui.Controls.BindableProperty property, Microsoft.Maui.Controls.BindableObject.BindablePropertyContext context, object value, bool currentlyApplying, Microsoft.Maui.Controls.Internals.SetValueFlags attributes, bool silent)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValueCore(Microsoft.Maui.Controls.BindableProperty property, object value, Microsoft.Maui.Controls.Internals.SetValueFlags attributes, Microsoft.Maui.Controls.BindableObject.SetValuePrivateFlags privateAttributes)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValue(Microsoft.Maui.Controls.BindableProperty property, object value, bool fromStyle, bool checkAccess)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.BindableObject.SetValue(Microsoft.Maui.Controls.BindableProperty property, object value)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellItem.CurrentItem.set(Microsoft.Maui.Controls.ShellSection value)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellItem.CreateFromShellSection(Microsoft.Maui.Controls.ShellSection shellSection)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellItem.implicit operator Microsoft.Maui.Controls.ShellItem(Microsoft.Maui.Controls.ShellSection shellSection)
Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Handlers.ShellItemHandler.OnNavigationTabChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
Microsoft.WinUI.dll!WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_global__Microsoft_UI_Xaml_Controls_NavigationView__global__Microsoft_UI_Xaml_Controls_NavigationViewSelectionChangedEventArgs_.EventState.GetEventInvoke.AnonymousMethod__1_0(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
Microsoft.Windows.SDK.NET.dll!ABI.Windows.Foundation.TypedEventHandler<Microsoft.UI.Xaml.Controls.NavigationView, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs>.Do_Abi_Invoke<System.IntPtr, System.IntPtr>(void* thisPtr, System.IntPtr sender, System.IntPtr args)
[Native to Managed Transition]
[Managed to Native Transition]
Microsoft.WinUI.dll!ABI.Microsoft.UI.Xaml.IApplicationStaticsMethods.Start(WinRT.IObjectReference _obj, Microsoft.UI.Xaml.ApplicationInitializationCallback callback)
Microsoft.WinUI.dll!Microsoft.UI.Xaml.Application.Start(Microsoft.UI.Xaml.ApplicationInitializationCallback callback)
[Namespace].Maui.dll![Namespace].Maui.WinUI.Program.Main(string[] args) Line 31
at [ProjectPath]\obj\Debug\net6.0-windows10.0.19041.0\win10-x64\Platforms\Windows\App.g.i.cs(31)
The IsRefreshing
change is executing the RefreshCommand
, is that correct?
Currently the IsRefreshing
property acts as a switch to start the Command (if set from false to true). Maybe that's intended, but imho it's misleading and the OnIsRefreshingPropertyChanged
should not invoke the command. At least it conflicts with the documentation which says:
IsRefreshing
, of type bool
, which indicates the current state of the RefreshView
.
@AndreasReitberger as a workaround:
Add
if (IsConnecting) return;
as the first statement in ConnectAction()
I came to this issue too.
However, it is explained here that we can use the CanExecute
delegate to enable or disable the RefreshView.Command
.
I no longer experience the double call issue since I specified that the command should only execute when it is not already running.
I use CommunityToolkit.Mvvm.Input package and its IAsyncRelayCommand
In GalleryView.xaml
<RefreshView
IsRefreshing="{Binding RefreshCommand.IsRunning}"
Command="{Binding RefreshCommand}">
<CollectionView ItemsSource="{Binding Photos}"
ItemSizingStrategy="MeasureFirstItem">
<!-- ... -->
</CollectionView>
</RefreshView>
In GalleryViewModel
public IAsyncRelayCommand RefreshCommand { get; }
public GalleryViewModel()
{
RefreshCommand = new AsyncRelayCommand(LoadGalleryAsync, () => RefreshCommand.IsRunning == false);
}
private async Task LoadGalleryAsync()
{
// load data from service
}
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
Still seeing this issue on the latest version. The workaround is working, though. But this should actually be fixed.. The main issue still is, that the RefreshView
fires the command twice if the pull down gesture is used.
Maybe related to?
https://github.com/xamarin/Xamarin.Forms/issues/7803
@PureWeen @jsuarezruiz
The documentation now states that setting IsRefreshing
to true
on RefreshView
will invoke the command.
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/refreshview?view=net-maui-7.0
Manually setting the IsRefreshing property to true will trigger the refresh visualization, and will execute the ICommand defined by the Command property.
It is also the reason why we are experiencing this issue (and probably it is not a bug but by design) because I think always the logic of the app is that we initially call something like ReloadData
(which is also our RefreshView
command) upon page visit and we set IsRefreshing=true
in ViewModel to also display the loading animation (which has a side effect of executing the function ReloadData
again).
We could set IsRefreshing=true
in the ViewModel
instead of calling ReloadData
to achieve the correct behavior but that is not a good idea because we cannot assume from the ViewModel
that setting property IsRefreshing
will call the ReloadData
method by coincidence (we don't know in ViewModel
that IsRefreshing
is bound to a RefreshView
control in the View
which also by coincidence calls ReloadData
via a registered Command
).
I think the issue here is that IsRefreshing
property on RefreshView
has two responsibilities instead of 1 (setting it has two effects).
When we only need 1., we also receive 2. which we don't actually want in the use case I explained.
I think the solution could be:
IsRefreshing=true
calls the associated commandRefreshView
control API which will split the responsibilitiesExample use case in my app:
I have a RefreshView and everytime I visit the page, the command gets executed twice. Repro: https://github.com/janseris/MauiRefreshViewCommandCalledTwiceOnAppearingBug
How to repro repeatedly in the app:
All
and click Login. Then click Logout in shell flyout menu.
This will send you back to Login screen where the Command will be invoked twice again.
This will happen everytime you visit the Login page.How to observe the command being called twice:
I added static counters and prints (Debug.WriteLine
).
Example output:
[0:] 09.04.2023 0:16:14: Total called LoginPage.OnAppearing: 1 times
[0:] 09.04.2023 0:16:14: Called LoginViewModel OnAppearing via toolkit:EventToCommandBehavior hooked to LoginPage.OnAppearing event. This also which calls ReloadUsersCommand. Total: 1 times.
[0:] 09.04.2023 0:16:14: Total started command: 1 times
[0:] 09.04.2023 0:16:14: Total started command: 2 times
Thanks for your input. As far as I understood, the RefreshView
fires the Command
once the IsRefreshing
is set true.
Coming from my XForm app, I had to set the binded property to true
in the command to trigger the loading animation.
I need to try what happens if I do not set the IsRefreshing
property manually. Is the animation shown then?
For me it does not make sense to trigger the Command
by setting the binded boolean to true.
Thanks for your input. As far as I understood, the
RefreshView
fires theCommand
once theIsRefreshing
is set true. Coming from my XForm app, I had to set the binded property totrue
in the command to trigger the loading animation. I need to try what happens if I do not set theIsRefreshing
property manually. Is the animation shown then?For me it does not make sense to trigger the
Command
by setting the binded boolean to true.
The animation is shown is equivalent to IsRefreshing=true
Hi @janseris !! Thank you for following up!
I removed the triaged and verified labels because we're doing a pass on all older issues to see if they are in a different state in the latest public versions. I'll update this one, since you've kindly done that for us!
i'm facing the exact same behavior, any update on this? tested with Android
I can reproduce this same issue on Android
Description
I'm using a RefreshView and noticed that the command is fired on the initial page loading. Is this intended?
Steps to Reproduce
Version with bug
Release Candidate 1 (current)
Last version that worked well
Unknown/Other
Affected platforms
iOS, I was not able test on other platforms
Affected platform versions
iOS
Did you find any workaround?
Add a "IsStartup" boolean to the CanExecute method for the Command binded to the RefreshView
Relevant log output
No response