什么是将事件传递给ViewModel的最佳方式?
什么是将事件传递给ViewModel的最佳方式?
情况是这样的:我有一个控件的事件,我希望我的ViewModel对其做出反应。目前,我是通过执行一个不可见按钮的命令来实现的,就像下面的示例中那样。
在View.xaml中:
在View.xaml.cs中:
private void SearchResultRefreshed(object sender, EventArgs e)
{
if (SearchResultRefreshedButton.Command != null)
{
SearchResultRefreshedButton.Command.Execute(SearchResultGrid.ResultRowCount);
}
}
这种方法虽然有效,但在我看来有些繁琐。我想知道是否有更好(标准)的方法?我找不到任何示例,这是我自己“发明”的。
问题的出现原因:
在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
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中注册事件处理程序,因为所有的路由事件都不支持绑定,或者说它们不是附加事件吗?
在我的回答中,有一个链接指向一个解释附加事件的页面。如果那个链接没有帮助到你,我建议你提一个新的问题,具体描述你的实际问题。
我知道如何实现附加事件。我无法找到如何使用附加事件来处理非附加的路由事件。也许我对你的回答理解有误。
问题原因: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>