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

时间:2022-05-11 01:39:02

I am trying to create a dynamic menu using binding. I my viewmodel I have a list of objects which contains an header and a command. However, it is not working. I think the problem is in the data template. See my code below:

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

<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>

The result only shows the first menu. The submenus are not shown but they are there since the menus which have children, the arrow is print after menu header.

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

Could anyone find something wrong on the binding? Or any suggestion?

任何人都可以在绑定上发现错误吗?还是有什么建议吗?

Just for information, MenuItems is a list of MenuItemViewModel objects which has an header and a list of MenuItemViewModel objects (submenus) called MenuItems too.

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

2 个解决方案

#1


32  

For me, it worked with this simple template:

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

<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>

Here is the complete example:

这是完整的例子:

MainWindow.xaml:

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:

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 { }
        }
    }
}

The resulting window looks like this:

生成的窗口如下所示:

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

#2


1  

that is very easy,you can use this code for your nested menu

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

ViewModel: TopMenuViewModel.cs

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"
            }
        };
    }

View: TopMenuView.xaml

查看: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>   

#1


32  

For me, it worked with this simple template:

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

<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>

Here is the complete example:

这是完整的例子:

MainWindow.xaml:

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:

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 { }
        }
    }
}

The resulting window looks like this:

生成的窗口如下所示:

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

#2


1  

that is very easy,you can use this code for your nested menu

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

ViewModel: TopMenuViewModel.cs

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"
            }
        };
    }

View: TopMenuView.xaml

查看: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>