WPF中的动态菜单

8
如何使用绑定和可观察集合从数据库表向WPF菜单控件(非上下文菜单)添加菜单项?我有以下菜单:
<Menu HorizontalAlignment="Left" Height="27" VerticalAlignment="Top" Width="649">
    <MenuItem Header="_File">
       <MenuItem Header="_Exit" Command="{Binding ExitCommand}"/>
       </MenuItem>
    <MenuItem Header="_MyMenu">
       <MenuItem Header="_SubMenu1" Command="{Binding  SubMenu1Command}" />
       <MenuItem Header="_SubMenu2" Command="{Binding  SubMenu2Command}" />
    </MenuItem>
</Menu>

"SubMenu1"和"_SuMenu2"是来自数据库表的值:
codSubMenu | SubMenuColum | CommandColumn
1__________|SubMenu1_____|SubMenu1Command 2__________|SubMenu2_____|_SubMenu2Command
我需要类似这样的东西:
<Menu HorizontalAlignment="Left" Height="27" VerticalAlignment="Top" Width="649"
    ItemsSource="{Binding ObservableCollectionMenu}">
    <MenuItem Header="_File">
       <MenuItem Header="_Exit" Command="{Binding ExitCommand}"/>
    </MenuItem>
    <MenuItem Header="_MyMenu">
        <MenuItem Header="{Binding  ObservableCollectionMenu.SubMenuColumn}" Command="{Binding  ObservableCollectionMenu.CommandColumn}" />
    </MenuItem>
</Menu>

当我运行应用程序时,当我按下选项文件和我的菜单时,菜单必须显示如下内容:
文件 | 我的菜单
退出 | 子菜单1
___| 子菜单2

你是如何填充ObservableCollectionMenu的?它包含哪种类型的对象? - Nitin
是的,ObservableCollectionMenu 有一个类类型,它是数据库表 Menu 的模型。我正在使用 WPF 和 MVVM。 - Ejrr1085
1
你的数据库表Menu的模型应该允许递归。例如,声明一个Items列表属性,用于存储子项。通过该模型,你可以表示整个菜单,甚至包括File/Exit路径。看看我的例子。 - Adolfo Perez
3个回答

10

使用菜单的ItemsSource属性和菜单项(在样式中)来绑定你的集合:

<Menu ItemsSource="{Binding YourCollection}" />

<Style TargetType="MenuItem">
    <Setter Property="Header" Value="{Binding Path=Name}" />
    <Setter Property="ItemsSource" Value="{Binding Path=Children}" />
</Style>

编辑:要进行命令绑定,请按照以下步骤操作:

  1. 在菜单项的模板中添加如下所示的setter:

  2. <Setter Property="Command" Value="{Binding Path=Command}" />
    
  3. 使用这个结构来创建一个MenuItem视图模型:

  4. public class BindableMenuItem
    {
         public string Name { get; set; }
         public BindableMenuItem[] Children { get; set; }
         public ICommand Command { get; set; }
    }
    
  5. 将根项添加到BindableMenuItems集合中,并将此集合绑定到菜单。


4
这是我的解决方法,
我创建了一个MenuItem类(请注意,它有一个项目列表,因此您可以构建子菜单):
public class MenuItem : ModelBase<MenuItem>
{
    private List<MenuItem> _Items;

    public MenuItem(string header, ICommand command)
    {
        Header = header;
        Command = command;
    }

    public MenuItem()
    {

    }

    public string Header { get; set; }

    public List<IMenuItem> Items
    {
        get { return _Items ?? (_Items = new List<IMenuItem>()); }
        set { _Items = value; }
    }

    public ICommand Command { get; set; }
    public string CommandName { get; set; }
    public object Icon { get; set; }
    public bool IsCheckable { get; set; }
    private bool _IsChecked;
    public bool IsChecked
    {
        get { return _IsChecked; }
        set
        {
            _IsChecked = value;
            NotifyPropertyChanged(m=>m.IsChecked);
        }
    }

    public bool Visible { get; set; }
    public bool IsSeparator { get; set; }
    public string InputGestureText { get; set; }
    public string ToolTip { get; set; }
    public int MenuHierarchyID { get; set; }
    public int ParentMenuHierarchyID { get; set; }
    public string IconPath { get; set; }
    public bool IsAdminOnly { get; set; }
    public object Context { get; set; }
    public IMenuItem Parent { get; set; }
    public int int_Sequence { get; set; }
    public int int_KeyIndex { get; set; }
}

以及一个视图:

<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MainMenu}">
    <Menu.ItemContainerStyle>
        <Style>
            <Setter Property="MenuItem.Header" Value="{Binding Path=Header}" />
            <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=Items}" />
            <Setter Property="MenuItem.Icon" Value="{Binding Path=Icon}" />
            <Setter Property="MenuItem.IsCheckable" Value="{Binding Path=IsCheckable}" />
            <Setter Property="MenuItem.IsChecked" Value="{Binding Path=IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
            <Setter Property="MenuItem.Command" Value="{Binding Path=Command}" />
            <!--<Setter Property="MenuItem.CommandParameter" Value="{Binding Path=IsChecked}"/>-->
            <Setter Property="MenuItem.CommandParameter" Value="{Binding Path=.}"/>
            <Setter Property="MenuItem.InputGestureText" Value="{Binding Path=InputGestureText}"/>
            <Setter Property="MenuItem.ToolTip" Value="{Binding Path=ToolTip}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=IsSeparator}" Value="true">
                    <Setter Property="MenuItem.Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type MenuItem}">
                                <Separator Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}" />
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>

在我的主 ViewModel 中,MainMenu 是一个 ObservableCollection 属性,您可以从数据库中填充它。

    public ObservableCollection<MenuItem> MainMenu
    {
        get { return _MainMenu; }
        set
        {
            _MainMenu = value;
            NotifyPropertyChanged(x => x.MainMenu);
        }
    }

为什么不直接使用HierarchicalDataTemplate呢? - Bob.

0

我在XAML中没有快速解决方案。我需要从数据库中获取子菜单项,根据特定的配置文件,有些用户拥有所有项目,而其他用户只有2或3个项目。唯一的方法是在XAML中创建带有禁用项的菜单,将菜单引用传递给ViewModel(如果是MVVM应用程序),并与ObservableCollection进行比较,只有相等的项目才能启用:

<menu horizontalalignment="Left" height="27" verticalalignment="Top" width="649" name="menu1">
      <menuitem header="_File">
          <menuitem header="_Exit" command="{Binding ExitCommand}" />
      </menuitem>
      <menuitem header="_MyMenu">
          <menuitem header="_SubMenu1" command="{Binding  Command1}" isenabled="False" />
          <menuitem header="_SubMenu2" command="{Binding  Command2}" isenabled="False" />
      </menuitem>
</menu>

视图模型:

for (int i = 0; i < ObservableCollectionMenu.Count; i++)
{

    for (int j = 0; j < ((MenuItem)menu1.Items[1]).Items.Count; j++)
    {
         if (((MenuItem)((MenuItem)menu1.Items[1]).Items[j]).Header.ToString().Equals(ObservableCollectionMenu[i].SubMenuColumn))
         {
            ((MenuItem)((MenuItem)menu1.Items[1]).Items[j]).IsEnabled = true;
             break;
         }
    }
}

感谢所有回答我的问题的人,stackoverflow比codeproject提供更好的帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接