AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.9k stars 2.24k forks source link

Menu Item InputGesture does not display when using MVVM #16302

Closed cornerbowlsoftware closed 4 months ago

cornerbowlsoftware commented 4 months ago

Describe the bug

Most samples I see used hard coded axaml to generate menu items. This breaks the MVVM architecture, so first, I'm surprised to see the Avalonia documentation show menu item implementation in this manner. Second, when binding to an 'InputGesture' the value is not displayed. This bug likely would have been caught if the docs used MVVM instead of showing hard coded values.

To Reproduce

Create a ViewModel to house the data model:

    public class MenuItemViewmModel : BaseViewModel
    {
        public string? Text { get; set; }
        public ICommand? Command { get; set; }
        public string? Icon { get; set; }
        public string? InputGestureText { get; set; }

        protected MenuItemViewModel()
        {
        }

        public MenuItemViewModel(string text)
        {
            Text = text;
        }

        public MenuItemViewModel(string text, string? icon)
        : this(text)
        {
            Icon = icon;
        }

        public MenuItemViewModel(string text, string? icon, ICommand? command)
        : this(text, icon)
        {
            Command = command;
        }

        public MenuItemViewModel(string text, string? icon, ICommand? command, string inputGestureText)
            : this(text, icon, command)
        {
            InputGestureText = inputGestureText;
        }

        public override string? ToString() => Text;

        public static object? Children => null;
    }

Create a style to bind the various context menu items to a view model:

    <Style Selector="MenuItem" x:DataType="lib:MenuItemViewModel">
        <Setter Property="Header" Value="{Binding Text}"/>
        <Setter Property="Command" Value="{Binding Command}"/>
        <Setter Property="InputGesture" Value="{Binding InputGestureText}"/>        
    </Style>

Next, define a context menu:

<local:BaseDataGrid
    x:Class="ServerManagerAvaloniaApp.FlatDataGrid"
    xmlns="https://github.com/avaloniaui"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:ServerManagerAvaloniaApp"
    xmlns:lib="using:ServerManagerLibrary"
    mc:Ignorable="d"
    d:DesignHeight="200" d:DesignWidth="400"
    x:DataType="lib:BaseViewModel">

    <DataGrid.ContextMenu>
        <ContextMenu ItemsSource="{Binding MenuItemVMs}"/>
    </DataGrid.ContextMenu>

</local:BaseDataGrid>

Run the app and display your control that hosts the menu items. Right click on the control. Notice the hot key, in this case "Ctrl+C" is not displayed. Put a breakpoint in the view model on InputGestureText. Reopen the view to regenerate the requests. Verify "Ctrl+C" is returned. Stop the program then change the style to hard code the InputGesture to "Ctrl+C" like so:

<Style Selector="MenuItem" x:DataType="lib:MenuItemModel">
    <Setter Property="Header" Value="{Binding Text}"/>
    <Setter Property="Command" Value="{Binding Command}"/>
    <Setter Property="InputGesture" Value="Ctrl+C"/>
</Style>

Rerun the application. Notice the InputGesture is displayed.

Expected behavior

The gesture should display.

Avalonia version

11.0.11

OS

Windows

Additional context

No response

cornerbowlsoftware commented 4 months ago

I was able to get this working by adding my own string to key gesture converter:

    <Style Selector="MenuItem" x:DataType="lib:MenuItemModel">
        <Setter Property="Header" Value="{Binding Text}"/>
        <Setter Property="Command" Value="{Binding Command}"/>
        <Setter Property="InputGesture" Value="{Binding InputGestureText, Converter={StaticResource StringToKeyGestureConverter}}"/>
    </Style>