diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 8cfc40bf0f2..bff06e898e5 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Windows.Input; using Avalonia.Automation; @@ -341,7 +342,7 @@ public string? GroupName /// IMenuElement? IMenuItem.Parent => Parent as IMenuElement; - protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute; + protected override bool IsEnabledCore => base.IsEnabled && (HasSubMenu || _commandCanExecute); /// bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap) => MoveSelection(direction, wrap); @@ -710,6 +711,15 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang { GroupNameChanged(change); } + else if (change.Property == ItemCountProperty) + { + // A menu item with no sub-menu is effectively disabled if its command binding + // failed: this means that the effectively enabled state depends on whether the + // number of items in the menu is 0 or not. + var (o, n) = change.GetOldAndNewValue(); + if (o == 0 || n == 0) + UpdateIsEffectivelyEnabled(); + } } /// /// Called when the property changes. diff --git a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs index 7f7b69056cc..64c675fa18a 100644 --- a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs +++ b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs @@ -62,6 +62,45 @@ public void MenuItem_Is_Disabled_When_Bound_Command_Doesnt_Exist() Assert.False(target.IsEffectivelyEnabled); } + [Fact] + public void MenuItem_With_Styled_Command_Binding_Should_Be_Enabled_With_Child_Missing_Command() + { + using var app = Application(); + + var viewModel = new MenuViewModel("Parent") + { + Children = [new MenuViewModel("Child")] + }; + + var contextMenu = new ContextMenu + { + ItemsSource = new[] { viewModel }, + Styles = + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(MenuItem.HeaderProperty, new Binding("Header")), + new Setter(MenuItem.ItemsSourceProperty, new Binding("Children")), + new Setter(MenuItem.CommandProperty, new Binding("Command")) + } + } + } + }; + + var window = new Window { ContextMenu = contextMenu }; + window.Show(); + contextMenu.Open(); + + var parentMenuItem = Assert.IsType(contextMenu.ContainerFromIndex(0)); + + Assert.Same(parentMenuItem.DataContext, viewModel); + Assert.Same(parentMenuItem.ItemsSource, viewModel.Children); + Assert.True(parentMenuItem.IsEnabled); + Assert.True(parentMenuItem.IsEffectivelyEnabled); + } + [Fact] public void MenuItem_Is_Disabled_When_Bound_Command_Is_Removed() {