WPF - How can I create menu and submenus using binding
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:
<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 { } } }}
The resulting window looks like this:
that is very easy,you can use this code for your nested menu
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
<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>
If like me you are keen to keep UI constructs in the XAML code, after some difficulty, I have worked a nice way of binding custom-typed collections to create menu items.
In 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>
In code-behind:
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; }}