WPF UIElement selection change event using behaviors (MVVM)

Introduction

 
In this post, we'll learn how to bind the Combo box selectionchange event using behaviors and routed events. I will demonstrate how to bind the combo box selection change event in WPF using MVVM using ICommand interface.
 
This ICommand will be registered with Combo box(UIElement) using behavior and will be executed when selection changes. This approach can be used for any UI Element selection change using MVVM.
 

Implementation

 
1. Have your CustomCommand : ICommand execute when Command is triggered from view.
 
The ICommand Interface is used for updating models from view using MVVM. It is defined under System.Windows.Input namespace. Let's jump to the code in order to understand the topic.
  1. public class MyCustomCommand < T >: ICommand {  
  2.  private Action < T > execute;  
  3.  private Func < T, bool > canExecute;  
  4.   
  5.  public event EventHandler CanExecuteChanged {  
  6.   add {  
  7.    CommandManager.RequerySuggested += value;  
  8.   }  
  9.   remove {  
  10.    CommandManager.RequerySuggested -= value;  
  11.   }  
  12.  }  
  13.   
  14.  public MyCustomCommand(Action < T > execute, Func < T, bool > canExecute = null) {  
  15.   this.execute = execute;  
  16.   this.canExecute = canExecute;  
  17.  }  
  18.   
  19.  public bool CanExecute(object parameter) {  
  20.   return this.canExecute == null || this.canExecute((T) parameter);  
  21.  }  
  22.   
  23.  public void Execute(object parameter) {  
  24.   this.execute((T) parameter);  
  25.  }  
  26. } 
2. Create your XAML with a Combo box and attach the behavior with that. The next step will show how to create this behavior and how selection change triggers from view to view models.
  1. <Grid>  
  2.      <Grid.RowDefinitions>  
  3.          <RowDefinition Height="*"></RowDefinition>  
  4.          <RowDefinition Height="100"></RowDefinition>  
  5.      </Grid.RowDefinitions>  
  6.      <DataGrid x:Name="McDataGrid" Margin="0" AutoGenerateColumns="False" ItemsSource="{Binding MainItems}">  
  7.          <DataGrid.Columns>  
  8.              <DataGridTextColumn Width="*" Binding="{Binding Name}" Header="Name" IsReadOnly="True"/>  
  9.              <DataGridTemplateColumn MinWidth="200" Width="Auto" Header="Departments">  
  10.                  <DataGridTemplateColumn.CellTemplate>  
  11.                      <DataTemplate>  
  12.                          <ComboBox ItemsSource="{Binding Items}"  
  13.                             SelectedItem="{Binding Department}"                                   
  14.                             local:SelectionChangedBehavior.SelectionChangedEvent="{Binding SelectionChangeCommand}">  
  15.                          </ComboBox>  
  16.                      </DataTemplate>  
  17.                  </DataGridTemplateColumn.CellTemplate>  
  18.              </DataGridTemplateColumn>  
  19.          </DataGrid.Columns>  
  20.      </DataGrid>  
  21.      <TextBlock Grid.Row="1" Text="{Binding Message}"/>  
  22.  </Grid>  
3. write new behaviour with dependency property SelectionChangedEvent with PropertyChangecallBack. 
  1. public static class SelectionChangedBehavior {  
  2.  private static readonly DependencyProperty SelectionChangedEvent =  
  3.   DependencyProperty.RegisterAttached(  
  4.    "SelectionChangedEvent",  
  5.    typeof(ICommand),  
  6.    typeof(SelectionChangedBehavior),  
  7.    new PropertyMetadata(nullnew ExecuteCommandOnRoutedEvent(Selector.SelectionChangedEvent).PropertyChangedHandler));  
  8.   
  9.  public static void SetSelectionChangedEvent(DependencyObject dependencyObject, ICommand value) {  
  10.   dependencyObject.SetValue(SelectionChangedEvent, value);  
  11.  }  
  12.   
  13.  public static ICommand GetSelectionChangedEvent(DependencyObject dependencyObject) {  
  14.   return (ICommand) dependencyObject.GetValue(SelectionChangedEvent);  
  15.  }  
  16. } 
4. Finally, implement propertyChangecallback routed event. Here, each UIElement of WPF can add and remove event handlers.
  1. public class ExecuteCommandOnRoutedEvent {  
  2.  private readonly RoutedEvent routed;  
  3.   
  4.  private DependencyProperty property;  
  5.   
  6.  public ExecuteCommandOnRoutedEvent(RoutedEvent @event) {  
  7.   routed = @event;  
  8.  }  
  9.   
  10.  private void ManageEventHandlers(DependencyObject sender, object oldValue, object newValue) {  
  11.   var element = sender as UIElement;  
  12.   
  13.   if (element == null) {  
  14.    return;  
  15.   }  
  16.   
  17.   if (oldValue != null) {  
  18.    element.RemoveHandler(routed, new RoutedEventHandler(CommandEventHandler));  
  19.   }  
  20.   
  21.   if (newValue != null) {  
  22.    element.AddHandler(routed, new RoutedEventHandler(CommandEventHandler));  
  23.   }  
  24.  }  
  25.   
  26.  private void CommandEventHandler(object sender, RoutedEventArgs e) {  
  27.   var dp = sender as DependencyObject;  
  28.   if (dp == null) {  
  29.    return;  
  30.   }  
  31.   
  32.   var command = dp.GetValue(property) as ICommand;  
  33.   
  34.   if (command == null) {  
  35.    return;  
  36.   }  
  37.   
  38.   if (command.CanExecute(e)) {  
  39.    command.Execute(e);  
  40.   }  
  41.  }  
  42.   
  43.  public void PropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e) {  
  44.   property = e.Property;  
  45.   var oldValue = e.OldValue;  
  46.   var newValue = e.NewValue;  
  47.   ManageEventHandlers(sender, oldValue, newValue);  
  48.  }  
  49. } 
The main view model and child view models all have properties bound to MainView, you can refer to the code to get the full working solution.

Conclusion 

Here, we learned how to write behaviors and how to add routed events to change View to viewmodel using behaviors.