Search code examples
c#wpfcontextmenusubmenu

C# WPF Context Menu Data Binding


I'm trying to create a dynamic Context Menu in the WPF DataGrid. The following are the issues that I need help:

1) Root Menu Item Header are not bind with ViewModel while the submenu works fine.

2) The submenu always pop up on the left side instead of the right. How can I fix this with style?

<DataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding PackageCM.Members}" HasDropShadow="True" Placement="Right">
    <ContextMenu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Header" Value="{Binding CategoryName}" />
        </Style>
    </ContextMenu.ItemContainerStyle>
    <ContextMenu.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Items}">
            <MenuItem Header="{Binding DisplayName}" Command="{Binding AllPackagesVM.OpenCOBAPackageCommand, Source={StaticResource Locator}}"></MenuItem>
        </HierarchicalDataTemplate>
    </ContextMenu.ItemTemplate>
</ContextMenu>

Root Menu Item Header are not being bind.

Basically, Context Menu is binding to the PackageCM.Members with has a list of Category object and I want to display the CategoryName on the Context Menu root. Following that, Each Category contains a list of Items which will be showed as submenu.

Thanks in advance for help.


Solution

  • First, your ContextMenu.ItemTemplate definition is incorrect, when you set an ItemSource for ContextMenu you do not define MenuItems yourself because actually ContextMenu will wrap this content inside another MenuItem. So you need to change your template to something like this:

    <ContextMenu ItemsSource="{Binding PackageCM.Members}" ...>
        <ContextMenu.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Items}">
                <TextBlock Text="{Binding DisplayName}"></TextBlock >
            </HierarchicalDataTemplate>
        </ContextMenu.ItemTemplate>
    </ContextMenu>
    

    You need to put a TextBlock instead of MenuItem because you want to display a text in your ContextMenu menus and bind its Text property to a propety in your model. But so this to work, the model used for root menus and model used for sub-menus must have a property that is named equally, in your case it is DisplayName for sub-menus so in your root menus model must also have a property named DisplayName, This property is bound to Text property of the TextBlock.

    You need to do some renaming in your models or introduce an new propety named DisplayName in Category model. So your models would have a common propety something like in this snippet:

    // for root menu
    public class Category
    {
        public string CategoryName { get; }
        public string DisplayName => CategoryName;
        ...
    }
    
    // for submenus
    public class Item
    {
        public string DisplayName { get; }
        ...
    }
    

    Hopefully this explanation helps you understand the problem for missing header values.