双击WPF ListView - 如何触发命令而不使用事件处理程序

时间:2012-05-04 23:56:13

标签: wpf mvvm

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

我有一个WPF'视图' - viewCustomerSearch。它上面有一些文本框,当用户点击“搜索”时,结果将填充在ListView中。 viewCustomerSearch绑定到viewmodelCustomerSearch,它运行良好。

viewCustomerSearch托管在viewCustomer上。

我想知道viewCustomerSearch公开一个自定义命令 - CustomerSelectedCommand - 只要双击viesCustomerSearch中的ListView,然后由viewCustomer(viewmodelCustomer)后面的viewmodel处理,就会'触发'。这似乎是正确实现的理论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);
}

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

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

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

(请注意:我在应用程序的其他地方使用常见的'RelayCommand';这会在这里发挥作用吗?)

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

考虑到上述所有情况,我不得不说我个人并不赞成'MVVM意味着在视图中没有代码'。这对我来说似乎很疯狂;与视图完全相关的代码,仅视图,不应该 - 恕我直言 - 进入视图模型。尽管如此,这看起来似乎是逻辑上的东西(不是查看内容)。

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

DS

2 个答案:

答案 0 :(得分:0)

在您的视图中,您可以在xaml中添加“Command”属性并将其绑定到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,所以我不会进入那个。我开始时发现How Do I: Build Data-driven WPF Application using the MVVM pattern很有帮助。

每个注释请求命令属性示例

我只是要抓取一些正常工作的代码,这里是如何将一个Command属性添加到XAML中的按钮。

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

这假设您已将DataContext设置为具有名为ConnectCommand的Command的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);
}

RelayClass

使这个变得简单的一部分是我在我的一个核心库.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);
}
}
}

答案 1 :(得分:0)

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