0

I am writing a C# application using WFP and the MVVM pattern.

In my view, I have a TreeView and each Item in that Tree View has its own Context Menu displayed when the user right clicks. The behavior I want a SubMenu within the Context Menu's Menu item that also allows for an Item Source to be supported.

Here is a text version of what I'd like: ContextMenu (items sourced from an Item Source) MenuItem_A (item sourced from an Item Source) SubMenuItem_A MenuItem_B (might not have any sub-items)

Here's what I tried:

<TreeView x:Name="MyTreeView" Tag={Binding ElementName=MyTreeView, Path=DataContext}>
    <TreeView.ItemTemplate> 
        <HeriarachicalDataTemplate DataType={x:Type namespace:ItemsViewModel} 
            <Grid> 
                <Grid.ContextMenu>
                    <ContextMenu 
    DataContext={Binding Path=PlacementTarget.Tag, RelativeSouce={Self}} ItemSource ={Binding HostedMenuOptions}>
                    <ContextMenu.ItemContainerStyle>
                        <Style TargetType="MenuItem"> 
                            <Setter Property="Header" Value={Binding HostedMenuItemName}> 
                            <Setter Property="ItemsSource" Value={Binding RelativeSource={RelativeSource FindAcestor, AncestorType={x:TYpe ContextMenu}}, Path=DataContext.HostedMenuItemSubMenus}/>
                        </Style> 
                    </ContextMenu.ItemContainerStyle> 
                </Grid.ContextMenu> 
            </Grid> 
        </HerarchicalDataTemplate> 
    </TreeView.ItemTemplate>
</TreeView>

The error that I'm seeing is an xaml binding error: HostedMenuItemName property not found on object of RunTimeType

Is there another way to achieve this? Basically I want the contextmenu, menus and submenus to to be determined by lists of objects that are stored in the ViewModel.

sdub0800
  • 139
  • 1
  • 9
  • Placementtarget is the thing you should google. The context menu is in a separate window so doesn't inherit datacontext. https://stackoverflow.com/questions/15033522/wpf-contextmenu-woes-how-do-i-set-the-datacontext-of-the-contextmenu – Andy Feb 02 '23 at 15:17
  • An alternative is to define whatever you want in the contextmenu as a resource somewhere whose datacontext is say the window. – Andy Feb 02 '23 at 15:18
  • I do have the PlacementTag set so the ContextMenu is getting the correct data context of the treeview. I can update the question to reflect that – sdub0800 Feb 02 '23 at 15:23
  • The menu items are coming from the ItemSource that are set in the context menu. – sdub0800 Feb 02 '23 at 15:33
  • That menuitem itemssource binding will have the datacontext of your menuitem. Where's your tag and placementtarget binding? – Andy Feb 02 '23 at 15:38
  • The datacontext of a menuitem subitems you get in the contextmenu will not need any of that relativesource binding. Their itemssource should just be {Binding ChildCollection} – Andy Feb 02 '23 at 15:59
  • That cannot be your real markup though. You have no quotes around those bindings – Andy Feb 02 '23 at 16:00

1 Answers1

0

Here's some experimental markup I tried.

I have a treeview which is bound to a collection of family which itself has family members.

As a quick and dirty experiment I added a class MenuThing

public class MenuThing : baseVM
{
    public string Name { get; set; }

    public List<MenuThing> MenuChildren { get; set; }
}

In my treeview resources I define a contextmenu as a resource:

            <ContextMenu ItemsSource="{Binding MenuThings}" x:Key="ContextMenuAsResource">
                <ContextMenu.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:MenuThing}" ItemsSource="{Binding MenuChildren}">
                        <MenuItem Header="{Binding Name}"/>
                    </HierarchicalDataTemplate>
                </ContextMenu.Resources>
            </ContextMenu>
        </TreeView.Resources>

Note that this will just be looking for a collection called MenuThings in the viewmodel a treeviewitem is bound to.

And I have a hierarchicaldatatemplate for family:

        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type local:Family}" ItemsSource="{Binding Members}">
               <Border>
                    <StackPanel Orientation="Horizontal"
                                Height="32"
                                ContextMenu="{DynamicResource ContextMenuAsResource}"
                                >
                        <Label VerticalAlignment="Center" FontFamily="WingDings" Content="1"/>
                        <TextBlock Text="{Binding Name}" />
                    </StackPanel>
                </Border>
            </HierarchicalDataTemplate>

Note that this template references the contextmenu resource.

This gets round some of that placementtarget stuff.

My contextmenu doesn't have any commands of course but it does display.

enter image description here

If some of the treeviewitems won't have commands then you'll get a sort of empty little box. You'd probably want to suppress that. But maybe every one of your treeviewitems will have commands.

My data setup.

    Families = new ObservableCollection<Family>();

    Family family1 = new Family() { Name = "The Doe's" };

    family1.MenuThings = new List<MenuThing>
    {
        new MenuThing{ Name="AA", MenuChildren=new List<MenuThing>{new MenuThing{Name ="AAA" }, new MenuThing { Name = "AAB" } } },
        new MenuThing{ Name="BB", MenuChildren=new List<MenuThing>{new MenuThing{Name ="BBB" }, new MenuThing { Name = "BBBB" } } }
    };

    family1.Members.Add(new FamilyMember() { Name = "John Doe", Age = 42 });
                    family1.Members.Add(new FamilyMember() { Name = "Jane Doe", Age = 39 });
                    family1.Members.Add(new FamilyMember() { Name = "Sammy Doe", Age = 13 });
                    Families.Add(family1);

                    Family family2 = new Family() { Name = "The Moe's" };
    family2.Members.Add(new FamilyMember() { Name = "Mark Moe", Age = 31 });
                    family2.Members.Add(new FamilyMember() { Name = "Norma Moe", Age = 28 });
                    Families.Add(family2);
    }
Andy
  • 11,864
  • 2
  • 17
  • 20
  • Thanks Andy, that empty little box and the strange display that is being show on your MenuItem AAA. Is becuase you have a menu item that is getting wrapped in a menu item. To get around that you can use an itemcontainerstyle. I'll give this a try with the heriarchical data template at the context menu level like you have shown – sdub0800 Feb 02 '23 at 16:47
  • Think you could use a datatrigger to collapse the context menu if there are zero items if it's an issue. – Andy Feb 02 '23 at 18:23
  • Thanks Andy, the key was using a hierarchical data template. You were close with your proposed solution and it did lead me in the right direction. I'll update this post with what I ended up getting work. – sdub0800 Feb 02 '23 at 19:24