Catel - CommandManager 未传递 CommandParameter

Catel - CommandManager not passing CommandParameter

我有一个带有 "MainView" 和一些嵌套视图的 Catel 应用程序。 嵌套视图有一个 ListView,一些项目有一个 ContextMenu,一些项目有 MenuItems

在 MainView 的 ViewModel 中,我创建了一个 TaskCommand<object>,它将对传递的参数执行某些操作。这个传递的参数应该是ListView当前的SelectedItem。此命令正在全局注册到 ICommandManager.

如果我使用来自 ICommandManager 的绑定命令单击 MenuItem,则传递的参数将始终为 null

这里是相关代码:

NestedView.xaml:

<catel:UserControl
  x:Name="UserControl"
  x:Class="My.NameSpace.Views.View"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:catel="http://catel.codeplex.com">

...
  <ListView ItemsSource="{Binding Items}"
            BorderThickness="0"
            SelectedItem="{Binding SelectedItem}"
            HorizontalContentAlignment="Stretch">
    <ItemsControl.ItemTemplate>
      <DataTemplate>
        <TextBlock VerticalAlignment="Center"
                   Margin="2.5"
                   Text="{Binding Description}">
          <TextBlock.ContextMenu>
            <ContextMenu>
              <MenuItem Header="Do Stuff"
                        Command="{catel:CommandManagerBinding DoStuffCommand}"
                        CommandParameter="{Binding DataContext.SelectedItem, ElementName=UserControl}" />
              ...
            </ContextMenu>
          </TextBlock.ContextMenu>
        </TextBlock>
      </DataTemplate>
    </ItemsControl.ItemTemplate>
  </ListView>
...
</catel:UserControl>

MainViewModel.cs:

public class MainViewModel : ViewModelBase {
    ...

    public ICommand DoStuffCommand { get; }

    public MainViewModel(ICommandManager commandManager, IUIVisualizerService uiVisualizerService, IMessageService messageService)
    {
      ...

      DoStuffCommand = new TaskCommand<object>(OnDoStuffCommandExecute);

      commandManager.CreateCommand(nameof(DoStuffCommand));
      commandManager.RegisterCommand(nameof(DoStuffCommand), DoStuffCommand, this);
    }

    private async Task OnDoStuffCommandExecute(object parameter)
    {
      // command needs to be in the MainViewModel because there will be some modification on the MainView based on parameter (adding tabs, etc)
      Debugger.Break();
    }

    ...
}

如果您需要更多代码,我也可以 post 但这应该足够了。

我还查看了 Catel 的 CommandManager 实现并发现:

    /// <summary>
    /// Executes the command.
    /// </summary>
    /// <param name="commandName">Name of the command.</param>
    /// <exception cref="ArgumentException">The <paramref name="commandName"/> is <c>null</c> or whitespace.</exception>
    /// <exception cref="InvalidOperationException">The specified command is not created using the <see cref="CreateCommand"/> method.</exception>
    public void ExecuteCommand(string commandName)
    {
        Argument.IsNotNullOrWhitespace("commandName", commandName);

        lock (_lockObject)
        {
            Log.Debug("Executing command '{0}'", commandName);

            if (!_commands.ContainsKey(commandName))
            {
                throw Log.ErrorAndCreateException<InvalidOperationException>("Command '{0}' is not yet created using the CreateCommand method", commandName);
            }

            _commands[commandName].Execute(null);
        }
    }

我假设如果我单击 MenuItem 将调用此方法并解释该行为。

是否有适当的 solution/workaround 将(绑定)参数传递给我的 OnExecute 方法?

提前致谢

  1. 在 xaml

    中的命令绑定 之前设置 CommandParameter 绑定

    菜单项Header="Do Stuff" CommandParameter="{Binding DataContext.SelectedItem, ElementName=UserControl}" Command="{catel:CommandManagerBinding DoStuffCommand}"

  2. 请记住,菜单项中的绑定上下文是 ,而不是 视图模型。如果你想绑定到视图模型,你需要为根网格提供一个名称并像这样绑定:

    CommandParameter="{Binding DataContext.SelectedItem, ElementName=rootGrid}"
    

请注意,使用 UserControl 无效,因为 VM 设置在内部网格中(请参阅文档了解原因的解释)。所以要么使用 UserControl.ViewModel 要么 SomeInnerControl.DataContext

好的,我找到了解决方案,但实际上有点奇怪。

首先调换Command={...}CommandParamter={...}的顺序(如Geert所说)。 然后在经历了很多失败之后我让它开始工作:只需传递到 CommandParameter-Binding the Markup 而不是其他任何东西。所以我的 XAML MenuItem 代码如下所示:

  <MenuItem Header="Do Stuff"
            CommandParameter="{Binding}"
            Command="{catel:CommandManagerBinding DoStuffCommand}"/>

编辑:

好的,现在我找到了解决方案(我想)。上面的方法大部分时间都有效,但可能 运行 正如@mm8 指出的那样出现问题。

所以我在谷歌上搜索得更多,发现了这个:(见更新 2)

这个答案中给出的解决方案现在对我来说很好用,因为我可以直接绑定到我的 ViewModel 并通过绑定获得所需的值。