WPF - 如何使用绑定创建菜单和子菜单

Posted

技术标签:

【中文标题】WPF - 如何使用绑定创建菜单和子菜单【英文标题】:WPF - How can I create menu and submenus using binding 【发布时间】:2014-07-19 10:18:29 【问题描述】:

我正在尝试使用绑定创建动态菜单。我的视图模型我有一个包含标题和命令的对象列表。但是,它不起作用。我认为问题出在数据模板中。请参阅下面的代码:

<Menu Background="x:Null" Grid.Row="0" Grid.Column="1" Panel.ZIndex="2" Width="865" Height="85" HorizontalAlignment="Left" ItemsSource="Binding Path=MenuItems">

        <Menu.ItemTemplate>
            <HierarchicalDataTemplate DataType="MenuItemViewModel" ItemsSource="Binding Path=MenuItems">
                <MenuItem Header="Binding Header" Style="DynamicResource MenuItemStyle1" ItemsSource="Binding Path=MenuItems" Padding="10,12,10,0" Height="44.1" Margin="30,0,0,0" FontWeight="Bold">
                    <MenuItem.ItemsPanel>
                        <ItemsPanelTemplate>
                            <VirtualizingStackPanel Orientation="Horizontal"/>
                        </ItemsPanelTemplate>
                    </MenuItem.ItemsPanel>
                </MenuItem>
                <HierarchicalDataTemplate.ItemTemplate>
                    <DataTemplate>
                        <MenuItem Header="Binding Header" Style="DynamicResource MenuItemStyle1" Padding="0,8,0,0" Height="38">
                        </MenuItem>
                    </DataTemplate>
                </HierarchicalDataTemplate.ItemTemplate>
            </HierarchicalDataTemplate>
        </Menu.ItemTemplate>            
    </Menu>

结果只显示第一个菜单。子菜单没有显示,但它们在那里,因为有子菜单,箭头打印在菜单标题之后。

有没有人发现装订有问题?或者有什么建议?

仅供参考,MenuItems 是一个 MenuItemViewModel 对象列表,它有一个标题和一个名为 MenuItems 的 MenuItemViewModel 对象(子菜单)列表。

【问题讨论】:

【参考方案1】:

如果您像我一样热衷于将 UI 构造保留在 XAML 代码中,经过一些困难,我找到了一种很好的方法来绑定自定义类型的集合以创建菜单项。

在 XAML 中:

<Menu DockPanel.Dock="Top">
    <MenuItem Header="SomeHeaderName" ItemsSource="Binding Path=MyCollection">
        <MenuItem.ItemsContainerStyle>
            <Setter Property="Header" Value="Binding Path=SomeRelevantTextProperty"/>
            <EventSetter Event="Click" Handler="SomeMenuItemClickEventHandler"/>
        </MenuItem.ItemsContainerStyle>
    </MenuItem>
</Menu>

在代码隐藏中:

ObservableCollection<MyClass> MyCollection;

private void SomeMenuItemClickEventHandler(object sender, RoutedEventArgs e)

    MenuItem menuItem = sender as MenuItem;
    MyClass myClass = menuItem.DataContext as MyClass;
    // do something useful!

public class MyClass

    public string SomeRelevantTextProperty  get; 

【讨论】:

【参考方案2】:

这很简单,您可以将此代码用于嵌套菜单

ViewModel:TopMenuViewModel.cs

public partial class TopMenuViewModel 

    public TopMenuViewModel()
    
        TopMenuItems = new ObservableCollection<MenuItem>
        
            new MenuItem
            
                Title = "File",
                PageName =typeof(OfficeListView).FullName,
                ChildMenuItems= 
                    new MenuItem
                    
                        Title = "New"
                    ,
                     new MenuItem
                    
                        Title = "Open"
                    ,
                     new MenuItem
                    
                        Title = "Save"
                    
                
            ,
            new MenuItem
            
                Title = "Edit"
            ,
            new MenuItem
            
                Title = "Search"
            
        ;
    

视图:TopMenuView.xaml

<Menu IsMainMenu="True" ItemsSource="Binding TopMenuItems">
            <Menu.ItemContainerStyle>
                <Style TargetType="x:Type MenuItem">
                    <Setter Property="Header" Value="Binding Title"/>
                    <Setter Property="ItemsSource" Value="Binding Path=ChildMenuItems"/>
                </Style>
            </Menu.ItemContainerStyle>
