Specify Command for MenuItem in a DataTemplate
Asked Answered
H

3

23

I have a context menu. It's bound to some collection and it has a defined ItemTemplate like this:

<ContextMenu
    ItemsSource={Binding ...}
    ItemTemplate={StaticResource itemTemplate}
    />

itemTemplate is a simple DataTemplate with a TextBlock:

<DataTemplate x:Key="itemTemplate">
    <TextBlock Text={Binding ...} />
</DataTemplate>

How do I bind Command property for MenuItem to the underlying object's property?

Hysell answered 22/5, 2009 at 16:49 Comment(0)
P
28

I think you need to wrap your TextBlock in a MenuItem:

<DataTemplate x:Key="itemTemplate">
    <MenuItem Command={Binding ...}>
        <TextBlock Text={Binding ...} />
    </MenuItem>
</DataTemplate>

But I don't have an IDE in front of me right now to try this. Let me know how it goes.


Looks like you need to use the ItemContainerStyle as seen here. Sorry for leading you down the wrong path at the start there - but I got in front of an IDE and this works:

<ContextMenu.ItemContainerStyle>
    <Style TargetType="MenuItem">
        <Setter Property="Command" Value="{Binding ...}"/>
    </Style>
</ContextMenu.ItemContainerStyle>
Phototypy answered 22/5, 2009 at 16:56 Comment(6)
Actually, this will add TextBlock to the MenuItem's Items collection. And it also puts MenuItem inside of another MenuItem.Hysell
But with your post I started thinking of templating MenuItem itself, this could be what i need.Hysell
This creates double MenuItem's, which f*cks up styling! Don't think this is an appropriate answer!Isostasy
@Isostasy - Read the second part of the answer where I correct myself. I didn't remove the incorrect first half because it had already been commented on.Phototypy
Excuse me for my first reaction; that second solution is indeed working.Isostasy
What if there is more than one menuitem and we need to bind the menu items with individual commands.Malo
S
8

Although this is only a slight variation on Martin Harris's answer, I thought I'd share it anyway. I found it more useful specify a single command for the whole collection and also send along a CommandParameter:

<MenuItem.ItemContainerStyle>
    <Style TargetType="MenuItem">
       <Setter Property="Command" Value="{x:Static v:ViewModel.CommandForAll}"/>
       <Setter Property="CommandParameter" Value="{Binding ValueForCommand}"/>
    </Style>
</MenuItem.ItemContainerStyle>

Then you can determine what to do in the handler for the command:

private void CommandForAll_Executed(object sender, ExecutedRoutedEventArgs e)
{
    var cmdParam = e.Paramater as ExpectedType
    if (cmdParam != null)
        //DoStuff...
}
Southeastwards answered 4/5, 2012 at 19:33 Comment(0)
C
0

I realize I'm answering this quite a bit after the fact, but I ran into the same problem and the previous answers seemed to make binding to multiple different commands difficult. The solution I arrived is very similar to MatrixManAtYrService's and works in 3 parts:

1) Bind the command using the ItemContainerStyle property to a command in the ViewModel -- this is the same as the previous answers. One exception is that I bind the CommandParameter to the MenuItem.

<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}}"/>

2) Create a custom class to define the look and behavior of each MenuItem. The ItemsSource of the menu will be set to a list of these. This is the same as other answers. However, in my implementation I have given the class an Action to be executed when the MenuItemCommand is invoked. I also included a boolean that will allow the MenuItem to be disabled.

public class MenuAction
{
    public string Name { get => name; set => name = value; }
    public Action Action { get => action; set => action = value; }
    public bool CanExecute { get => canExecute; set => canExecute = value; }
}

3) In the command implementation route control to the delegates in MenuAction.

public void HandleCommand(object sender)
{
    MenuItem clickedMenuItem = sender as MenuItem;
    MenuAction menuAction = clickedMenuItem?.DataContext as MenuAction;
    if(menuAction != null)
        menuAction.Action();
}

public bool CanMenuItemExecute(object sender)
{
    MenuItem clickedMenuItem = sender as MenuItem;
    MenuAction menuAction = clickedMenuItem?.DataContext as MenuAction;
    if (menuAction != null)
        return menuAction.CanExecute;
    else
        return false;
}

This should allow you to define all of the behavior of your commands in a list. While it is technically binding to a single command it is functionally similar to having multiple different commands. The same method should also work with nested MenuItems and HierarchicalDataTemplates with some tweaking.

Cuman answered 3/12, 2019 at 7:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.