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

C# 什么';将事件传递给ViewModel的最佳方式是什么?,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,情况是:我有一个控件的事件,我希望我的ViewModel对其作出反应。目前,我通过执行一个不可见按钮的命令来实现这一点,如下面的示例所示 在View.xaml中: <Control x:Name="SearchResultGrid" ... DataRefreshed="SearchResultRefreshed" /> <Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="{B

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

在View.xaml中:

<Control x:Name="SearchResultGrid" ... DataRefreshed="SearchResultRefreshed" />
<Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="{Binding SearchResultRefreshedCommand}" />

这很管用,但对我来说像是一个黑客。我想知道是否有更好的(标准的)方法?我找不到任何例子,这是我自己“发明”的。

使用MVVM,处理事件的一般方法是简单地将它们包装起来,或者使用。以下是在附加属性中使用
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\u 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" />

您应该向控件添加一个依赖项属性
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}" />


请看下面的教程以了解更多内容。就我个人而言,我从未需要使用附加属性来处理控件的事件。在您的示例中,一个控件希望知道“SearchResultRefresh”何时更新,然后通过隐藏控件通知ViewModel。。。为什么ViewModel还不知道结果已经刷新了

如果结果首先来自ViewModel,并且绑定用于在您的控件中显示它们,那么搜索结果已刷新的知识应该由您的ViewModel驱动,而不是由您的视图驱动


只有在少数情况下,我发现需要脱离ICommands和数据绑定。

Google for interactivity triggers。或者将视图的DataContext强制转换为ViewModel,然后执行您想执行的操作。ViewModel vm=this.DataContext作为ViewModel;然后你可以做虚拟机。SomeAction@RohitVats谢谢,我在一些例子中看到了它们的使用,但它看起来不像我需要的。现在我明白我错了。这看起来很有趣。。。我需要检查它是如何工作的。这是如何工作的,我也喜欢这个解决方案是多么干净、简单和简单。我也被建议使用交互性触发器,但我的意见是这样更好。谢谢你能把附带的活动方式显示出来吗?我无法找到如何使用附加事件在VM上注册事件处理程序,因为所有路由事件都不支持绑定,或者不是吗?@Uzivatel828,在我的回答中有一个链接,指向解释附加事件的页面。如果这没有帮助,我建议你针对你的实际问题提出一个新问题。@Sheridan:我知道如何实现附加事件。我无法找到如何使用附加事件来处理非附加路由事件。也许我误解了你的答案。是的,数据来自ViewModel,但当过滤在其中完成时,事件由3dparty WinForms控件引发。哦,好的。然后我会使用交互性名称空间,因为它比编写新的附加属性所需的代码要少得多:
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); }
}
<Control x:Name="SearchResultGrid" ... DataRefreshed="{Binding SearchResultRefreshed}" />
<Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="{Binding SearchResultRefreshedCommand}" />