1

In my application I want to dynamically build up a context-menu. The first MenuItem is static and the second one should be a Separator. All items after the Separator are dynamically created at runtime.

I don't want to use code-behind becaus I'm working with the MVVM-Pattern. My idea now was to create an interface called IAppMenuItem with the following three implementations

  • ModifyMenuItem (Static MenuItem)
  • SeparatorMenuItem
  • ExecuteMenuItem (Dynamic MenuItem

In the viewmodel of my application I've created an ObservableCollection<IAppMenuItem> which contains the ContextMenu-Items.

Until here everything works fine. My problem is the presentation of the ContextMenu-Items in the UI.

I tried to set the correct controls with the following DataTemplates in the Resources of the view.

<DataTemplate DataType="{x:Type model:SeparatorMenuItem}">
    <Separator/>
</DataTemplate>

<DataTemplate DataType="{x:Type model:ModifyMenuItem}">
    <MenuItem Header="Edit items"/>
</DataTemplate>

<DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
    <MenuItem Header="{Binding DisplayText}"/>
</DataTemplate>

The definition of my ContextMenu is just:

<ContextMenu ItemsSource="{Binding MenuItemsCollection}"/>

The DataTemplates are working fine, but the controls are drawn inside a MenuItem. So for example for the Separator I see in the UI a Separator-Control inside a MenuItem-Control. But I need the Separator to be the Control.

Anyone have an idea how to set the Controls inside the DataTemplates directly to the contextmenu?


Update:

The complete ContextMenu looks like:

<ToggleButton Margin="0,0,10,0"
              AutomationProperties.Name="Update"
              AutomationProperties.AutomationId="Update_List"
              Content="Update"
              AttachedProperties:ButtonExtensions.IsDropDownButton="True"
              Style="{StaticResource GenericToggleButtonStyle}">
    <ToggleButton.ContextMenu>
        <controls:CustomContextMenu ItemsSource="{Binding MenuItemsCollection}">
            <ContextMenu.Resources>
                <ResourceDictionary>
                    <DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
                        <MenuItem Header="{Binding DisplayText}"/>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type model:ModifyMenuItem}">
                        <MenuItem Header="Edit items"/>
                    </DataTemplate>
                </ResourceDictionary>
            </ContextMenu.Resources>
        </controls:CustomContextMenu>       
    </ToggleButton.ContextMenu>
</ToggleButton>

The GenericToggleButtonStyle is just:

<Style x:Key="GenericToggleButtonStyle" TargetType="ToggleButton"
       BasedOn="{StaticResource {x:Type ToggleButton}}">
    <Setter Property="MinWidth" Value="80" />
    <Setter Property="Height" Value="22" />
    <Setter Property="Padding" Value="3,1" />
</Style>

Here is a screenshot of the MenuItems

MenuItems

2
  • Can you add a screenshot of the incorrect MenuItems on the UI? Commented Nov 16, 2015 at 12:01
  • Please see updated answer to resolve "inner" MenuItems Commented Nov 16, 2015 at 12:24

1 Answer 1

1

It seems when you set the ItemSource property on a ContextMenu the default ItemContainer used is MenuItem. This is why the Separator is rendered as a MenuItem.

You can fix this by implementing your own ContextMenu control that inherits from ContextMenu.

You need to override the IsItemItsOwnContainerOverride and GetContainerForItemOverride methods.

Please see my example below:

public class CustomContextMenu 
    : ContextMenu
{
    private bool _mustGenerateAsSeparator = false;

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
         _mustGenerateAsSeparator = (item is SeparatorMenuItem);

         return base.IsItemItsOwnContainerOverride(item);            
    }

    protected override System.Windows.DependencyObject GetContainerForItemOverride()
    {
        if (_mustGenerateAsSeparator)
        {
            return new Separator { Style = this.FindResource(MenuItem.SeparatorStyleKey) as System.Windows.Style };
        }
        else 
        {
            return base.GetContainerForItemOverride();                      
        }            
    }
}

Style = this.FindResource(MenuItem.SeparatorStyleKey) as System.Windows.Style is needed as you have to apply the default MenuItem.SeparatorStyleKey style to get the default separator style.

This link pointed me into the right direction http://drwpf.com/blog/category/item-containers/

How to use the custom control in XAML:

window declaration :xmlns:cnt="your namespace"

<Label Content="Dynamic Menu">
        <Label.ContextMenu>                
            <cnt:CustomContextMenu x:Name="contextMenu" ItemsSource="{Binding MenuItemsCollection}">                    
                <ContextMenu.Resources>
                    <ResourceDictionary>                            
                        <DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
                            <MenuItem Header="{Binding DisplayText}"></MenuItem>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type model:ModifyMenuItem}">
                            <MenuItem Header="{Binding DisplayText}"></MenuItem>
                        </DataTemplate>                            
                    </ResourceDictionary>
                </ContextMenu.Resources>
            </model:CustomContextMenu>                            
        </Label.ContextMenu>
    </Label> 

Second Part of the question:

Update your data templates to use TextBlock as they will be rendered inside a MenuItem, please see below:

<DataTemplate DataType="{x:Type model:ModifyMenuItem}">
    <TextBlock Header="Edit items"/>
</DataTemplate>

<DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
    <TextBlock Header="{Binding DisplayText}"/>
</DataTemplate>
Sign up to request clarification or add additional context in comments.

2 Comments

Really cool. The separator works perfect. Still having the problem with the other items. There is a MenuItem drawn inside another MenuItem.
Can you post your entire XAML from your context menu? Or a screenshot of the incorrect menuitems?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.