Browse Source

Fix `MenuItem` enabled state in the presence of sub-items. (#18679)

* Add unit test for sub menu item testing

* Parent menu items should not be disabled on command binding failure.

If a menu has a failed `Command` binding, but also has sub-menu items, then it should not be disabled.

---------

Co-authored-by: Anastassia Pellja <anyka545@gmail.com>
release/11.3.0-rc1
Steven Kirk 10 months ago
committed by Julien Lebosquain
parent
commit
e989b475ef
  1. 12
      src/Avalonia.Controls/MenuItem.cs
  2. 39
      tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

12
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 @@ namespace Avalonia.Controls
/// <inheritdoc/>
IMenuElement? IMenuItem.Parent => Parent as IMenuElement;
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute;
protected override bool IsEnabledCore => base.IsEnabled && (HasSubMenu || _commandCanExecute);
/// <inheritdoc/>
bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap) => MoveSelection(direction, wrap);
@ -710,6 +711,15 @@ namespace Avalonia.Controls
{
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<int>();
if (o == 0 || n == 0)
UpdateIsEffectivelyEnabled();
}
}
/// <summary>
/// Called when the <see cref="GroupName"/> property changes.

39
tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

@ -62,6 +62,45 @@ namespace Avalonia.Controls.UnitTests
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<MenuItem>())
{
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<MenuItem>(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()
{

Loading…
Cancel
Save