Specify Command for MenuItem in a DataTemplate

2020-02-05 02:02发布

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?

3条回答
爷的心禁止访问
2楼-- · 2020-02-05 02:46

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>
查看更多
等我变得足够好
3楼-- · 2020-02-05 02:46

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...
}
查看更多
在下西门庆
4楼-- · 2020-02-05 02:56

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.

查看更多
登录 后发表回答