I was looking for the solution on the internet but was not able to find it within my sample. I need to add a separator between Context menu item that are generated from code behind. I tried to add it with such code lines like below but without success.
this.Commands.Add(new ToolStripSeparator());
I am wondering if someone can help. Thank you in advance.
Context Menu XAML:
<Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu ItemsSource="{Binding Commands}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding}" />
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Setter.Value>
</Setter>
C# that added in the method:
this.Commands = new ObservableCollection<ICommand>();
this.Commands.Add(MainWindow.AddRole1);
this.Commands.Add(MainWindow.AddRole2);
this.Commands.Add(MainWindow.AddRole3);
this.Commands.Add(MainWindow.AddRole4);
//this.Add(new ToolStripSeparator());
this.Commands.Add(MainWindow.AddRole5);
this.Commands.Add(MainWindow.AddRole6);
this.Commands.Add(MainWindow.AddRole7);
In this article A Separator control draws a line, horizontal or vertical, between items in controls, such as ListBox, Menu, and ToolBar.
I did this once and used a null
as my separator. From the XAML, I then styled the template to use a separator if the datacontext was null
Code behind:
this.Commands.Add(MainWindow.AddRole4); this.Add(null); this.Commands.Add(MainWindow.AddRole5);
XAML was something like this:
<ContextMenu.ItemContainerStyle> <Style TargetType="{x:Type MenuItem}"> <Setter Property="Command" Value="{Binding}" /> <Setter Property="Header" Value="{Binding Path=Text}" /> <Setter Property="CommandParameter" Value="{Binding Path=Parameter}" /> <Style.Triggers> <DataTrigger Binding="{Binding }" Value="{x:Null}"> <Setter Property="Template" Value="{StaticResource MenuSeparatorTemplate}" /> </DataTrigger> </Style.Triggers> </Style> </ContextMenu.ItemContainerStyle>
Hope I got the syntax right - I don't have an IDE on this machine to verify the code
EDIT
Here is an example template for the context menu separator. I am putting it in ContextMenu.Resources
, although you could put this anywhere you want in your application as long as the ContextMenu can access it.
<ContextMenu.Resources> <ControlTemplate x:Key="MenuSeparatorTemplate"> <Separator /> </ControlTemplate> </ContextMenu.Resources>
EDIT:
My first answer to this question, though it actually worked, does not follow the MVVM design principle. I am now providing an MVVM approach and leaving the original answer below for reference.
You can create a behavior to solve this problem.
XAML:
<Menu> <MenuItem Header="_File" menu:MenuBehavior.MenuItems="{Binding Path=MenuItemViewModels, Mode=OneWay}"> </MenuItem> </Menu>
ViewModel:
public IEnumerable<MenuItemViewModelBase> MenuItemViewModels => new List<MenuItemViewModelBase> { new MenuItemViewModel { Header = "Hello" }, new MenuItemSeparatorViewModel(), new MenuItemViewModel { Header = "World" } };
Behavior:
public class MenuBehavior { public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.RegisterAttached("MenuItems", typeof(IEnumerable<MenuItemViewModelBase>), typeof(MenuBehavior), new FrameworkPropertyMetadata(MenuItemsChanged)); public static IEnumerable<MenuItemViewModelBase> GetMenuItems(DependencyObject element) { if (element == null) { throw (new ArgumentNullException("element")); } return (IEnumerable<MenuItemViewModelBase>)element.GetValue(MenuItemsProperty); } public static void SetMenuItems(DependencyObject element, IEnumerable<MenuItemViewModelBase> value) { if (element == null) { throw (new ArgumentNullException("element")); } element.SetValue(MenuItemsProperty, value); } private static void MenuItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var menu = (MenuItem)d; if (e.OldValue != e.NewValue) { menu.ItemsSource = ConvertViewModelsToFrameworkElements((IEnumerable<MenuItemViewModelBase>)e.NewValue); } } private static IEnumerable<FrameworkElement> ConvertViewModelsToFrameworkElements(IEnumerable<MenuItemViewModelBase> viewModels) { var frameworkElementList = new List<FrameworkElement>(); foreach (var viewModel in viewModels) { switch (viewModel) { case MenuItemViewModel mi: frameworkElementList.Add(new MenuItem { Header = mi.Header, Command = mi.Command, Icon = mi.Icon }); break; case MenuItemSeparatorViewModel s: frameworkElementList.Add(new Separator()); break; } } return frameworkElementList; } }
Classes:
public class MenuItemViewModelBase { } public class MenuItemViewModel : MenuItemViewModelBase { public object Header { get; set; } public ICommand Command { get; set; } public object Icon { get; set; } } public class MenuItemSeparatorViewModel : MenuItemViewModelBase { }
Original Answer:
Or, instead of having your ContextMenu bind to a collection of commands, bind it to a collection of FrameworkElements then you can add either MenuItems or Separators directly to the collection and let the Menu control do all the templating....
<Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}"> <Setter Property="ContextMenu"> <Setter.Value> <ContextMenu ItemsSource="{Binding Commands}" /> </Setter.Value> </Setter> </Style>
C#:
this.Commands = new ObservableCollection<FrameworkElement>(); this.Commands.Add(new MenuItem {Header = "Menuitem 2", Command = MainWindow.AddRole1}); this.Commands.Add(new MenuItem {Header = "Menuitem 2", Command = MainWindow.AddRole2}); this.Commands.Add(new MenuItem {Header = "Menuitem 3", Command = MainWindow.AddRole3}); this.Commands.Add(new MenuItem {Header = "Menuitem 4", Command = MainWindow.AddRole4}); this.Commands.Add(new Separator); this.Commands.Add(new MenuItem {Header = "Menuitem 5", Command = MainWindow.AddRole5}); this.Commands.Add(new MenuItem {Header = "Menuitem 6", Command = MainWindow.AddRole6}); this.Commands.Add(new MenuItem {Header = "Menuitem 7", Command = MainWindow.AddRole7});
Just used this approach in my app - the separator looks better this way also.
I have modified the solution provided by Rachel above to correct the Separator style. I realize this post is old, but still one of the top results on Google. In my situation, I was using it for a Menu vs a ContextMenu, but the same should work.
XAML
<Menu ItemsSource="{Binding MenuItems}">
<Menu.Resources>
<ControlTemplate x:Key="MenuSeparatorTemplate">
<Separator>
<Separator.Style>
<Style TargetType="{x:Type Separator}" BasedOn="{StaticResource ResourceKey={x:Static MenuItem.SeparatorStyleKey}}"/>
</Separator.Style>
</Separator>
</ControlTemplate>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding MenuItemHeader}" />
<Setter Property="Command" Value="{Binding MenuItemCommand}" />
<Setter Property="CommandParameter" Value="{Binding MenuItemCommandParameter}" />
<Setter Property="ItemsSource" Value="{Binding MenuItemCollection}" />
<Style.Triggers>
<DataTrigger Binding="{Binding }" Value="{x:Null}">
<Setter Property="Template" Value="{StaticResource MenuSeparatorTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.Resources>
</Menu>
Without Separator Style Change
With Separator Style Change
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With