Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to pass event to ViewModel?

Tags:

c#

mvvm

wpf

xaml

The case is: I have a control's event that I want my ViewModel to react on. Currently I'm doing this by executing a command of invisible button like in the example below.

In View.xaml:

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

In View.xaml.cs:

private void SearchResultRefreshed(object sender, EventArgs e)
{
    if (SearchResultRefreshedButton.Command != null)
    {
        SearchResultRefreshedButton.Command.Execute(SearchResultGrid.ResultRowCount);
    }
}

This works good, but it looks like a hack to me. I'm wondering if there is better (standard) way of doing this? I could not find any examples and this is what I "invented" myself.

like image 653
Kurtevich Avatar asked Jan 22 '14 14:01

Kurtevich


1 Answers

Using MVVM, the general way to handle events is to simply wrap them in Attached Properties, or use Attached Events. Here is an example using the PreviewKeyDown event in an Attached Property:

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);
}

Note that it is just as easy (and better too) to use an ICommand instead of the actual KeyEventArgs object which shouldn't really be in the view model. Just create an Attached Property of type ICommand and call that from this TextBox_PreviewKeyDown handler instead:

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);
}

Either way, it would be used something like this:

<TextBox TextBoxProperties.PreviewKeyDown="SomeKeyEventHandler" />

Or if you used the preferred ICommand method:

<TextBox TextBoxProperties.PreviewKeyDownCommand="{Binding SomeCommand}" />
like image 198
Sheridan Avatar answered Sep 30 '22 20:09

Sheridan