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:
WPF Binding menu item with bounded Submenu
Don't add a MenuItem
element in the ItemTemplate
. There is a MenuItem
element generated implicitly for each item in the ItemsSource
. You should set the ItemTemplate
to an HierarchicalDataTemplate
.
Try this:
<Menu>
<Menu.Resources>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding MenuItemCommand}" />
</Style>
</Menu.Resources>
<MenuItem Header="Minor Graph" ItemsSource="{Binding GraphMenuItems}">
<MenuItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
wpf: Binding of nested Menu items
You could use this
<Menu>
<MenuItem Header="Select Source:"
ItemsSource="{Binding FirstViewModel.MenuItemsSecondLevel}">
<MenuItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:MySecondViewModel}"
ItemsSource="{Binding MenuItemsThirdLevel}">
<TextBlock Text="{Binding DisplayName}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:MyThirdViewModel}">
<CheckBox Content="{Binding DisplayName}" />
</DataTemplate>
</MenuItem.Resources>
</MenuItem>
</Menu>
Assuming FirstViewModel
is a property of your viewmodel.
How to bind Menu items with observableCollection in xaml and wpf?
MenuItems
have an ItemsSource
property.
<Menu>
<MenuItem Header="Item Collection" ItemsSource="{Binding ItemCollection}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }"
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
See this question for a more complete answer.
Dynamic menus and submenus with Caliburn Micro, how can I bind the commands?
Add a setter to your Style
that sets the cal:Message.Attach
attached property and passes the $executionContext
:
<Menu x:Name="ToolBarMenuItems">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="ItemsSource" Value="{Binding Path=Children}" />
<Setter Property="cal:Message.Attach" Value="RunCommand($executionContext)" />
</Style>
</Menu.ItemContainerStyle>
</Menu>
You need the context in your interface and implementation to be able to stop the event from bubbeling:
public void RunCommand(ActionExecutionContext context)
{
if (context?.EventArgs is RoutedEventArgs routedEventArgs)
routedEventArgs.Handled = true;
MessageBox.Show("Run!");
}
Please refer to this question for more information about this.
XAML and Binding Submenu items in a ContextMenu?
Guess I should have experimented more. Turns out this was relatively simple:
<my:DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Add to" ItemsSource="{Binding MyItems}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem CommandTarget="{Binding}" Click="AddClick">
<MenuItem.Header>
<TextBlock>
<TextBlock.Text><Binding StringFormat="Add to {0}" /></TextBlock.Text>
</TextBlock>
</MenuItem.Header>
</MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="Remove from All" />
</ContextMenu>
</my:DataGrid.ContextMenu>
How to properly create a sub-menu for a menu item in WPF
Unless you're a XAML ninja, the best way to work with any template is to start by extracting the default template.
Because it is very very easy to totally break controls when you start doing this sort of stuff. A template totally utterly replaces everything that makes up your control. In particular, be very careful to ensure any contentpresenter or named part is still in there once you finish.
Step by step:
Add a control of the type you want (menuitem) to a view.
Select it.
Over in the properties window find the Miscellaneous group.
Scroll down to template.
Click the little box on the right.
Choose Convert to new resource.
You can find you need to add references to stuff for some templates.
In any case, ensure that works.
You are now starting with a working template ( rather than a broken one ).
Make a small change and test.
Repeat.
Creep up on your requirement by a teeny tiny baby step at a time.
Also.
Rather than defining menuitems in code, template them out from a bound hierarchical collection.
Related Topics
No Console Output When Using Allocconsole and Target Architecture X86
How to Disable a System Device Programmatically
Inheritance with Base Class Constructor with Parameters
How to Get a Uri of the Image Stored in the Resources
JSON.Net Serialize Specific Private Field
Server Execution Failed (Exception from Hresult: 0X80080005 (Co_E_Server_Exec_Failure))
Passing Object in Redirecttoaction
How to Find Out When You'Ve Been Loaded via Xml Serialization
How to Find the State of Numlock, Capslock and Scrolllock in .Net
Getting Specified Node Values from Xml Document
Win32_Processor::Is Processorid Unique for All Computers
How to Use Httpclient to Send Content in Body of Get Request
String Concatenation VS String Builder. Performance
C# - Fill a Combo Box with a Datatable