将事件传递给 ViewModel 的最佳方式是啥?
Posted
技术标签:
【中文标题】将事件传递给 ViewModel 的最佳方式是啥?【英文标题】:What's the best way to pass event to ViewModel?将事件传递给 ViewModel 的最佳方式是什么? 【发布时间】:2014-02-12 16:00:51 【问题描述】:案例是:我有一个控件的事件,我希望我的 ViewModel 对其做出反应。目前我正在通过执行如下示例中的不可见按钮命令来执行此操作。
在 View.xaml 中:
<Control x:Name="SearchResultGrid" ... DataRefreshed="SearchResultRefreshed" />
<Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="Binding SearchResultRefreshedCommand" />
在 View.xaml.cs 中:
private void SearchResultRefreshed(object sender, EventArgs e)
if (SearchResultRefreshedButton.Command != null)
SearchResultRefreshedButton.Command.Execute(SearchResultGrid.ResultRowCount);
这很好用,但对我来说似乎是一个 hack。我想知道是否有更好的(标准)方法来做到这一点?我找不到任何例子,这就是我自己“发明”的。
【问题讨论】:
Google 的交互触发器。 或者只是将视图的 DataContext 转换为 ViewModel 并做任何你想做的事情。ViewModel vm = this.DataContext as ViewModel;那么你可以做 vm.SomeAction @RohitVats 谢谢,我在一些例子中看到了它们的使用,但它看起来不像我需要的。现在我发现我错了。 【参考方案1】:使用 MVVM,处理事件的一般方法是简单地将它们包装在 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
对象同样容易(而且更好)。只需创建一个 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);
无论哪种方式,它都会像这样使用:
<TextBox TextBoxProperties.PreviewKeyDown="SomeKeyEventHandler" />
或者,如果您使用首选的ICommand
方法:
<TextBox TextBoxProperties.PreviewKeyDownCommand="Binding SomeCommand" />
【讨论】:
这看起来很有趣...我需要检查它是如何工作的。 这行得通,我也喜欢这个解决方案的简洁明了。我也被告知有关交互触发器的信息,但我认为这更好。谢谢! 你能展示附加事件的方式吗?我找不到如何使用附加事件在 VM 上注册事件处理程序,因为所有路由事件都不支持绑定,或者不是吗? @Uzivatel828,我的回答中有一个指向解释附加事件的页面的链接。如果这没有帮助,我建议您针对您的实际问题提出一个新问题。 @Sheridan:我知道如何实现附加事件。我不知道如何使用附加事件来处理非附加路由事件。也许我误解了你的回答。【参考方案2】:就我个人而言,我从来不需要使用附加属性来处理控件的事件。在您的示例中,想要知道“SearchResultRefreshed”何时通过隐藏控件通知 ViewModel 的控件......为什么 ViewModel 不知道结果已被刷新?
如果结果首先来自 ViewModel,并且绑定用于在您的控件中显示它们,那么搜索结果已刷新的知识应该由您的 ViewModel 驱动,而不是由您的视图驱动。
在少数情况下,我发现需要摆脱 ICommand 和数据绑定。
【讨论】:
是的,数据来自 ViewModel,但是在 3dparty WinForms 控件内部进行过滤时会引发该事件。 哦,好的。然后我会使用交互命名空间,因为它比编写新的附加属性需要更少的代码:<i:Interaction.Triggers> <i:EventTrigger EventName="DataRefreshed"> <i:InvokeCommandAction Command="Binding SearchResultRefreshedCommand, Mode=OneWay" CommandParameter="Binding Name, ElementName=SearchResultGrid"/> </i:EventTrigger> </i:Interaction.Triggers>
【参考方案3】:
你应该在你的控件中添加一个依赖属性DataRefreshed
以便绑定它
这里有一个例子你怎么做
public static readonly DependencyProperty DataRefreshedProperty = DependencyProperty.Register(
"DataRefreshed",
typeof(bool),
typeof("typeof yourcontrol here "),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnDataRefreshedChanged)
)
);
public bool DataRefreshed
get return (bool)GetValue(DataRefreshedProperty);
set SetValue(DataRefreshedProperty, value);
然后,您可以像在 ViewModel 中定义的任何其他 WPF 属性一样操作您的属性,例如 SearchResultRefreshed
<Control x:Name="SearchResultGrid" ... DataRefreshed="Binding SearchResultRefreshed" />
<Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="Binding SearchResultRefreshedCommand" />
看看下面的教程了解更多dependecyproperty and attachedproperty
【讨论】:
以上是关于将事件传递给 ViewModel 的最佳方式是啥?的主要内容,如果未能解决你的问题,请参考以下文章
可以将 ViewModel 传递给 RecyclerView(无 DataBinding)吗?
将 formGroup 传递给 mat-step 的最干净的方法是啥?