diff --git a/src/Controls/src/Core/BindableObject.cs b/src/Controls/src/Core/BindableObject.cs index 11ef8b8cac64..f205dbe7e611 100644 --- a/src/Controls/src/Core/BindableObject.cs +++ b/src/Controls/src/Core/BindableObject.cs @@ -432,25 +432,6 @@ internal bool GetIsBound(BindableProperty targetProperty) return bpcontext != null && bpcontext.Bindings.Count > 0; } - /// - /// Forces the binding for the specified property to apply immediately. - /// This is used when one property depends on another and needs the dependent - /// property's binding to resolve before proceeding. - /// See https://github.com/dotnet/maui/issues/31939 - /// - internal void ForceBindingApply(BindableProperty targetProperty) - { - if (targetProperty == null) - throw new ArgumentNullException(nameof(targetProperty)); - - BindablePropertyContext bpcontext = GetContext(targetProperty); - if (bpcontext == null || bpcontext.Bindings.Count == 0) - return; - - // Force the binding to apply now - ApplyBinding(bpcontext, fromBindingContextChanged: false); - } - internal virtual void OnRemoveDynamicResource(BindableProperty property) { } diff --git a/src/Controls/src/Core/BindableProperty.cs b/src/Controls/src/Core/BindableProperty.cs index ad7eed736eee..ed771a2c94c8 100644 --- a/src/Controls/src/Core/BindableProperty.cs +++ b/src/Controls/src/Core/BindableProperty.cs @@ -252,21 +252,6 @@ public sealed class BindableProperty internal ValidateValueDelegate ValidateValue { get; private set; } - // Properties that this property depends on - when getting this property's value, - // if the dependency has a pending binding, return the default value instead. - // This is used to fix timing issues where one property binding resolves before another. - // See https://github.com/dotnet/maui/issues/31939 - internal BindableProperty[] Dependencies { get; private set; } - - /// - /// Registers a dependency on another BindableProperty. When this property's value is retrieved, - /// if the dependency has a binding that hasn't resolved yet (value is null), return null. - /// - internal void DependsOn(params BindableProperty[] dependencies) - { - Dependencies = dependencies; - } - /// Creates a new instance of the BindableProperty class. /// The name of the BindableProperty. /// The type of the property. diff --git a/src/Controls/src/Core/Button/Button.cs b/src/Controls/src/Core/Button/Button.cs index cc87be1f60c8..39caa9c6a301 100644 --- a/src/Controls/src/Core/Button/Button.cs +++ b/src/Controls/src/Core/Button/Button.cs @@ -465,7 +465,7 @@ void ICommandElement.CanExecuteChanged(object sender, EventArgs e) => RefreshIsEnabledProperty(); protected override bool IsEnabledCore => - base.IsEnabledCore && CommandElement.GetCanExecute(this, CommandProperty); + base.IsEnabledCore && CommandElement.GetCanExecute(this); bool _wasImageLoading; diff --git a/src/Controls/src/Core/Button/ButtonElement.cs b/src/Controls/src/Core/Button/ButtonElement.cs index 99869fa7c3d9..00f5c3ff71d1 100644 --- a/src/Controls/src/Core/Button/ButtonElement.cs +++ b/src/Controls/src/Core/Button/ButtonElement.cs @@ -10,27 +10,16 @@ static class ButtonElement /// /// The backing store for the bindable property. /// - public static readonly BindableProperty CommandProperty; + public static readonly BindableProperty CommandProperty = BindableProperty.Create( + nameof(IButtonElement.Command), typeof(ICommand), typeof(IButtonElement), null, + propertyChanging: CommandElement.OnCommandChanging, propertyChanged: CommandElement.OnCommandChanged); /// /// The backing store for the bindable property. /// - public static readonly BindableProperty CommandParameterProperty; - - static ButtonElement() - { - CommandParameterProperty = BindableProperty.Create( - nameof(IButtonElement.CommandParameter), typeof(object), typeof(IButtonElement), null, - propertyChanged: CommandElement.OnCommandParameterChanged); - - CommandProperty = BindableProperty.Create( - nameof(IButtonElement.Command), typeof(ICommand), typeof(IButtonElement), null, - propertyChanging: CommandElement.OnCommandChanging, propertyChanged: CommandElement.OnCommandChanged); - - // Register dependency: Command depends on CommandParameter for CanExecute evaluation - // See https://github.com/dotnet/maui/issues/31939 - CommandProperty.DependsOn(CommandParameterProperty); - } + public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create( + nameof(IButtonElement.CommandParameter), typeof(object), typeof(IButtonElement), null, + propertyChanged: CommandElement.OnCommandParameterChanged); /// /// The string identifier for the pressed visual state of this control. diff --git a/src/Controls/src/Core/Cells/TextCell.cs b/src/Controls/src/Core/Cells/TextCell.cs index 0b22d618a9f1..fb976ead8e4b 100644 --- a/src/Controls/src/Core/Cells/TextCell.cs +++ b/src/Controls/src/Core/Cells/TextCell.cs @@ -11,28 +11,19 @@ namespace Microsoft.Maui.Controls public class TextCell : Cell, ICommandElement { /// Bindable property for . - public static readonly BindableProperty CommandProperty; + public static readonly BindableProperty CommandProperty = + BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(TextCell), + propertyChanging: CommandElement.OnCommandChanging, + propertyChanged: CommandElement.OnCommandChanged); /// Bindable property for . - public static readonly BindableProperty CommandParameterProperty; - - static TextCell() - { - CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), + public static readonly BindableProperty CommandParameterProperty = + BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(TextCell), null, propertyChanged: CommandElement.OnCommandParameterChanged); - CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(TextCell), - propertyChanging: CommandElement.OnCommandChanging, - propertyChanged: CommandElement.OnCommandChanged); - - // Register dependency: Command depends on CommandParameter for CanExecute evaluation - // See https://github.com/dotnet/maui/issues/31939 - CommandProperty.DependsOn(CommandParameterProperty); - } - /// Bindable property for . public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(TextCell), default(string)); @@ -104,7 +95,10 @@ protected internal override void OnTapped() void ICommandElement.CanExecuteChanged(object sender, EventArgs eventArgs) { - IsEnabled = CommandElement.GetCanExecute(this, CommandProperty); + if (Command is null) + return; + + IsEnabled = Command.CanExecute(CommandParameter); } WeakCommandSubscription ICommandElement.CleanupTracker { get; set; } diff --git a/src/Controls/src/Core/CheckBox/CheckBox.Mapper.cs b/src/Controls/src/Core/CheckBox/CheckBox.Mapper.cs index 4453b3727b7c..d777dd9c45f1 100644 --- a/src/Controls/src/Core/CheckBox/CheckBox.Mapper.cs +++ b/src/Controls/src/Core/CheckBox/CheckBox.Mapper.cs @@ -9,13 +9,7 @@ namespace Microsoft.Maui.Controls { public partial class CheckBox { - static CheckBox() - { - // Register dependency: Command depends on CommandParameter for CanExecute evaluation - // See https://github.com/dotnet/maui/issues/31939 - CommandProperty.DependsOn(CommandParameterProperty); - RemapForControls(); - } + static CheckBox() => RemapForControls(); private new static void RemapForControls() { diff --git a/src/Controls/src/Core/CheckBox/CheckBox.cs b/src/Controls/src/Core/CheckBox/CheckBox.cs index 60f6d77b23c6..f13ab47ff78e 100644 --- a/src/Controls/src/Core/CheckBox/CheckBox.cs +++ b/src/Controls/src/Core/CheckBox/CheckBox.cs @@ -145,7 +145,7 @@ void ICommandElement.CanExecuteChanged(object sender, EventArgs e) => RefreshIsEnabledProperty(); protected override bool IsEnabledCore => - base.IsEnabledCore && CommandElement.GetCanExecute(this, CommandProperty); + base.IsEnabledCore && CommandElement.GetCanExecute(this); public Paint Foreground => Color?.AsPaint(); bool ICheckBox.IsChecked diff --git a/src/Controls/src/Core/CommandElement.cs b/src/Controls/src/Core/CommandElement.cs index 767badd9fbfa..6a4a70de5519 100644 --- a/src/Controls/src/Core/CommandElement.cs +++ b/src/Controls/src/Core/CommandElement.cs @@ -37,23 +37,11 @@ public static void OnCommandParameterChanged(BindableObject bo, object o, object commandElement.CanExecuteChanged(bo, EventArgs.Empty); } - public static bool GetCanExecute(ICommandElement commandElement, BindableProperty? commandProperty = null) + public static bool GetCanExecute(ICommandElement commandElement) { if (commandElement.Command == null) return true; - // If there are dependencies (e.g., CommandParameter for Command), force their bindings - // to apply before evaluating CanExecute. This fixes timing issues where Command binding - // resolves before CommandParameter binding during reparenting. - // See https://github.com/dotnet/maui/issues/31939 - if (commandProperty?.Dependencies is not null && commandElement is BindableObject bo) - { - foreach (var dependency in commandProperty.Dependencies) - { - bo.ForceBindingApply(dependency); - } - } - return commandElement.Command.CanExecute(commandElement.CommandParameter); } } diff --git a/src/Controls/src/Core/ImageButton/ImageButton.cs b/src/Controls/src/Core/ImageButton/ImageButton.cs index 6262771ca9e3..73ecfed62810 100644 --- a/src/Controls/src/Core/ImageButton/ImageButton.cs +++ b/src/Controls/src/Core/ImageButton/ImageButton.cs @@ -244,7 +244,7 @@ bool IImageElement.IsAnimationPlaying bool IImageController.GetLoadAsAnimation() => false; protected override bool IsEnabledCore => - base.IsEnabledCore && CommandElement.GetCanExecute(this, CommandProperty); + base.IsEnabledCore && CommandElement.GetCanExecute(this); void ICommandElement.CanExecuteChanged(object sender, EventArgs e) => RefreshIsEnabledProperty(); diff --git a/src/Controls/src/Core/Menu/MenuItem.cs b/src/Controls/src/Core/Menu/MenuItem.cs index 59577062ed00..fd7f8260b0fc 100644 --- a/src/Controls/src/Core/Menu/MenuItem.cs +++ b/src/Controls/src/Core/Menu/MenuItem.cs @@ -12,26 +12,15 @@ namespace Microsoft.Maui.Controls public partial class MenuItem : BaseMenuItem, IMenuItemController, ICommandElement, IMenuElement, IPropertyPropagationController { /// Bindable property for . - public static readonly BindableProperty CommandProperty; + public static readonly BindableProperty CommandProperty = BindableProperty.Create( + nameof(Command), typeof(ICommand), typeof(MenuItem), null, + propertyChanging: CommandElement.OnCommandChanging, + propertyChanged: CommandElement.OnCommandChanged); /// Bindable property for . - public static readonly BindableProperty CommandParameterProperty; - - static MenuItem() - { - CommandParameterProperty = BindableProperty.Create( - nameof(CommandParameter), typeof(object), typeof(MenuItem), null, - propertyChanged: CommandElement.OnCommandParameterChanged); - - CommandProperty = BindableProperty.Create( - nameof(Command), typeof(ICommand), typeof(MenuItem), null, - propertyChanging: CommandElement.OnCommandChanging, - propertyChanged: CommandElement.OnCommandChanged); - - // Register dependency: Command depends on CommandParameter for CanExecute evaluation - // See https://github.com/dotnet/maui/issues/31939 - CommandProperty.DependsOn(CommandParameterProperty); - } + public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create( + nameof(CommandParameter), typeof(object), typeof(MenuItem), null, + propertyChanged: CommandElement.OnCommandParameterChanged); /// Bindable property for . public static readonly BindableProperty IsDestructiveProperty = BindableProperty.Create(nameof(IsDestructive), typeof(bool), typeof(MenuItem), false); @@ -133,7 +122,7 @@ static object CoerceIsEnabledProperty(BindableObject bindable, object value) return false; } - var canExecute = CommandElement.GetCanExecute(menuItem, CommandProperty); + var canExecute = CommandElement.GetCanExecute(menuItem); if (!canExecute) { return false; diff --git a/src/Controls/src/Core/RefreshView/RefreshView.Mapper.cs b/src/Controls/src/Core/RefreshView/RefreshView.Mapper.cs index 038f56fb9277..41329181883d 100644 --- a/src/Controls/src/Core/RefreshView/RefreshView.Mapper.cs +++ b/src/Controls/src/Core/RefreshView/RefreshView.Mapper.cs @@ -6,13 +6,6 @@ namespace Microsoft.Maui.Controls { public partial class RefreshView { - static RefreshView() - { - // Register dependency: Command depends on CommandParameter for CanExecute evaluation - // See https://github.com/dotnet/maui/issues/31939 - CommandProperty.DependsOn(CommandParameterProperty); - } - internal static new void RemapForControls() { // Adjust the mappings to preserve Controls.RefreshView legacy behaviors diff --git a/src/Controls/src/Core/RefreshView/RefreshView.cs b/src/Controls/src/Core/RefreshView/RefreshView.cs index 564806dbeccb..ff2de69c5e81 100644 --- a/src/Controls/src/Core/RefreshView/RefreshView.cs +++ b/src/Controls/src/Core/RefreshView/RefreshView.cs @@ -121,7 +121,7 @@ static object CoerceIsRefreshEnabledProperty(BindableObject bindable, object val if (bindable is RefreshView refreshView) { refreshView._isRefreshEnabledExplicit = (bool)value; - return refreshView._isRefreshEnabledExplicit && CommandElement.GetCanExecute(refreshView, CommandProperty); + return refreshView._isRefreshEnabledExplicit && CommandElement.GetCanExecute(refreshView); } return false; diff --git a/src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs b/src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs index 3278ba05f637..0bfd6cc6806f 100644 --- a/src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs +++ b/src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs @@ -6,13 +6,6 @@ namespace Microsoft.Maui.Controls { public partial class SearchBar { - static SearchBar() - { - // Register dependency: SearchCommand depends on SearchCommandParameter for CanExecute evaluation - // See https://github.com/dotnet/maui/issues/31939 - SearchCommandProperty.DependsOn(SearchCommandParameterProperty); - } - internal static new void RemapForControls() { // Adjust the mappings to preserve Controls.SearchBar legacy behaviors diff --git a/src/Controls/src/Core/SearchBar/SearchBar.cs b/src/Controls/src/Core/SearchBar/SearchBar.cs index 2b7e09dd629a..fbc3b649e5f3 100644 --- a/src/Controls/src/Core/SearchBar/SearchBar.cs +++ b/src/Controls/src/Core/SearchBar/SearchBar.cs @@ -164,7 +164,7 @@ private void OnRequestedThemeChanged(object sender, AppThemeChangedEventArgs e) object ICommandElement.CommandParameter => SearchCommandParameter; protected override bool IsEnabledCore => - base.IsEnabledCore && CommandElement.GetCanExecute(this, SearchCommandProperty); + base.IsEnabledCore && CommandElement.GetCanExecute(this); void ICommandElement.CanExecuteChanged(object sender, EventArgs e) => RefreshIsEnabledProperty(); diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml.cs index 3674f3b4b8b4..474e6c9b7482 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml.cs @@ -15,7 +15,6 @@ public Issue13537() { InitializeComponent(); Routing.RegisterRoute("NewPage", typeof(Issue13537InnerPage)); - Application.Current.Windows[0].Page = new NavigationPage(new Issue13537HomePage()); } } diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShouldFlyoutBeVisibleAfterMaximizingWindow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShouldFlyoutBeVisibleAfterMaximizingWindow.png index b07c35c40a21..742c7c90c6cf 100644 Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShouldFlyoutBeVisibleAfterMaximizingWindow.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShouldFlyoutBeVisibleAfterMaximizingWindow.png differ diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShouldFlyoutBeVisibleAfterMaximizingWindow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShouldFlyoutBeVisibleAfterMaximizingWindow.png index 8af59788dc1d..715a5e019386 100644 Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShouldFlyoutBeVisibleAfterMaximizingWindow.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShouldFlyoutBeVisibleAfterMaximizingWindow.png differ diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui31939.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui31939.xaml deleted file mode 100644 index 80a671ace710..000000000000 --- a/src/Controls/tests/Xaml.UnitTests/Issues/Maui31939.xaml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - -