如何在WPF中实现命令以使用祖先方法?

Tow*_*wer 7 c# wpf xaml

我有这个上下文菜单资源:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ContextMenu x:Key="FooContextMenu">
        <ContextMenu.CommandBindings>
            <CommandBinding Command="Help" Executed="{Binding ElementName=MainTabs, Path=HelpExecuted}" />
        </ContextMenu.CommandBindings>

        <MenuItem Command="Help">
            <MenuItem.Icon>
                <Image Source="../Resources/Icons/Help.png" Stretch="None" />
            </MenuItem.Icon>
        </MenuItem>
    </ContextMenu>
</ResourceDictionary>
Run Code Online (Sandbox Code Playgroud)

我想在两个地方重复使用它.首先,我试图把它放在DataGrid:

<DataGrid ContextMenu="{DynamicResource FooContextMenu}">...
Run Code Online (Sandbox Code Playgroud)

ContextMenu本身工作正常,但Executed="..."我现在打破了应用程序并抛出:

PresentationFramework.dll中出现"System.InvalidCastException"类型的第一次机会异常

附加信息:无法将类型为"System.Reflection.RuntimeEventInfo"的对象强制转换为"System.Reflection.MethodInfo".

如果我删除整个Executed="..."定义,那么代码就可以工作(并且命令不会执行任何操作/灰显).一旦我右键单击网格/打开上下文菜单,就会抛出异常.

DataGrid被放置在几个元素之下,但最终它们都在一个TabControl(被调用的MainTabs)下面,它被ItemsSource设置为一个FooViewModels 的集合,并且FooViewModel我有一个HelpExecuted我想要被调用的方法.

让我们想象一下:

  • TabControl(ItemsSource=ObservableCollection<FooViewModel>,x:Name=MainTabs)
      • 更多UI
        • DataGrid(带上下文菜单集)

为什么会出现这个错误,我怎样才能使上下文菜单命令"目标"的FooViewModelHelpExecuted方法是什么?

Mat*_*asG 3

不幸的是,您无法绑定Executeda ContextMenu,因为它是一个事件。另一个问题是,在您的应用程序的其余ContextMenu部分中不存在该问题VisualTree。这两个问题都有解决方案。

首先,您可以使用Tag父控件的属性来ContextMenu传递DataContext应用程序的。然后你就可以使用DelegateCommand你的了CommandBinding,然后就可以了。这是一个小示例View,显示了ViewModel以及DelegateCommand您必须添加到项目中的实现。

DelegateCommand.cs

public class DelegateCommand : ICommand
{
    private readonly Action<object> execute;
    private readonly Predicate<object> canExecute;

    public DelegateCommand(Action<object> execute)
        : this(execute, null)
    { }

    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        this.execute = execute;
        this.canExecute = canExecute;
    }

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return canExecute == null ? true : canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        execute(parameter);
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

主窗口视图.xaml

<Window x:Class="Application.MainWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindowView" Height="300" Width="300"
        x:Name="MainWindow">
    <Window.Resources>
        <ResourceDictionary>
            <ContextMenu x:Key="FooContextMenu">
                <MenuItem Header="Help" Command="{Binding PlacementTarget.Tag.HelpExecuted, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
            </ContextMenu>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <TabControl ItemsSource="{Binding FooViewModels}" x:Name="MainTabs">
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <DataGrid ContextMenu="{DynamicResource FooContextMenu}" Tag="{Binding}" />
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

MainWindowView.xaml.cs

public partial class MainWindowView : Window
{
    public MainWindowView()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel();
    }
}
Run Code Online (Sandbox Code Playgroud)

MainWindowViewModel.cs

public class MainWindowViewModel
{
    public ObservableCollection<FooViewModel> FooViewModels { get; set; }

    public MainWindowViewModel()
    {
        FooViewModels = new ObservableCollection<FooViewModel>();
    }
}
Run Code Online (Sandbox Code Playgroud)

FooViewModel.cs

public class FooViewModel
{
    public ICommand HelpExecuted { get; set; }

    public FooViewModel()
    {
        HelpExecuted = new DelegateCommand(ShowHelp);
    }

    private void ShowHelp(object obj)
    {
        // Yay!
    }
}
Run Code Online (Sandbox Code Playgroud)