MVVM Dynamic Menu UI from binding with ViewModel
Try something like this:
public class MenuItemViewModel{ public MenuItemViewModel() { this.MenuItems = new List<MenuItemViewModel>(); } public string Text { get; set; } public IList<MenuItemViewModel> MenuItems { get; private set; }}
Assume that your DataContext has a property called MenuItems which is a list of MenuItemViewModel. Something like this should work, then:
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:self="clr-namespace:WpfApplication1" Title="Window1" Height="300" Width="300"> <Window.Resources> <HierarchicalDataTemplate DataType="{x:Type self:MenuItemViewModel}" ItemsSource="{Binding Path=MenuItems}"> <ContentPresenter Content="{Binding Path=Text}" /> </HierarchicalDataTemplate> </Window.Resources> <DockPanel> <Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MenuItems}" /> <Grid /> </DockPanel></Window>
This should get you where you are going
<UserControl x:Class="WindowsUI.Views.Default.MenuView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:ViewModels="clr-namespace:WindowsUI.ViewModels" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"><UserControl.Resources> <Style TargetType="{x:Type MenuItem}"> <Setter Property="Header" Value="{Binding Path=DisplayName}"/> <Setter Property="Command" Value="{Binding Path=Command}"/> </Style> <HierarchicalDataTemplate DataType="{x:Type ViewModels:MenuItemViewModel}" ItemsSource="{Binding Path=Items}"> </HierarchicalDataTemplate></UserControl.Resources><Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=Items}"/>
Note that in my example, my menu Item has a property of type ICommand called Command.
This solution doesn't need any code in code behind and that makes it simpler solution.
<Menu> <MenuItem ItemsSource="{Binding Path=ChildMenuItems}" Header="{Binding Path=Header}"> <MenuItem.Resources> <HierarchicalDataTemplate DataType="{x:Type vm:MenuItemViewModel}" ItemsSource="{Binding ChildMenuItems}"> <MenuItem Header="{Binding Path=Header}" Command="{Binding Path=Command}"/> </HierarchicalDataTemplate> <DataTemplate DataType="{x:Type vm:SeparatorViewModel}"> <Separator> <Separator.Template> <ControlTemplate> <Line X1="0" X2="1" Stroke="Black" StrokeThickness="1" Stretch="Fill"/> </ControlTemplate> </Separator.Template> </Separator> </DataTemplate> </MenuItem.Resources> </MenuItem> </Menu>
And MenuItem is represented as:
public class MenuItemViewModel : BaseViewModel { /// <summary> /// Initializes a new instance of the <see cref="MenuItemViewModel"/> class. /// </summary> /// <param name="parentViewModel">The parent view model.</param> public MenuItemViewModel(MenuItemViewModel parentViewModel) { ParentViewModel = parentViewModel; _childMenuItems = new ObservableCollection<MenuItemViewModel>(); } private ObservableCollection<MenuItemViewModel> _childMenuItems; /// <summary> /// Gets the child menu items. /// </summary> /// <value>The child menu items.</value> public ObservableCollection<MenuItemViewModel> ChildMenuItems { get { return _childMenuItems; } } private string _header; /// <summary> /// Gets or sets the header. /// </summary> /// <value>The header.</value> public string Header { get { return _header; } set { _header = value; NotifyOnPropertyChanged("Header"); } } /// <summary> /// Gets or sets the parent view model. /// </summary> /// <value>The parent view model.</value> public MenuItemViewModel ParentViewModel { get; set; } public virtual void LoadChildMenuItems() { } }
The concrete MenuItems can be either instantiated directly or you could make your own SubTypes through inheritance.