diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 64daa133a3..163c39b219 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -286,6 +286,26 @@ namespace Avalonia.Controls return new MenuItemContainerGenerator(this); } + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + + if (Command != null) + { + Command.CanExecuteChanged += CanExecuteChanged; + } + } + + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + + if (Command != null) + { + Command.CanExecuteChanged -= CanExecuteChanged; + } + } + /// /// Called when the is clicked. /// @@ -399,14 +419,17 @@ namespace Avalonia.Controls { if (e.Sender is MenuItem menuItem) { - if (e.OldValue is ICommand oldCommand) + if (((ILogical)menuItem).IsAttachedToLogicalTree) { - oldCommand.CanExecuteChanged -= menuItem.CanExecuteChanged; - } + if (e.OldValue is ICommand oldCommand) + { + oldCommand.CanExecuteChanged -= menuItem.CanExecuteChanged; + } - if (e.NewValue is ICommand newCommand) - { - newCommand.CanExecuteChanged += menuItem.CanExecuteChanged; + if (e.NewValue is ICommand newCommand) + { + newCommand.CanExecuteChanged += menuItem.CanExecuteChanged; + } } menuItem.CanExecuteChanged(menuItem, EventArgs.Empty); diff --git a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs index e7352af23e..32d154249c 100644 --- a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs +++ b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Text; +using System.Windows.Input; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Controls.UnitTests @@ -22,5 +24,57 @@ namespace Avalonia.Controls.UnitTests Assert.False(target.Focusable); } + + [Fact] + public void MenuItem_Does_Not_Subscribe_To_Command_CanExecuteChanged_Until_Added_To_Logical_Tree() + { + var command = new TestCommand(); + var target = new MenuItem + { + Command = command, + }; + + Assert.Equal(0, command.SubscriptionCount); + } + + [Fact] + public void MenuItem_Subscribes_To_Command_CanExecuteChanged_When_Added_To_Logical_Tree() + { + var command = new TestCommand(); + var target = new MenuItem { Command = command }; + var root = new TestRoot { Child = target }; + + Assert.Equal(1, command.SubscriptionCount); + } + + [Fact] + public void MenuItem_Unsubscribes_From_Command_CanExecuteChanged_When_Removed_From_Logical_Tree() + { + var command = new TestCommand(); + var target = new MenuItem { Command = command }; + var root = new TestRoot { Child = target }; + + root.Child = null; + Assert.Equal(0, command.SubscriptionCount); + } + + private class TestCommand : ICommand + { + private EventHandler _canExecuteChanged; + + public int SubscriptionCount { get; private set; } + + public event EventHandler CanExecuteChanged + { + add { _canExecuteChanged += value; ++SubscriptionCount; } + remove { _canExecuteChanged -= value; --SubscriptionCount; } + } + + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter) + { + } + } } }