如何获取所选菜单项的内容?

How to get the Content of selected Menu Item?

问题总结

目标

我的目标是在 WPF 中创建导航菜单。

导航项是使用节点在视图模型中构建的。

选中SubItem的菜单会显示选中节点的内容

预期和实际结果

这是我到目前为止构建的:

错误

现在我需要将选定的 MenuItem 呈现给 ContentPresenter - 这就是我遇到的问题。

我试过的

这是我当前的代码

XAML

<!--Menu-->
<Menu x:Name="NavigationTreeView" IsMainMenu="True" ItemsSource="{Binding Navigation}" Grid.Row="0">
    <Menu.Resources>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Header" Value="{Binding Path=Title}" />
            <Setter Property="Template" Value="{StaticResource VsMenuTop}" />
        </Style>
        <HierarchicalDataTemplate DataType="{x:Type nav:PageViewModel}" ItemsSource="{Binding Children}" />
    </Menu.Resources>
</Menu>

<!--Content-->
<ContentPresenter Grid.Row="1" Content="{Binding ElementName=NavigationTreeView, Path=SelectedItem.Content}" />

这行不通,因为我无法在此处使用此行将其绑定到菜单:SelectedItem.Content - 并且菜单没有 属性 SelectedItem

备选

我的备选方案是使用 ListView,因为它包含 属性 SelectedItem。但最好我想使用 Menu.

问题

如何从分层数据模板中获取所选项目?

Menu 控件没有选定项的概念,因为它的 MenuItem 本质上是按钮,单击时应执行操作。您可以将 IsCheckable 属性 设置为 true 以便能够 检查 菜单项,例如 CheckBox,但我猜此处不适合您的用例。

由于您可能希望根据单击的菜单项显示不同的内容,您可以使用命令,当您单击 MenuItem 并获取相应的视图模型作为参数并设置属性 在可以由 ContentPresenter 绑定的视图模型上。

在包含 Navigation 属性的主视图模型中引入类型为 PageViewModelSelected 属性 和 Select 命令。该命令设置 Selected 属性.

public class MainViewModel : INotifyPropertyChanged
{
   public MainViewModel()
   {
      Selected = new RelayCommand<PageViewModel>(pageViewModel => Selected = pageViewModel);
      // ...other code.
   }

   public DelegateCommand<PageViewModel> Select { get; }
   
   private PageViewModel _selected;
   public PageViewModel Selected
   {
      get => _selected;
      private set
      {
         if (_selected == value)
            return;
   
         _selected = value;
         OnPropertyChanged();
      }
   }
   
   // ...other code.
}

您可以将 RelayCommand 替换为您可以随意使用的命令实现。

然后,您可以调整样式以将命令绑定到主视图模型(使用 ElementNameRelativeSource 到数据上下文)并绑定单击的视图模型 MenuItem 作为命令参数。通过这种方式将其传递给命令,该命令将其设置为 Selected.

<Style TargetType="{x:Type MenuItem}">
   <Setter Property="Header" Value="{Binding Path=Title}" />
   <Setter Property="Template" Value="{StaticResource VsMenuTop}" />
   <Setter Property="Command" Value="{Binding DataContext.Select, ElementName=NavigationTreeView}"/>
   <Setter Property="CommandParameter" Value="{Binding}"/>
</Style>

ContentPresenterContent 绑定到主视图模型上的 Selected 属性。

<ContentPresenter Grid.Row="1" Content="{Binding ElementName=NavigationTreeView, Path=Selected}" />

Сan 想到了许多实现,但并不完全清楚您需要什么。 需要更多详细信息。

SelectedItem(选择器class 的属性)不包含UI 元素,而是集合源元素。 也许您也需要它,而不是 MenuItem?

你没有使用命令,所以你可以使用它们来解决问题。

我已经使用 实现了一个简单的代理。

using Simplified;

namespace SelectedItem
{
    public class SelectedItemProxy : BaseInpc
    {
        private object _selectedMenuItem;

        public object SelectedMenuItem { get => _selectedMenuItem; set => Set(ref _selectedMenuItem, value); }

        private RelayCommand _selectItemCommand;
        public RelayCommand SelectItemCommand => _selectItemCommand
            ?? (_selectItemCommand = new RelayCommand(item => SelectedMenuItem = item));
    }
}

使用示例:

<Window x:Class="SelectedItem.SmiExamleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SelectedItem"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="SmiExamleWindow" Height="450" Width="800">
    <Window.Resources>
        <local:SelectedItemProxy x:Key="proxy"/>
        
    </Window.Resources>
    <Grid>
        <Menu x:Name="NavigationTreeView" IsMainMenu="True" ItemsSource="{Binding Navigation}" Grid.Row="0" VerticalAlignment="Top">
            <Menu.Resources>
                <Style TargetType="{x:Type MenuItem}">
                    <Setter Property="Header" Value="{Binding}"/>
                    <Setter Property="Command" Value="{Binding SelectItemCommand, Source={StaticResource proxy}}"/>
                    <Setter Property="CommandParameter" Value="{Binding}"/>
                </Style>
            </Menu.Resources>
            <sys:String>First</sys:String>
            <sys:String>Second</sys:String>
        </Menu>
        <ContentPresenter Content="{Binding SelectedMenuItem, Source={StaticResource proxy}}" VerticalAlignment="Bottom" />
    </Grid>
</Window>