</Menu>   

【讨论】:

【参考方案3】:

对我来说,它适用于这个简单的模板:

<Menu.ItemContainerStyle>
    <Style TargetType="x:Type MenuItem">
        <Setter Property="Command" Value="Binding Command" />
    </Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
    <HierarchicalDataTemplate DataType="x:Type local:MenuItemViewModel" ItemsSource="Binding Path=MenuItems">
        <TextBlock Text="Binding Header"/>
    </HierarchicalDataTemplate>
</Menu.ItemTemplate>

这是完整的例子:

MainWindow.xaml:

<Window x:Class="WpfApplication14.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication14"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <Menu DockPanel.Dock="Top" ItemsSource="Binding MenuItems">
            <Menu.ItemContainerStyle>
                <Style TargetType="x:Type MenuItem">
                    <Setter Property="Command" Value="Binding Command" />
                </Style>
            </Menu.ItemContainerStyle>
            <Menu.ItemTemplate>
                <HierarchicalDataTemplate DataType="x:Type local:MenuItemViewModel" ItemsSource="Binding Path=MenuItems">
                    <TextBlock Text="Binding Header"/>
                </HierarchicalDataTemplate>
            </Menu.ItemTemplate>
        </Menu>
        <Grid>
        </Grid>
    </DockPanel>
</Window>

MainWindow.xaml.cs:

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;

namespace WpfApplication14

    public partial class MainWindow : Window
    
        public ObservableCollection<MenuItemViewModel> MenuItems  get; set; 

        public MainWindow()
        
            InitializeComponent();

            MenuItems = new ObservableCollection<MenuItemViewModel>
            
                new MenuItemViewModel  Header = "Alpha" ,
                new MenuItemViewModel  Header = "Beta",
                    MenuItems = new ObservableCollection<MenuItemViewModel>
                        
                            new MenuItemViewModel  Header = "Beta1" ,
                            new MenuItemViewModel  Header = "Beta2",
                                MenuItems = new ObservableCollection<MenuItemViewModel>
                                
                                    new MenuItemViewModel  Header = "Beta1a" ,
                                    new MenuItemViewModel  Header = "Beta1b" ,
                                    new MenuItemViewModel  Header = "Beta1c" 
                                
                            ,
                            new MenuItemViewModel  Header = "Beta3" 
                        
                ,
                new MenuItemViewModel  Header = "Gamma" 
            ;

            DataContext = this;
        
    

    public class MenuItemViewModel
    
        private readonly ICommand _command;

        public MenuItemViewModel()
        
            _command = new CommandViewModel(Execute);
        

        public string Header  get; set; 

        public ObservableCollection<MenuItemViewModel> MenuItems  get; set; 

        public ICommand Command
        
            get
            
                return _command;
            
        

        private void Execute()
        
            // (NOTE: In a view model, you normally should not use MessageBox.Show()).
            MessageBox.Show("Clicked at " + Header);
        
    

    public class CommandViewModel : ICommand
    
        private readonly Action _action;

        public CommandViewModel(Action action)
        
            _action = action;
        

        public void Execute(object o)
        
            _action();
        

        public bool CanExecute(object o)
        
            return true;
        

        public event EventHandler CanExecuteChanged
        
            add  
            remove  
        
    

生成的窗口如下所示:

【讨论】:

谢谢!菜单正在正确构建。但是,如何在 MenuItemViewModel 中绑定命令? 是的,这是可能的。请参阅上面我更新的示例。单击菜单项时,会出现一个带有菜单项标题文本的消息框。 我无法添加Separator ((

以上是关于WPF - 如何使用绑定创建菜单和子菜单的主要内容,如果未能解决你的问题,请参考以下文章

WPF 如何创建这样的横向菜单(Modern-UI)

WPF:将命令添加到通过绑定菜单项自动生成

将 WPF 快捷键绑定到 ViewModel 中的命令

wpf中怎么为treeview动态添加子菜单

WPF 菜单事件绑定 DataTemplate下button Command事件绑定 DataTemplate遍历实体数据

如何通过悬停而不单击[重复]来显示下拉菜单和子菜单项