使用 ViewModel 中定义的 RelayCommand 传递参数(来自 Josh Smith 示例)

Posted

技术标签:

【中文标题】使用 ViewModel 中定义的 RelayCommand 传递参数(来自 Josh Smith 示例)【英文标题】:Passing a parameter using RelayCommand defined in the ViewModel (from Josh Smith example) 【发布时间】:2010-10-23 01:14:29 【问题描述】:

我想通过使用 RelayCommand 将我的应用程序的 XAML(视图)中定义的参数传递给 ViewModel 类。我关注了Josh Smith's excellent article on MVVM,并实现了以下内容。

XAML 代码

        <Button 
        Command="Binding Path=ACommandWithAParameter"
        CommandParameter="Orange"
        HorizontalAlignment="Left" 
        Style="DynamicResource SimpleButton" 
        VerticalAlignment="Top" 
        Content="Button"/>

ViewModel 代码

  public RelayCommand _aCommandWithAParameter;
  /// <summary>
  /// Returns a command with a parameter
  /// </summary>
  public RelayCommand ACommandWithAParameter
  
     get
     
        if (_aCommandWithAParameter == null)
        
           _aCommandWithAParameter = new RelayCommand(
               param => this.CommandWithAParameter("Apple")
               );
        

        return _aCommandWithAParameter;
     
  

  public void CommandWithAParameter(String aParameter)
  
     String theParameter = aParameter;
  
  #endregion

我在 CommandWithAParameter 方法中设置了一个断点,并观察到 ​​aParameter 设置为“Apple”,而不是“Orange”。这似乎很明显,因为使用文字字符串“Apple”调用了 CommandWithAParameter 方法。

但是,查看执行堆栈,我可以看到“橙色”,我在XAML中设置的CommandParameter是ICommand Execute接口方法的RelayCommand实现的参数值。

即执行栈下面方法中的参数值为“橙色”,

  public void Execute(object parameter)
  
     _execute(parameter);
  

我想弄清楚的是如何创建 RelayCommand ACommandWithAParameter 属性,以便它可以使用 XAML 中定义的 CommandParameter“Orange”调用 CommandWithAParameter 方法。

有没有办法做到这一点?

我为什么要这样做? “即时本地化”的一部分 在我的特定实现中,我想创建一个可以绑定到多个按钮的 SetLanguage RelayCommand。我想将两个字符语言标识符(“en”、“es”、“ja”等)作为 CommandParameter 传递,并为 XAML 中定义的每个“设置语言”按钮进行定义。我想避免必须为每种支持的语言创建 SetLanguageToXXX 命令,并将两个字符语言标识符硬编码到 ViewModel 中的每个 RelayCommand 中。

【问题讨论】:

对这个问题提供的答案都没有帮助我创建一个 RelayCommand 对象来与需要参数输入的方法进行通信。如果有人有类似的问题,这个帖子帮助了我:***.com/questions/5298910/… 【参考方案1】:

我不明白为什么您首先要指定 lambda 的额外复杂性。为什么不这样做:

if (_aCommandWithAParameter == null)
           
    _aCommandWithAParameter = new RelayCommand<object>(CommandWithAParameter);


private void CommandWithAParameter(object state)

    var str = state as string;

【讨论】:

只读操作 _execute;只读谓词 _canExecute; public RelayCommand(Action execute) : this(execute, null) public RelayCommand(Action execute, Predicate canExecute) if (execute == null) throw new ArgumentNullException("execute"); _execute = 执行; _canExecute = 可以执行; Action 是一个委托,在调用构造函数时需要一个 lambda 表达式。不知道为什么,否则我会收到编译器错误。 因为它是 Action,而不是 Action。更新了我的帖子以澄清。 对第一条评论中的间距感到抱歉。无论如何,关键是由于 RelayCommand 构造函数中的参数参数,需要 lambda 表达式。即 public RelayCommand(Action execute) : this(execute, null)。由于 RelayCommand 的构造函数需要一个委托,因此在创建新的 RelayCommand 时,必须使用我的帖子中显示的 lambda 表达式。在 lambda 表达式右侧使用的方法中的参数类型确实改变了在创建 RelayCommand 时必须使用 lambda 表达式这一事实。 没有参数就不用lambda表达式了吧?否则它不会为我编译。 RelayCommand 构造函数采用Action&lt;object&gt;,因此您传递给它的方法必须与该委托匹配。我上面的代码示例中的那个,因为它需要一个对象参数。如果您的方法不采用对象参数,那么最简单的转换方法确实是使用 lambda:new RelayCommand(_ =&gt; MyMethod())(下划线是未使用的 lambda 参数的常规名称)【参考方案2】:

