c# viewmodel使用 将事件传递给ViewModel的最佳方式是什么?




viewmodel livedata (3)

情况是:我有一个控件的事件,我希望我的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);
    }
}

这工作很好,但是对我来说看起来像是黑客。 我想知道是否有更好(标准)的方式来做到这一点? 我找不到任何例子,这就是我自己“发明”的东西。

https://code.i-harness.com


就我个人而言,我从来没有必要使用附加属性来处理控件的事件。 在你的例子中,一个控制想知道什么时候“SearchResultRefreshed”,然后通过隐藏的控制通知ViewModel ...为什么ViewModel不知道结果已经刷新?

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

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


您应该添加一个依赖项属性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); }
}

然后你可以像任何其他WPF属性一样操作你的属性,例如在ViewModel中定义的SearchResultRefreshed

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

看看下面的教程来了解更多的dependecyproperty and attachedproperty


使用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_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}" />




mvvm