diff --git a/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt index a073ac4961c6..6d33e48264f0 100644 --- a/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ -#nullable enable +#nullable enable +override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void *REMOVED*~static readonly Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.DefaultForegroundColor -> Microsoft.Maui.Graphics.Color *REMOVED*~static readonly Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.DefaultTitleColor -> Microsoft.Maui.Graphics.Color *REMOVED*~static readonly Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.DefaultUnselectedColor -> Microsoft.Maui.Graphics.Color @@ -8,9 +9,9 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? propertyName = null) -> void ~override Microsoft.Maui.Controls.Handlers.Items.MauiRecyclerView.OnInterceptTouchEvent(Android.Views.MotionEvent e) -> bool ~override Microsoft.Maui.Controls.Handlers.Items.MauiRecyclerView.OnTouchEvent(Android.Views.MotionEvent e) -> bool -override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void -override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.OnHiddenChanged(bool hidden) -> void ~override Microsoft.Maui.Controls.Handlers.Items.RecyclerViewScrollListener.OnScrollStateChanged(AndroidX.RecyclerView.Widget.RecyclerView recyclerView, int newState) -> void ~override Microsoft.Maui.Controls.Handlers.Items.SelectableItemsViewAdapter.IsSelectionEnabled(Android.Views.ViewGroup parent, int viewType) -> bool +override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.OnHiddenChanged(bool hidden) -> void +override Microsoft.Maui.Controls.SwipeItemView.IsEnabledCore.get -> bool ~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt index 5099477ef08b..5ebf51b09272 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -1,15 +1,16 @@ -#nullable enable +#nullable enable +override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void override Microsoft.Maui.Controls.Handlers.Items.MauiCollectionView.AddSubview(UIKit.UIView! view) -> void -override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? propertyName = null) -> void override Microsoft.Maui.Controls.Handlers.Items2.CarouselViewController2.Dispose(bool disposing) -> void -~override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void -override Microsoft.Maui.Controls.Platform.Compatibility.ShellTableViewController.LoadView() -> void -*REMOVED*~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void -override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void ~override Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2.DisconnectHandler(UIKit.UIView platformView) -> void -~override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2.NumberOfSections(UIKit.UICollectionView collectionView) -> nint +override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2.UpdateFlowDirection() -> void +~override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void override Microsoft.Maui.Controls.Platform.Compatibility.ShellItemRenderer.ViewDidAppear(bool animated) -> void ~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.DidMoveToParentViewController(UIKit.UIViewController parent) -> void +*REMOVED*~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void +override Microsoft.Maui.Controls.Platform.Compatibility.ShellTableViewController.LoadView() -> void +override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? propertyName = null) -> void +override Microsoft.Maui.Controls.SwipeItemView.IsEnabledCore.get -> bool +~override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2.NumberOfSections(UIKit.UICollectionView collectionView) -> nint ~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void -override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2.UpdateFlowDirection() -> void diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index 5099477ef08b..5ebf51b09272 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -1,15 +1,16 @@ -#nullable enable +#nullable enable +override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void override Microsoft.Maui.Controls.Handlers.Items.MauiCollectionView.AddSubview(UIKit.UIView! view) -> void -override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? propertyName = null) -> void override Microsoft.Maui.Controls.Handlers.Items2.CarouselViewController2.Dispose(bool disposing) -> void -~override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void -override Microsoft.Maui.Controls.Platform.Compatibility.ShellTableViewController.LoadView() -> void -*REMOVED*~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void -override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void ~override Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2.DisconnectHandler(UIKit.UIView platformView) -> void -~override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2.NumberOfSections(UIKit.UICollectionView collectionView) -> nint +override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2.UpdateFlowDirection() -> void +~override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void override Microsoft.Maui.Controls.Platform.Compatibility.ShellItemRenderer.ViewDidAppear(bool animated) -> void ~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.DidMoveToParentViewController(UIKit.UIViewController parent) -> void +*REMOVED*~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void +override Microsoft.Maui.Controls.Platform.Compatibility.ShellTableViewController.LoadView() -> void +override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? propertyName = null) -> void +override Microsoft.Maui.Controls.SwipeItemView.IsEnabledCore.get -> bool +~override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2.NumberOfSections(UIKit.UICollectionView collectionView) -> nint ~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void -override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2.UpdateFlowDirection() -> void diff --git a/src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt index 145d755476e6..29fccd9e881f 100644 --- a/src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt @@ -1,5 +1,6 @@ -#nullable enable +#nullable enable override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? propertyName = null) -> void ~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void +override Microsoft.Maui.Controls.SwipeItemView.IsEnabledCore.get -> bool override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void diff --git a/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt index 145d755476e6..29fccd9e881f 100644 --- a/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt @@ -1,5 +1,6 @@ -#nullable enable +#nullable enable override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? propertyName = null) -> void ~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void +override Microsoft.Maui.Controls.SwipeItemView.IsEnabledCore.get -> bool override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void diff --git a/src/Controls/src/Core/SwipeView/SwipeItemView.cs b/src/Controls/src/Core/SwipeView/SwipeItemView.cs index 346ada5523f6..47c41f3d4ad5 100644 --- a/src/Controls/src/Core/SwipeView/SwipeItemView.cs +++ b/src/Controls/src/Core/SwipeView/SwipeItemView.cs @@ -2,6 +2,7 @@ using System; using System.ComponentModel; using System.Windows.Input; +using Microsoft.Maui.Controls.Internals; namespace Microsoft.Maui.Controls { @@ -9,16 +10,21 @@ namespace Microsoft.Maui.Controls /// Represents a swipe item that displays custom content in a . /// [ContentProperty(nameof(Content))] - public partial class SwipeItemView : ContentView, Controls.ISwipeItem, Maui.ISwipeItemView + public partial class SwipeItemView : ContentView, Controls.ISwipeItem, Maui.ISwipeItemView, ICommandElement { /// Bindable property for . public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(SwipeItemView), null, - propertyChanging: (bo, o, n) => ((SwipeItemView)bo).OnCommandChanging(), - propertyChanged: (bo, o, n) => ((SwipeItemView)bo).OnCommandChanged()); + propertyChanging: CommandElement.OnCommandChanging, + propertyChanged: CommandElement.OnCommandChanged); /// Bindable property for . public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(SwipeItemView), null, - propertyChanged: (bo, o, n) => ((SwipeItemView)bo).OnCommandParameterChanged()); + propertyChanged: CommandElement.OnCommandParameterChanged); + + static SwipeItemView() + { + CommandProperty.DependsOn(CommandParameterProperty); + } /// /// Gets or sets the command invoked when this swipe item is activated. This is a bindable property. @@ -52,35 +58,12 @@ public void OnInvoked() Invoked?.Invoke(this, EventArgs.Empty); } - void OnCommandChanged() - { - IsEnabled = Command?.CanExecute(CommandParameter) ?? true; - - if (Command == null) - return; - - Command.CanExecuteChanged += OnCommandCanExecuteChanged; - } - - void OnCommandChanging() - { - if (Command == null) - return; - - Command.CanExecuteChanged -= OnCommandCanExecuteChanged; - } - - void OnCommandParameterChanged() - { - if (Command == null) - return; + protected override bool IsEnabledCore => + base.IsEnabledCore && CommandElement.GetCanExecute(this, CommandProperty); - IsEnabled = Command.CanExecute(CommandParameter); - } + void ICommandElement.CanExecuteChanged(object sender, EventArgs e) => + RefreshIsEnabledProperty(); - void OnCommandCanExecuteChanged(object sender, EventArgs eventArgs) - { - IsEnabled = Command.CanExecute(CommandParameter); - } + WeakCommandSubscription ICommandElement.CleanupTracker { get; set; } } -} \ No newline at end of file +} diff --git a/src/Controls/tests/Core.UnitTests/CommandTests.cs b/src/Controls/tests/Core.UnitTests/CommandTests.cs index 3f4237785ca9..76b7bddbffaf 100644 --- a/src/Controls/tests/Core.UnitTests/CommandTests.cs +++ b/src/Controls/tests/Core.UnitTests/CommandTests.cs @@ -271,6 +271,8 @@ public void ExecuteDoesNotRunIfValueTypeAndSetToNull() [InlineData(typeof(SearchBar), false)] [InlineData(typeof(SearchHandler), true)] [InlineData(typeof(SearchHandler), false)] + [InlineData(typeof(SwipeItemView), true)] + [InlineData(typeof(SwipeItemView), false)] public async Task CommandsSubscribedToCanExecuteCollect(Type controlType, bool useWeakEventHandler) { // Create a view model with a Command @@ -310,6 +312,9 @@ public async Task CommandsSubscribedToCanExecuteCollect(Type controlType, bool u sh.Command = command; sh.ClearPlaceholderCommand = command; break; + case SwipeItemView siv: + siv.Command = command; + break; } // Create a weak reference to the button diff --git a/src/Controls/tests/Core.UnitTests/SwipeViewTests.cs b/src/Controls/tests/Core.UnitTests/SwipeViewTests.cs index 19cf3979fcdf..2118199daec3 100644 --- a/src/Controls/tests/Core.UnitTests/SwipeViewTests.cs +++ b/src/Controls/tests/Core.UnitTests/SwipeViewTests.cs @@ -414,6 +414,41 @@ public void TestSwipeItemView() Assert.NotEmpty(swipeView.LeftItems); } + [Fact] + public void SwipeItemViewCommandCanExecuteUpdatesIsEnabled() + { + var expectedParameter = new object(); + var canExecute = false; + var command = new Command( + _ => { }, + parameter => canExecute && ReferenceEquals(parameter, expectedParameter)); + var swipeItemView = new SwipeItemView + { + CommandParameter = expectedParameter, + Command = command + }; + + Assert.False(swipeItemView.IsEnabled); + + canExecute = true; + command.ChangeCanExecute(); + + Assert.True(swipeItemView.IsEnabled); + + swipeItemView.CommandParameter = new object(); + + Assert.False(swipeItemView.IsEnabled); + + swipeItemView.CommandParameter = expectedParameter; + + Assert.True(swipeItemView.IsEnabled); + + swipeItemView.IsEnabled = false; + command.ChangeCanExecute(); + + Assert.False(swipeItemView.IsEnabled); + } + [Fact] public void SwipeItemsRemainInLogicalTreeWhenContentIsSet() {