1

我对如何在我的特定情况下将事件实现为命令感到有些困惑。我想尊重 MVVM,但在这种情况下不知道怎么做。

我有一个 WPF '视图' - viewCustomerSearch。这上面有一些文本框,当用户单击“搜索”时,结果会填充到 ListView 中。viewCustomerSearch 绑定到 viewmodelCustomerSearch,效果很好。

viewCustomerSearch 托管在 viewCustomer 上。

我想知道 viewCustomerSearch 公开一个自定义命令 - CustomerSelectedCommand - 每当双击 viesCustomerSearch 中的 ListView 时“触发”,然后由 viewCustomer 后面的视图模型(即 viewmodelCustomer)处理。这似乎正确实现了理论上的 MVVM 模式。

我将主要问题分解为三个较小的问题,但希望您能看到它们都是同一个挑战的组成部分。

第一个问题:为了让 viewCustomerSearch 公开一个自定义命令,我似乎必须将此代码放入 viewCustomerSearch - 这似乎“破坏”了 MVVM(后面的视图代码中没有代码)。

public readonly DependencyProperty CustomerSelectedCommandProperty = DependencyProperty.Register("CustomerSelectedCommand", typeof(ICommand), typeof(viewCustomerSearch));

public ICommand CustomerSelectedCommand
{
    get { return (ICommand)GetValue(CustomerSelectedCommandProperty); }
    set { SetValue(CustomerSelectedCommandProperty, value); }
}

第二个问题(这是真正困扰我的问题):最好通过展示我会做什么来破坏 MVVM 来解释。我会在视图中有一个事件处理程序:

private void lstResults_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    if (CustomerSelectedCommand != null) CustomerSelectedCommand.Execute(((ViewModels.viewmodelCustomerSearchResult)this.lstResults.SelectedItem).CustomerId);
}

嗯......我知道你不应该把这个事件处理程序放在这里;相反,它应该有一个命令来在 viewmodelCustomerSearch 中处理它。这里的两个问题是

  • 因为'CustomerSelectedCommand' ICommand 是在 viewCustomerSearch 中实现的,所以 viewmodelCustomerSearch 看不到它来触发它。

  • 我看不到如何将 MouseDoubleClick 事件绑定到命令,而不是后面的视图代码中的事件处理程序。我正在阅读有关附加属性的信息,但看不到如何在此处应用它们。

(请注意:我在应用程序的其他地方使用通用的“RelayCommand”;这在这里起作用吗??)

第三个问题:当我在事件处理程序后面的代码中使用非 MVVM 方式触发命令时,您可以看到我将 Selected Customer Id 作为参数传递给命令。如何在 viewCustomer 的命令处理程序中看到该参数?我创建了一个新的 RelayCommand 来处理它,但似乎 Execute 方法不接受参数?

鉴于以上所有情况,我不得不说我个人并不赞同“MVVM 意味着视图中没有代码”。这对我来说似乎很疯狂;完全与视图有关的代码,并且仅与视图有关,恕我直言,不应进入视图模型。尽管如此,这看起来确实像逻辑上的东西(不是视图的东西)。

非常感谢您的一些见解。对不起,很长的帖子;试图平衡足够的信息来帮助我处理“战争与和平”。

DS

4

2 回答 2

0

为什么不将其命名为“DoubleClickCommand”,这样您就不会将业务逻辑置于您的控制之中。然后将此命令绑定到您的视图模型,就像 Tod 解释的那样。关于您背后的代码,有一个纯 xaml 解决方案,更准确地说,它涉及附加行为,但不需要覆盖 WPF 类(我想避免),例如搜索“事件触发命令。最后一件事:Code Behind 不会以任何方式破坏 MVVM,我想知道这个神话是从哪里来的。后面的代码非常好!MVVM 是将视图和逻辑分开,而不是告诉您将代码放在哪里。设计原则应该帮助而不是阻碍你。

于 2012-05-05T00:11:49.510 回答
0

在您的视图中,您可以在 xaml 中添加“命令”属性并将其绑定到 ViewModel 的命令

Command="{Binding CustomerSelectedCommand}"

参数可以通过多种方式传递。大多数时候,我只是将其他项目绑定到我的 ViewModel,我可以直接使用它们。但是,还有一个名为 CommandParameter 的属性,这是在 XAML 中指定它的示例。

 CommandParameter="{Binding ElementName=txtPassword}"

然后在我的 ViewModel 中,我的 Command 的定义如下所示

private void UserLogonCommandExecute(object parameter)
{
...
       var password_box = parameter as PasswordBox;
...
}

听起来您已经知道如何在 ViewModel 中设置 RelayCommand,所以我不会深入讨论。我发现我如何:使用 MVVM 模式构建数据驱动的 WPF 应用程序在我开始时很有帮助。

Per Comment Request 命令属性示例

我只是要获取一些工作代码,这是在 XAML 中将 Command 属性添加到按钮的方法。

<Button Command="{Binding ConnectCommand}">
//Your button content and closing </Button> here

这假设您已将 DataContext 设置为具有名为 ConnectCommand 的命令的 ViewModel。这是 ConnectCommand 的示例。您需要将 ConnectCommandCanExecute 和 ConnectCommandExecute 的内容替换为您想要完成的任何工作。

public ICommand ConnectCommand
{
    get
    {
        if (_connectCommand == null)
        {
            _connectCommand = new RelayCommand(param => ConnectCommandExecute(),
                                               param => ConnectCommandCanExecute);
        }
        return _connectCommand;
    }
}

private bool ConnectCommandCanExecute
{
    get { return !_instrumentModel.IsConnected; }
}

private void ConnectCommandExecute()
{
    if (TcpSettingsChanged()) SaveTcpSettings();
    _instrumentModel.Connect(_tcpData);
}

中继类

使这个简单的一部分是我在我的一个核心库 .dll 中拥有的 RelayClass。我可能从我看过的一个视频中得到了这个。这可以完整地剪切和粘贴,这里不需要自定义任何内容,除非您可能想要更改它所在的命名空间。

using System;
using System.Diagnostics;
using System.Windows.Input;

namespace Syncor.MvvmLib
{
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;

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

public RelayCommand(Action<object> execute)
  : this(execute, (Predicate<object>) null)
{
  this._execute = execute;
}

public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
  if (execute == null)
    throw new ArgumentNullException("execute");
  this._execute = execute;
  this._canExecute = canExecute;
}

[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
  if (this._canExecute != null)
    return this._canExecute(parameter);
  else
    return true;
}

public void Execute(object parameter)
{
  this._execute(parameter);
}
}
}
于 2012-05-05T00:03:52.717 回答