Browse Source

Use `SetCurrentValue` for all `IMenuItem` properties (#20682)

* Add IMenuItem binding tests

* Use SetCurrentValue for all IMenuItem properties
Fix XMLDoc comments for MenuItem being inherited from an internal type
pull/20687/head
Tom Edwards 1 month ago
committed by GitHub
parent
commit
f4b5dc95b7
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 41
      src/Avalonia.Controls/IMenuItem.cs
  2. 31
      src/Avalonia.Controls/MenuItem.cs
  3. 66
      tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs

41
src/Avalonia.Controls/IMenuItem.cs

@ -1,15 +1,11 @@
using Avalonia.Metadata; namespace Avalonia.Controls
namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Represents a <see cref="MenuItem"/>. /// Represents a <see cref="MenuItem"/>.
/// </summary> /// </summary>
internal interface IMenuItem : IMenuElement internal interface IMenuItem : IMenuElement
{ {
/// <summary> /// <inheritdoc cref="MenuItem.HasSubMenu"/>
/// Gets or sets a value that indicates whether the item has a submenu.
/// </summary>
bool HasSubMenu { get; } bool HasSubMenu { get; }
/// <summary> /// <summary>
@ -17,21 +13,13 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
bool IsPointerOverSubMenu { get; } bool IsPointerOverSubMenu { get; }
/// <summary> /// <inheritdoc cref="MenuItem.IsSubMenuOpen"/>
/// Gets or sets a value that indicates whether the submenu of the <see cref="MenuItem"/> is
/// open.
/// </summary>
bool IsSubMenuOpen { get; set; } bool IsSubMenuOpen { get; set; }
/// <summary> /// <inheritdoc cref="MenuItem.StaysOpenOnClick"/>
/// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is
/// within should not close when this item is clicked.
/// </summary>
bool StaysOpenOnClick { get; set; } bool StaysOpenOnClick { get; set; }
/// <summary> /// <inheritdoc cref="MenuItem.IsTopLevel"/>
/// Gets a value that indicates whether the <see cref="MenuItem"/> is a top-level main menu item.
/// </summary>
bool IsTopLevel { get; } bool IsTopLevel { get; }
/// <summary> /// <summary>
@ -39,22 +27,15 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
IMenuElement? Parent { get; } IMenuElement? Parent { get; }
/// <summary> /// <inheritdoc cref="MenuItem.ToggleType"/>
/// Gets toggle type of the menu item.
/// </summary>
MenuItemToggleType ToggleType { get; } MenuItemToggleType ToggleType { get; }
/// <summary> /// <inheritdoc cref="MenuItem.GroupName"/>
/// Gets menu item group name when <see cref="ToggleType"/> is <see cref="MenuItemToggleType.Radio"/>.
/// </summary>
string? GroupName { get; } string? GroupName { get; }
/// <summary> /// <inheritdoc cref="MenuItem.IsChecked"/>
/// Gets or sets if menu item is checked when <see cref="ToggleType"/> is
/// <see cref="MenuItemToggleType.CheckBox"/> or <see cref="MenuItemToggleType.Radio"/>.
/// </summary>
bool IsChecked { get; set; } bool IsChecked { get; set; }
/// <summary> /// <summary>
/// Raises a click event on the menu item. /// Raises a click event on the menu item.
/// </summary> /// </summary>

31
src/Avalonia.Controls/MenuItem.cs

@ -288,6 +288,12 @@ namespace Avalonia.Controls
set => SetValue(IsSubMenuOpenProperty, value); set => SetValue(IsSubMenuOpenProperty, value);
} }
bool IMenuItem.IsSubMenuOpen
{
get => IsSubMenuOpen;
set => SetCurrentValue(IsSubMenuOpenProperty, value);
}
/// <summary> /// <summary>
/// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is /// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is
/// within should not close when this item is clicked. /// within should not close when this item is clicked.
@ -298,27 +304,46 @@ namespace Avalonia.Controls
set => SetValue(StaysOpenOnClickProperty, value); set => SetValue(StaysOpenOnClickProperty, value);
} }
/// <inheritdoc cref="IMenuItem.ToggleType" /> bool IMenuItem.StaysOpenOnClick
{
get => StaysOpenOnClick;
set => SetCurrentValue(StaysOpenOnClickProperty, value);
}
/// <summary>
/// Gets toggle type of the menu item.
/// </summary>
public MenuItemToggleType ToggleType public MenuItemToggleType ToggleType
{ {
get => GetValue(ToggleTypeProperty); get => GetValue(ToggleTypeProperty);
set => SetValue(ToggleTypeProperty, value); set => SetValue(ToggleTypeProperty, value);
} }
/// <inheritdoc cref="IMenuItem.IsChecked"/> /// <summary>
/// Gets or sets if menu item is checked when <see cref="ToggleType"/> is
/// <see cref="MenuItemToggleType.CheckBox"/> or <see cref="MenuItemToggleType.Radio"/>.
/// </summary>
public bool IsChecked public bool IsChecked
{ {
get => GetValue(IsCheckedProperty); get => GetValue(IsCheckedProperty);
set => SetValue(IsCheckedProperty, value); set => SetValue(IsCheckedProperty, value);
} }
bool IMenuItem.IsChecked
{
get => IsChecked;
set => SetCurrentValue(IsCheckedProperty, value);
}
bool IRadioButton.IsChecked bool IRadioButton.IsChecked
{ {
get => IsChecked; get => IsChecked;
set => SetCurrentValue(IsCheckedProperty, value); set => SetCurrentValue(IsCheckedProperty, value);
} }
/// <inheritdoc cref="IMenuItem.GroupName"/> /// <summary>
/// Gets menu item group name when <see cref="ToggleType"/> is <see cref="MenuItemToggleType.Radio"/>.
/// </summary>
public string? GroupName public string? GroupName
{ {
get => GetValue(GroupNameProperty); get => GetValue(GroupNameProperty);

66
tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs

@ -1,6 +1,9 @@
using System; using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.UnitTests; using Avalonia.UnitTests;
@ -12,11 +15,11 @@ namespace Avalonia.Controls.UnitTests.Platform
public class DefaultMenuInteractionHandlerTests : ScopedTestBase public class DefaultMenuInteractionHandlerTests : ScopedTestBase
{ {
static PointerPressedEventArgs CreatePressed(object source) => new PointerPressedEventArgs(source, 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); default);
static PointerReleasedEventArgs CreateReleased(object source) => new PointerReleasedEventArgs(source, 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), new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonReleased),
default, MouseButton.Left); default, MouseButton.Left);
@ -224,6 +227,63 @@ namespace Avalonia.Controls.UnitTests.Platform
target.KeyDown(menu, e); 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 public class NonTopLevel : ScopedTestBase

Loading…
Cancel
Save