【问题标题】:Catel - CommandManager not passing CommandParameterCatel - CommandManager 未传递 CommandParameter
【发布时间】:2017-08-06 07:05:39
【问题描述】:

我有一个带有“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();
    }

    ...
}

如果您需要更多代码,我也可以发布此内容,但这应该足够了。

我还查看了 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 会调用此方法并解释该行为。

是否有任何适当的解决方案/解决方法可以将(绑定)参数传递给我的 OnExecute 方法?

提前致谢

【问题讨论】:

    标签: c# wpf mvvm catel


    【解决方案1】:
    1. 在xaml中设置CommandParameter绑定之前命令绑定

      MenuItem Header="做事" CommandParameter="{绑定 DataContext.SelectedItem, ElementName=UserControl}" Command="{catel:CommandManagerBinding DoStuffCommand}"

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

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

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

    【讨论】:

    • 我更改了Command={...}CommandParameter={...} 的顺序,但它仍然通过null。我调试了代码,似乎 Binding-Expression 没有得到正确评估。我在视图的代码隐藏中为MenuItemClick-Event 添加了一个方法,而CommandParameter 始终为空。我实际上不知道这段代码有什么问题
    【解决方案2】:

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

    首先交换Command={...}CommandParamter={...} 的顺序(就像Geert 所说的那样)。 然后在经历了很多失败之后,我得到了它的工作:只需传递到 CommandParameter-Binding 标记而不是其他任何东西。所以我的MenuItem 的 XAML 代码如下所示:

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

    编辑:

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

    所以我用谷歌搜索了更多,发现了这个:https://stackoverflow.com/a/3668699(见更新 2)

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

    【讨论】:

    • 您不能绑定到 ContextMenu 的 MenuItem 中的 ElementName=UserControl,因为 ContextMenu 和 UserControl 位于不同的元素树中。您使用 Command="{Binding}" 绑定到的 MenuItem 的 DataContext 是您在打开 ContextMenu 时单击的项目。这不一定与当前选定的项目相同。
    猜你喜欢
    • 1970-01-01
    • 2012-12-14
    • 2012-05-13
    • 2022-01-26
    • 2014-04-22
    • 1970-01-01
    • 2015-04-06
    • 2012-09-04
    • 2016-04-27
    相关资源
    最近更新 更多