什么是将事件传递给ViewModel的最佳方式?

18 浏览
0 Comments

什么是将事件传递给ViewModel的最佳方式?

情况是这样的:我有一个控件的事件,我希望我的ViewModel对其做出反应。目前,我是通过执行一个不可见按钮的命令来实现的,就像下面的示例中那样。

在View.xaml中:

0
0 Comments

问题的出现原因:

在ViewModel中传递事件的最佳方法是什么?

解决方法:

为控件添加一个依赖属性"DataRefreshed",以便在其上进行绑定。以下是一个示例,展示如何实现:

public static readonly DependencyProperty DataRefreshedProperty = DependencyProperty.Register(
  "DataRefreshed",
  typeof(bool),
  typeof("yourcontrol的类型"),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender, 
      new PropertyChangedCallback(OnDataRefreshedChanged)
  )
);
public bool DataRefreshed
{
  get { return (bool)GetValue(DataRefreshedProperty); }
  set { SetValue(DataRefreshedProperty, value); }
}

然后,您可以像操作其他WPF属性一样操作该属性,例如ViewModel中定义的"SearchResultRefreshed"属性:


请参考以下教程以了解更多信息:dependecyproperty和attachedproperty

0
0 Comments

MVVM模式中传递事件给ViewModel的最佳方法是使用附加属性(Attached Properties)或者附加事件(Attached Events)。下面给出了一个使用附加属性来处理PreviewKeyDown事件的示例代码:

public static DependencyProperty PreviewKeyDownProperty = DependencyProperty.RegisterAttached("PreviewKeyDown", typeof(KeyEventHandler), typeof(TextBoxProperties), new UIPropertyMetadata(null, OnPreviewKeyDownChanged));
public static KeyEventHandler GetPreviewKeyDown(DependencyObject dependencyObject)
{
    return (KeyEventHandler)dependencyObject.GetValue(PreviewKeyDownProperty);
}
public static void SetPreviewKeyDown(DependencyObject dependencyObject, KeyEventHandler value)
{
    dependencyObject.SetValue(PreviewKeyDownProperty, value);
}
public static void OnPreviewKeyDownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
    TextBox textBox = dependencyObject as TextBox;
    if (e.OldValue == null && e.NewValue != null) textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
    else if (e.OldValue != null && e.NewValue == null) textBox.PreviewKeyDown -= TextBox_PreviewKeyDown;
}
private static void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    TextBox textBox = sender as TextBox;
    KeyEventHandler eventHandler = GetPreviewKeyDown(textBox);
    if (eventHandler != null) eventHandler(sender, e);
}

需要注意的是,使用ICommand代替KeyEventArgs对象更加方便和优雅,因为KeyEventArgs对象本不应该出现在ViewModel中。可以创建一个类型为ICommand的附加属性,并在TextBox_PreviewKeyDown处理函数中调用这个属性:

private static void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    TextBox textBox = sender as TextBox;
    ICommand command = PreviewKeyDownCommand(textBox);
    if (command != null && command.CanExecute(textBox)) command.Execute(textBox);
}

不管哪种方式,都可以这样使用:


或者使用更推荐的ICommand方式:


这看起来很有意思...我需要查看它的工作原理。

这个方法有效,而且我也喜欢这个解决方案的简洁和简单。我之前也听说过交互触发器,但是我认为这个方法更好。谢谢!

你能展示一下附加事件的方式吗?我无法找到如何使用附加事件来在ViewModel中注册事件处理程序,因为所有的路由事件都不支持绑定,或者说它们不是附加事件吗?

在我的回答中,有一个链接指向一个解释附加事件的页面。如果那个链接没有帮助到你,我建议你提一个新的问题,具体描述你的实际问题。

我知道如何实现附加事件。我无法找到如何使用附加事件来处理非附加的路由事件。也许我对你的回答理解有误。

0
0 Comments

问题原因:ViewModel需要接收来自控件的事件,但是控件的事件无法直接传递给ViewModel。

解决方法:使用Interactivity命名空间来实现事件传递。

在这个例子中,ViewModel需要知道搜索结果是否已经刷新。虽然数据是从ViewModel来的,但是刷新事件是由第三方WinForms控件引发的。为了将事件传递给ViewModel,可以使用Interactivity命名空间中的Interactions和Triggers来监听事件,并通过绑定的方式调用ViewModel中的相关命令。

使用Interactivity命名空间,可以在XAML中添加如下代码来实现事件传递:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="DataRefreshed">
        <i:InvokeCommandAction Command="{Binding SearchResultRefreshedCommand, Mode=OneWay}" CommandParameter="{Binding Name, ElementName=SearchResultGrid}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

0