diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs
index 49ac0bac1b..68874333ef 100644
--- a/src/Avalonia.Controls/IMenuItem.cs
+++ b/src/Avalonia.Controls/IMenuItem.cs
@@ -1,15 +1,11 @@
-using Avalonia.Metadata;
-
-namespace Avalonia.Controls
+namespace Avalonia.Controls
{
///
/// Represents a .
///
internal interface IMenuItem : IMenuElement
{
- ///
- /// Gets or sets a value that indicates whether the item has a submenu.
- ///
+ ///
bool HasSubMenu { get; }
///
@@ -17,21 +13,13 @@ namespace Avalonia.Controls
///
bool IsPointerOverSubMenu { get; }
- ///
- /// Gets or sets a value that indicates whether the submenu of the is
- /// open.
- ///
+ ///
bool IsSubMenuOpen { get; set; }
- ///
- /// Gets or sets a value that indicates the submenu that this is
- /// within should not close when this item is clicked.
- ///
+ ///
bool StaysOpenOnClick { get; set; }
- ///
- /// Gets a value that indicates whether the is a top-level main menu item.
- ///
+ ///
bool IsTopLevel { get; }
///
@@ -39,22 +27,15 @@ namespace Avalonia.Controls
///
IMenuElement? Parent { get; }
- ///
- /// Gets toggle type of the menu item.
- ///
+ ///
MenuItemToggleType ToggleType { get; }
-
- ///
- /// Gets menu item group name when is .
- ///
+
+ ///
string? GroupName { get; }
-
- ///
- /// Gets or sets if menu item is checked when is
- /// or .
- ///
+
+ ///
bool IsChecked { get; set; }
-
+
///
/// Raises a click event on the menu item.
///
diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs
index dcdcec069a..231dcc6a53 100644
--- a/src/Avalonia.Controls/MenuItem.cs
+++ b/src/Avalonia.Controls/MenuItem.cs
@@ -288,6 +288,12 @@ namespace Avalonia.Controls
set => SetValue(IsSubMenuOpenProperty, value);
}
+ bool IMenuItem.IsSubMenuOpen
+ {
+ get => IsSubMenuOpen;
+ set => SetCurrentValue(IsSubMenuOpenProperty, value);
+ }
+
///
/// Gets or sets a value that indicates the submenu that this is
/// within should not close when this item is clicked.
@@ -298,27 +304,46 @@ namespace Avalonia.Controls
set => SetValue(StaysOpenOnClickProperty, value);
}
- ///
+ bool IMenuItem.StaysOpenOnClick
+ {
+ get => StaysOpenOnClick;
+ set => SetCurrentValue(StaysOpenOnClickProperty, value);
+ }
+
+ ///
+ /// Gets toggle type of the menu item.
+ ///
public MenuItemToggleType ToggleType
{
get => GetValue(ToggleTypeProperty);
set => SetValue(ToggleTypeProperty, value);
}
- ///
+ ///
+ /// Gets or sets if menu item is checked when is
+ /// or .
+ ///
public bool IsChecked
{
get => GetValue(IsCheckedProperty);
set => SetValue(IsCheckedProperty, value);
}
+ bool IMenuItem.IsChecked
+ {
+ get => IsChecked;
+ set => SetCurrentValue(IsCheckedProperty, value);
+ }
+
bool IRadioButton.IsChecked
{
get => IsChecked;
set => SetCurrentValue(IsCheckedProperty, value);
}
- ///
+ ///
+ /// Gets menu item group name when is .
+ ///
public string? GroupName
{
get => GetValue(GroupNameProperty);
diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
index 7c133c50a7..821d1ec599 100644
--- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
@@ -1,6 +1,9 @@
using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
+using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.UnitTests;
@@ -12,11 +15,11 @@ namespace Avalonia.Controls.UnitTests.Platform
public class DefaultMenuInteractionHandlerTests : ScopedTestBase
{
static PointerPressedEventArgs CreatePressed(object source) => new PointerPressedEventArgs(source,
- new FakePointer(), (Visual)source, default,0, new PointerPointProperties (RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed),
+ new FakePointer(), (Visual)source, default, 0, new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed),
default);
-
+
static PointerReleasedEventArgs CreateReleased(object source) => new PointerReleasedEventArgs(source,
- new FakePointer(), (Visual)source, default,0,
+ new FakePointer(), (Visual)source, default, 0,
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonReleased),
default, MouseButton.Left);
@@ -224,6 +227,63 @@ namespace Avalonia.Controls.UnitTests.Platform
target.KeyDown(menu, e);
}
+
+ private class MenuItemVM : INotifyPropertyChanged
+ {
+ public bool IsChecked { get; set { field = value; OnPropertyChanged(); } }
+ public bool IsSubMenuOpen { get; set { field = value; OnPropertyChanged(); } }
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new(propertyName));
+ }
+
+ [Fact]
+ public void Doesnt_Replace_IsChecked_Binding()
+ {
+ var target = new DefaultMenuInteractionHandler(false);
+ var menu = new Menu();
+ var vm = new MenuItemVM();
+
+ var item = new MenuItem
+ {
+ DataContext = vm,
+ [!MenuItem.IsCheckedProperty] = new Binding(nameof(MenuItemVM.IsChecked)) { Priority = BindingPriority.Style, Mode = BindingMode.TwoWay },
+ ToggleType = MenuItemToggleType.CheckBox,
+ };
+ menu.Items.Add(item);
+
+ target.KeyDown(item, new KeyEventArgs { Key = Key.Enter, Source = menu });
+
+ Assert.True(item.IsChecked);
+
+ vm.IsChecked = false;
+
+ Assert.False(item.IsChecked);
+ }
+
+ [Fact]
+ public void Doesnt_Replace_IsSubMenuOpen_Binding()
+ {
+ var target = new DefaultMenuInteractionHandler(false);
+ var menu = new Menu();
+ var vm = new MenuItemVM();
+
+ var item = new MenuItem
+ {
+ DataContext = vm,
+ [!MenuItem.IsSubMenuOpenProperty] = new Binding(nameof(MenuItemVM.IsSubMenuOpen)) { Priority = BindingPriority.Style, Mode = BindingMode.TwoWay },
+ Items = { new MenuItem() }
+ };
+
+ target.KeyDown(item, new KeyEventArgs { Key = Key.Enter, Source = menu });
+
+ Assert.True(item.IsSubMenuOpen);
+
+ vm.IsSubMenuOpen = false;
+
+ Assert.False(item.IsSubMenuOpen);
+ }
}
public class NonTopLevel : ScopedTestBase