您将 lambda 中的参数传递给命令,如下所示:

if (_aCommandWithAParameter == null)
           
    _aCommandWithAParameter = new RelayCommand(               
        param => this.CommandWithAParameter(param)
        );        

【讨论】:

Paul,当我使用您上面建议的语法时,我收到编译器错误“无效表达式')'”。 ** 字符用于表示强调 - 它们不应出现在您实际剪切和粘贴的代码中。 保罗谢谢你是对的。我应该意识到两边的**是为了强调。您的解决方案有效。我将 Kent 的解决方案标记为答案,因为它更简单(或者我应该说没有 lambda 表达式更容易理解)。 您是否介意编辑您的答案以显示 = new RelayCommand(param => this.CommandWithAParameter(param))。我认为这会让其他人更容易理解,他们不会犯我犯的同样的剪切和粘贴错误。我猜我没有足够的声望点来编辑其他人的答案,否则我会自己做。【参考方案3】:

在此之前发布的任何内容都对我有用。

事实证明,RelayCommand 之后的所有答案都缺少&lt;object&gt;

这对我有用:

public RelayCommand<object> OKCommand

    get
    
        if (_okCommand == null)
            _okCommand = new RelayCommand<object>(OkCommand_Execute);
        return _okCommand;
    

private RelayCommand<object> _okCommand = null;

private void OkCommand_Execute(object obj)

    Result = true;

如果要使用CanExecute 方法,请使用以下代码:

_okCommand = new RelayCommand<object>(OkCommand_Execute, OkCommand_CanExecute);

private bool OkCommand_CanExecute(object obj)  

【讨论】:

【参考方案4】:

这是命令参数的简单解决方案,因为我正在寻找有关该主题的帮助。我在网上找不到任何足够简单的东西。当您使用中继命令时,以下解决方案效果很好。 我有一些超链接需要获取使用命令参数单击的 url 值。

第 1 步:在您的中继命令中,创建一个简单的属性来保存参数对象的值。您可以将其命名为 parametervalue 或您喜欢的任何名称。

public object ParameterValue

  get;
  set;

第二步:在 RelayCommand 类的 Execute 方法中,将上面创建的值或属性设置为 Execute 方法的参数。

readonly Action<object> m_execute;       // Action to execute

public void Execute(object parameter)
 
   this.ParameterValue = parameter;
   m_execute(parameter);
 

第 3 步:现在您可以将 xaml 中的 CommandParameter 绑定到执行命令时要检索的任何值。 示例:

<TextBlock>
  <Hyperlink Command="Binding Path=NavigateUrlCmd"
             CommandParameter="Binding ElementName=tbwebsite, Path=Text">
    <TextBlock Name="tbwebsite" Text="Binding Path=website"/>
  </Hyperlink>
</TextBlock> 

如果你有一个名为 chickenCommand 的命令,在执行时你可以访问参数: chickenCommand.ParameterValue

我希望这对某人有所帮助。感谢您之前的所有帮助。

【讨论】:

【参考方案5】:

我只是想推销我的观点,看看这是否有效......

http://mywpf-visu.blogspot.com/2009/12/relay-command-how-to-pass-parameter.html

【讨论】:

【参考方案6】:

由于编译错误,我无法替换对 lamda 表达式的方法名称的引用。显然,并且毫不奇怪,不能使用非静态方法名称引用来代替 lambda。我几乎不认为它是“增加的复杂性”。持续传递 lamdas 对我来说很有意义。

【讨论】:

以上是关于使用 ViewModel 中定义的 RelayCommand 传递参数(来自 Josh Smith 示例)的主要内容,如果未能解决你的问题,请参考以下文章

如何在自定义View里使用ViewModel

定义 ViewModel 类的更好的 MVVM 方法是啥?

如何在ViewModel中访问View中的控件

Xamarin iOS - MVVMCross:无法使用 ViewModel 中的命令连接自定义单元格中的按钮

将自定义组件存储配置绑定到 ViewModel 存储

Prism MVVM:“没有为类型定义无参数构造函数”将 ViewModel 绑定到 View 时出错