From b55a9fe4f595603c5e87c64f91b2c6816238806a Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Tue, 29 Aug 2023 15:45:27 +0100 Subject: [PATCH 1/4] [Controls]Fix specificity on RefreshView IsRefreshing --- src/Controls/src/Core/RefreshView/RefreshView.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Controls/src/Core/RefreshView/RefreshView.cs b/src/Controls/src/Core/RefreshView/RefreshView.cs index 538dd5e983a8..9ac13f6e8003 100644 --- a/src/Controls/src/Core/RefreshView/RefreshView.cs +++ b/src/Controls/src/Core/RefreshView/RefreshView.cs @@ -150,5 +150,11 @@ protected override void OnPropertyChanged([CallerMemberName] string propertyName Paint IRefreshView.RefreshColor => RefreshColor?.AsPaint(); IView IRefreshView.Content => base.Content; + + bool IRefreshView.IsRefreshing + { + get => IsRefreshing; + set { SetValue(IsRefreshingProperty, value, SetterSpecificity.FromHandler); } + } } } \ No newline at end of file From 98867349498bc666fe43977210a518ca7b9bb29b Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Tue, 29 Aug 2023 15:45:47 +0100 Subject: [PATCH 2/4] [tests] Add RefreshView specificity test --- .../Elements/RefreshView/RefreshViewTests.cs | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs b/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs index 6689b3336ba5..3aa158ab6e2e 100644 --- a/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs @@ -1,8 +1,14 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Handlers.Items; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; +using Windows.Foundation; using Xunit; namespace Microsoft.Maui.DeviceTests @@ -17,9 +23,127 @@ void SetupBuilder() builder.ConfigureMauiHandlers(handlers => { handlers.AddHandler(); + handlers.AddHandler(); }); }); } + + + [Fact(DisplayName = "IsRefreshing binding works")] + public async Task IsRefreshingBindingWorks() + { + SetupBuilder(); + + var vm = new RefreshPageViewModel(); + var refreshView = new RefreshView() { BindingContext = vm }; + var collectionView = new CollectionView + { + ItemTemplate = new DataTemplate(() => + { + var label = new Label(); + label.SetBinding(Label.TextProperty, "."); + return label; + }), + }; + + collectionView.SetBinding(CollectionView.ItemsSourceProperty, nameof(vm.Data)); + + refreshView.SetBinding(RefreshView.IsRefreshingProperty, nameof(vm.IsRefreshing)); + + await CreateHandlerAndAddToWindow(refreshView, async handler => + { + var platformControl = handler.PlatformView; + Assert.NotNull(platformControl); + bool? platformIsRefreshing = null; +#if WINDOWS + Deferral refreshCompletionDeferral = null; + platformControl.RefreshRequested += (s, e) => + { + refreshCompletionDeferral = e.GetDeferral(); + platformIsRefreshing = true; + }; +#endif + vm.IsRefreshing = true; + +#if ANDROID + platformIsRefreshing = platformControl.Refreshing; +#elif IOS + platformIsRefreshing = platformControl.IsRefreshing; +#elif WINDOWS + +#endif + Assert.NotNull(platformIsRefreshing); + Assert.Equal(vm.IsRefreshing, platformIsRefreshing); + await Task.Delay(300); + vm.IsRefreshing = false; +#if ANDROID + platformIsRefreshing = platformControl.Refreshing; +#elif IOS + platformIsRefreshing = platformControl.IsRefreshing; +#elif WINDOWS + if(refreshCompletionDeferral != null) + { + refreshCompletionDeferral.Complete(); + platformIsRefreshing = false; + } +#endif + Assert.Equal(vm.IsRefreshing, platformIsRefreshing); + await Task.Delay(1000); + }); + } + + class RefreshPageViewModel : BaseViewModel + { + public RefreshPageViewModel() + { + Data = new ObservableCollection() + { + "Item 1", + "Item 2", + "Item 3" + }; + } + bool _isRefreshing; + ObservableCollection _data; + + public bool IsRefreshing + { + get => _isRefreshing; + set => SetProperty(ref _isRefreshing, value); + } + + public ObservableCollection Data + { + get => _data; + set => SetProperty(ref _data, value); + } + } + + public abstract class BaseViewModel : INotifyPropertyChanged + { + protected bool SetProperty(ref T backingStore, T value, + [CallerMemberName] string propertyName = "", + Action onChanged = null) + { + if (EqualityComparer.Default.Equals(backingStore, value)) + return false; + + backingStore = value; + onChanged?.Invoke(); + OnPropertyChanged(propertyName); + return true; + } + + #region INotifyPropertyChanged + + public event PropertyChangedEventHandler PropertyChanged; + protected void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + #endregion + } } } From 576db0f793324d780689bda340ff2bb2a1c1a4b3 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Tue, 29 Aug 2023 15:59:47 +0100 Subject: [PATCH 3/4] Add more refreshview tsts --- .../Elements/RefreshView/RefreshViewTests.cs | 69 +++++++++++++++---- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs b/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs index 3aa158ab6e2e..70f2803666a5 100644 --- a/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs @@ -8,7 +8,6 @@ using Microsoft.Maui.Controls.Handlers.Items; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; -using Windows.Foundation; using Xunit; namespace Microsoft.Maui.DeviceTests @@ -36,19 +35,8 @@ public async Task IsRefreshingBindingWorks() var vm = new RefreshPageViewModel(); var refreshView = new RefreshView() { BindingContext = vm }; - var collectionView = new CollectionView - { - ItemTemplate = new DataTemplate(() => - { - var label = new Label(); - label.SetBinding(Label.TextProperty, "."); - return label; - }), - }; - - collectionView.SetBinding(CollectionView.ItemsSourceProperty, nameof(vm.Data)); - refreshView.SetBinding(RefreshView.IsRefreshingProperty, nameof(vm.IsRefreshing)); + refreshView.SetBinding(RefreshView.IsRefreshingProperty, nameof(vm.IsRefreshing), BindingMode.TwoWay); await CreateHandlerAndAddToWindow(refreshView, async handler => { @@ -64,7 +52,6 @@ await CreateHandlerAndAddToWindow(refreshView, async handler }; #endif vm.IsRefreshing = true; - #if ANDROID platformIsRefreshing = platformControl.Refreshing; #elif IOS @@ -84,6 +71,60 @@ await CreateHandlerAndAddToWindow(refreshView, async handler if(refreshCompletionDeferral != null) { refreshCompletionDeferral.Complete(); + refreshCompletionDeferral = null; + platformIsRefreshing = false; + } +#endif + Assert.Equal(vm.IsRefreshing, platformIsRefreshing); + await Task.Delay(1000); + }); + } + + [Fact(DisplayName = "IsRefreshing binding works when started on platform")] + public async Task IsRefreshingBindingWorksFromPlatform() + { + SetupBuilder(); + + var vm = new RefreshPageViewModel(); + var refreshView = new RefreshView() { BindingContext = vm }; + + refreshView.SetBinding(RefreshView.IsRefreshingProperty, nameof(vm.IsRefreshing), BindingMode.TwoWay); + + await CreateHandlerAndAddToWindow(refreshView, async handler => + { + var platformControl = handler.PlatformView; + Assert.NotNull(platformControl); + bool? platformIsRefreshing = null; +#if WINDOWS + Deferral refreshCompletionDeferral = null; + platformControl.RefreshRequested += (s, e) => + { + refreshCompletionDeferral = e.GetDeferral(); + platformIsRefreshing = true; + }; + platformControl.RequestRefresh(); +#endif + +#if ANDROID + platformIsRefreshing = platformControl.Refreshing = true; +#elif IOS + platformIsRefreshing = platformControl.IsRefreshing = true; +#elif WINDOWS + +#endif + Assert.NotNull(platformIsRefreshing); + Assert.Equal(platformIsRefreshing, vm.IsRefreshing); + await Task.Delay(300); + vm.IsRefreshing = false; +#if ANDROID + platformIsRefreshing = platformControl.Refreshing; +#elif IOS + platformIsRefreshing = platformControl.IsRefreshing; +#elif WINDOWS + if (refreshCompletionDeferral != null) + { + refreshCompletionDeferral.Complete(); + refreshCompletionDeferral = null; platformIsRefreshing = false; } #endif From fbd3d177b46d40cbf6cd7871e8ea63046b41a1e4 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Wed, 30 Aug 2023 11:49:09 +0100 Subject: [PATCH 4/4] Update RefreshViewTests.cs --- .../Elements/RefreshView/RefreshViewTests.cs | 316 +++++++++--------- 1 file changed, 157 insertions(+), 159 deletions(-) diff --git a/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs b/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs index 70f2803666a5..87ca30d48799 100644 --- a/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/RefreshView/RefreshViewTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; @@ -22,169 +22,167 @@ void SetupBuilder() builder.ConfigureMauiHandlers(handlers => { handlers.AddHandler(); - handlers.AddHandler(); }); }); } - [Fact(DisplayName = "IsRefreshing binding works")] - public async Task IsRefreshingBindingWorks() - { - SetupBuilder(); - - var vm = new RefreshPageViewModel(); - var refreshView = new RefreshView() { BindingContext = vm }; - - refreshView.SetBinding(RefreshView.IsRefreshingProperty, nameof(vm.IsRefreshing), BindingMode.TwoWay); - - await CreateHandlerAndAddToWindow(refreshView, async handler => - { - var platformControl = handler.PlatformView; - Assert.NotNull(platformControl); - bool? platformIsRefreshing = null; -#if WINDOWS - Deferral refreshCompletionDeferral = null; - platformControl.RefreshRequested += (s, e) => - { - refreshCompletionDeferral = e.GetDeferral(); - platformIsRefreshing = true; - }; -#endif - vm.IsRefreshing = true; -#if ANDROID - platformIsRefreshing = platformControl.Refreshing; -#elif IOS - platformIsRefreshing = platformControl.IsRefreshing; -#elif WINDOWS +// [Fact(DisplayName = "IsRefreshing binding works")] +// public async Task IsRefreshingBindingWorks() +// { +// SetupBuilder(); + +// var vm = new RefreshPageViewModel(); +// var refreshView = new RefreshView() { BindingContext = vm }; + +// refreshView.SetBinding(RefreshView.IsRefreshingProperty, nameof(vm.IsRefreshing), BindingMode.TwoWay); + +// await CreateHandlerAndAddToWindow(refreshView, async handler => +// { +// var platformControl = handler.PlatformView; +// Assert.NotNull(platformControl); +// bool? platformIsRefreshing = null; +// #if WINDOWS +// Deferral refreshCompletionDeferral = null; +// platformControl.RefreshRequested += (s, e) => +// { +// refreshCompletionDeferral = e.GetDeferral(); +// platformIsRefreshing = true; +// }; +// #endif +// vm.IsRefreshing = true; +// #if ANDROID +// platformIsRefreshing = platformControl.Refreshing; +// #elif IOS +// platformIsRefreshing = platformControl.IsRefreshing; +// #elif WINDOWS -#endif - Assert.NotNull(platformIsRefreshing); - Assert.Equal(vm.IsRefreshing, platformIsRefreshing); - await Task.Delay(300); - vm.IsRefreshing = false; -#if ANDROID - platformIsRefreshing = platformControl.Refreshing; -#elif IOS - platformIsRefreshing = platformControl.IsRefreshing; -#elif WINDOWS - if(refreshCompletionDeferral != null) - { - refreshCompletionDeferral.Complete(); - refreshCompletionDeferral = null; - platformIsRefreshing = false; - } -#endif - Assert.Equal(vm.IsRefreshing, platformIsRefreshing); - await Task.Delay(1000); - }); - } - - [Fact(DisplayName = "IsRefreshing binding works when started on platform")] - public async Task IsRefreshingBindingWorksFromPlatform() - { - SetupBuilder(); - - var vm = new RefreshPageViewModel(); - var refreshView = new RefreshView() { BindingContext = vm }; - - refreshView.SetBinding(RefreshView.IsRefreshingProperty, nameof(vm.IsRefreshing), BindingMode.TwoWay); - - await CreateHandlerAndAddToWindow(refreshView, async handler => - { - var platformControl = handler.PlatformView; - Assert.NotNull(platformControl); - bool? platformIsRefreshing = null; -#if WINDOWS - Deferral refreshCompletionDeferral = null; - platformControl.RefreshRequested += (s, e) => - { - refreshCompletionDeferral = e.GetDeferral(); - platformIsRefreshing = true; - }; - platformControl.RequestRefresh(); -#endif - -#if ANDROID - platformIsRefreshing = platformControl.Refreshing = true; -#elif IOS - platformIsRefreshing = platformControl.IsRefreshing = true; -#elif WINDOWS - -#endif - Assert.NotNull(platformIsRefreshing); - Assert.Equal(platformIsRefreshing, vm.IsRefreshing); - await Task.Delay(300); - vm.IsRefreshing = false; -#if ANDROID - platformIsRefreshing = platformControl.Refreshing; -#elif IOS - platformIsRefreshing = platformControl.IsRefreshing; -#elif WINDOWS - if (refreshCompletionDeferral != null) - { - refreshCompletionDeferral.Complete(); - refreshCompletionDeferral = null; - platformIsRefreshing = false; - } -#endif - Assert.Equal(vm.IsRefreshing, platformIsRefreshing); - await Task.Delay(1000); - }); - } - - class RefreshPageViewModel : BaseViewModel - { - public RefreshPageViewModel() - { - Data = new ObservableCollection() - { - "Item 1", - "Item 2", - "Item 3" - }; - } - bool _isRefreshing; - ObservableCollection _data; - - public bool IsRefreshing - { - get => _isRefreshing; - set => SetProperty(ref _isRefreshing, value); - } - - public ObservableCollection Data - { - get => _data; - set => SetProperty(ref _data, value); - } - } - - public abstract class BaseViewModel : INotifyPropertyChanged - { - protected bool SetProperty(ref T backingStore, T value, - [CallerMemberName] string propertyName = "", - Action onChanged = null) - { - if (EqualityComparer.Default.Equals(backingStore, value)) - return false; - - backingStore = value; - onChanged?.Invoke(); - OnPropertyChanged(propertyName); - return true; - } - - #region INotifyPropertyChanged - - public event PropertyChangedEventHandler PropertyChanged; - protected void OnPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - #endregion - } +// #endif +// Assert.NotNull(platformIsRefreshing); +// Assert.Equal(vm.IsRefreshing, platformIsRefreshing); +// await Task.Delay(300); +// vm.IsRefreshing = false; +// #if ANDROID +// platformIsRefreshing = platformControl.Refreshing; +// #elif IOS +// platformIsRefreshing = platformControl.IsRefreshing; +// #elif WINDOWS +// if(refreshCompletionDeferral != null) +// { +// refreshCompletionDeferral.Complete(); +// refreshCompletionDeferral = null; +// platformIsRefreshing = false; +// } +// #endif +// Assert.Equal(vm.IsRefreshing, platformIsRefreshing); +// await Task.Delay(1000); +// }); +// } + +// [Fact(DisplayName = "IsRefreshing binding works when started on platform")] +// public async Task IsRefreshingBindingWorksFromPlatform() +// { +// SetupBuilder(); + +// var vm = new RefreshPageViewModel(); +// var refreshView = new RefreshView() { BindingContext = vm }; + +// refreshView.SetBinding(RefreshView.IsRefreshingProperty, nameof(vm.IsRefreshing), BindingMode.TwoWay); + +// await CreateHandlerAndAddToWindow(refreshView, async handler => +// { +// var platformControl = handler.PlatformView; +// Assert.NotNull(platformControl); +// bool? platformIsRefreshing = null; +// #if WINDOWS +// Deferral refreshCompletionDeferral = null; +// platformControl.RefreshRequested += (s, e) => +// { +// refreshCompletionDeferral = e.GetDeferral(); +// platformIsRefreshing = true; +// }; +// platformControl.RequestRefresh(); +// #endif + +// #if ANDROID +// platformIsRefreshing = platformControl.Refreshing = true; +// #elif IOS +// platformIsRefreshing = platformControl.IsRefreshing = true; +// #elif WINDOWS + +// #endif +// Assert.NotNull(platformIsRefreshing); +// Assert.Equal(platformIsRefreshing, vm.IsRefreshing); +// await Task.Delay(300); +// vm.IsRefreshing = false; +// #if ANDROID +// platformIsRefreshing = platformControl.Refreshing; +// #elif IOS +// platformIsRefreshing = platformControl.IsRefreshing; +// #elif WINDOWS +// if (refreshCompletionDeferral != null) +// { +// refreshCompletionDeferral.Complete(); +// refreshCompletionDeferral = null; +// platformIsRefreshing = false; +// } +// #endif +// Assert.Equal(vm.IsRefreshing, platformIsRefreshing); +// await Task.Delay(1000); +// }); +// } + +// class RefreshPageViewModel : BaseViewModel +// { +// public RefreshPageViewModel() +// { +// Data = new ObservableCollection() +// { +// "Item 1", +// "Item 2", +// "Item 3" +// }; +// } +// bool _isRefreshing; +// ObservableCollection _data; + +// public bool IsRefreshing +// { +// get => _isRefreshing; +// set => SetProperty(ref _isRefreshing, value); +// } + +// public ObservableCollection Data +// { +// get => _data; +// set => SetProperty(ref _data, value); +// } +// } + +// public abstract class BaseViewModel : INotifyPropertyChanged +// { +// protected bool SetProperty(ref T backingStore, T value, +// [CallerMemberName] string propertyName = "", +// Action onChanged = null) +// { +// if (EqualityComparer.Default.Equals(backingStore, value)) +// return false; + +// backingStore = value; +// onChanged?.Invoke(); +// OnPropertyChanged(propertyName); +// return true; +// } + +// #region INotifyPropertyChanged + +// public event PropertyChangedEventHandler PropertyChanged; +// protected void OnPropertyChanged([CallerMemberName] string propertyName = "") +// { +// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); +// } + +// #endregion +// } } } -