Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add horizontal separator in a dynamically created ContextMenu?

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); 
like image 521
vladc77 Avatar asked Jan 28 '11 01:01

vladc77


People also ask

What is separator in WPF?

In this article A Separator control draws a line, horizontal or vertical, between items in controls, such as ListBox, Menu, and ToolBar.


3 Answers

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> 
like image 88
Rachel Avatar answered Sep 23 '22 22:09

Rachel


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.

like image 24
samneric Avatar answered Sep 25 '22 22:09

samneric


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

like image 33
Tin Man Avatar answered Sep 22 '22 22:09

Tin Man