Silverlight Tips & Tricks: Make Silverlight DataGrid be more MVVM friendly =)

Today one guy from our dev team met some problems with binding of datagrid selected items. Because of SelectedItems is readonly property, we cannot create two way binding for it. After a short discussion we decided to create DataGridSelectedChanged command behavior which will inform our model about selected rows. (We are using microsoft p&p prism commands api).

To create new command behavior you should perform to steps:

1. Create behaviour

   1: public class DataGridSelectionChangedCommandBehaviour : CommandBehaviourBase<DataGrid>
   2: {
   3:     public DataGridSelectionChangedCommandBehaviour(DataGrid targetObject)
   4:         : base(targetObject)
   5:     {
   6:         targetObject.SelectionChanged += this.OnSelectionChanged;
   7:     }
   8:  
   9:     private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  10:     {
  11:         this.ExecuteCommand();
  12:     }
  13: }

2. Add attached property

   1: public static class SelectionChanged
   2: {
   3:     /// <summary>
   4:     /// Command paramter.
   5:     /// </summary>
   6:     public static readonly DependencyProperty CommandParameterProperty =
   7:         DependencyProperty.RegisterAttached(
   8:             "CommandParameter", typeof(object), typeof(SelectionChanged), new PropertyMetadata(OnSetCommandParameterCallback));
   9:  
  10:     /// <summary>
  11:     /// Command to execute on click event.
  12:     /// </summary>
  13:     public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
  14:         "Command", typeof(ICommand), typeof(SelectionChanged), new PropertyMetadata(OnSetCommandCallback));
  15:  
  16:     /// <summary>
  17:     ///  Command behavior.
  18:     /// </summary>
  19:     private static readonly DependencyProperty ClickCommandBehaviorProperty =
  20:         DependencyProperty.RegisterAttached(
  21:             "ClickCommandBehavior", typeof(DataGridSelectionChangedCommandBehaviour), typeof(SelectionChanged), null);
  22:  
  23:     /// <summary>
  24:     /// Gets command
  25:     /// </summary>
  26:     /// <param name="dataGrid">
  27:     /// containig dependecy property
  28:     /// </param>
  29:     /// <returns>
  30:     /// attached command
  31:     /// </returns>
  32:     public static ICommand GetCommand(DataGrid dataGrid)
  33:     {
  34:         return dataGrid.GetValue(CommandProperty) as ICommand;
  35:     }
  36:  
  37:     /// <summary>
  38:     /// Gets command parameter
  39:     /// </summary>
  40:     /// <param name="dataGrid">
  41:     /// containig dependecy property
  42:     /// </param>
  43:     /// <returns>
  44:     /// attached command
  45:     /// </returns>
  46:     public static object GetCommandParameter(DataGrid dataGrid)
  47:     {
  48:         return dataGrid.GetValue(CommandParameterProperty) as object;
  49:     }
  50:  
  51:     /// <summary>
  52:     /// Sets the <see cref="ICommand"></see> for execute
  53:     /// </summary>
  54:     /// <param name="dataGrid">
  55:     /// dependency object to attache command
  56:     /// </param>
  57:     /// <param name="command">
  58:     /// command for execute
  59:     /// </param>
  60:     public static void SetCommand(DataGrid dataGrid, ICommand command)
  61:     {
  62:         dataGrid.SetValue(CommandProperty, command);
  63:     }
  64:  
  65:     /// <summary>
  66:     /// Sets the value for the CommandParameter attached property on the provided <see cref="DataGrid"/>.
  67:     /// </summary>
  68:     /// <param name="dataGrid">
  69:     /// dependency object to attach CommandParameter
  70:     /// </param>
  71:     /// <param name="parameter">
  72:     /// Parameter value to attach
  73:     /// </param>
  74:     public static void SetCommandParameter(DataGrid dataGrid, object parameter)
  75:     {
  76:         dataGrid.SetValue(CommandParameterProperty, parameter);
  77:     }
  78:  
  79:     /// <summary>
  80:     /// Get or create command behavior
  81:     /// </summary>
  82:     /// <param name="dataGrid">
  83:     /// dependecy object to attach
  84:     /// </param>
  85:     /// <returns>
  86:     /// click behavior
  87:     /// </returns>
  88:     private static DataGridSelectionChangedCommandBehaviour GetOrCreateBehavior(DataGrid dataGrid)
  89:     {
  90:         var behavior = dataGrid.GetValue(ClickCommandBehaviorProperty) as DataGridSelectionChangedCommandBehaviour;
  91:         if (behavior == null)
  92:         {
  93:             behavior = new DataGridSelectionChangedCommandBehaviour(dataGrid);
  94:             dataGrid.SetValue(ClickCommandBehaviorProperty, behavior);
  95:         }
  96:  
  97:         return behavior;
  98:     }
  99:  
 100:     /// <summary>
 101:     /// On set command
 102:     /// </summary>
 103:     /// <param name="dependencyObject">
 104:     /// dependecy object to attach command
 105:     /// </param>
 106:     /// <param name="e">
 107:     /// event args
 108:     /// </param>
 109:     private static void OnSetCommandCallback(
 110:         DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
 111:     {
 112:         var dataGrid = dependencyObject as DataGrid;
 113:         if (dataGrid != null)
 114:         {
 115:             DataGridSelectionChangedCommandBehaviour behaviour = GetOrCreateBehavior(dataGrid);
 116:             behaviour.Command = e.NewValue as ICommand;
 117:         }
 118:     }
 119:  
 120:     /// <summary>
 121:     /// On set command parameter.
 122:     /// </summary>
 123:     /// <param name="dependencyObject">
 124:     /// dependency object to attach command
 125:     /// </param>
 126:     /// <param name="e">
 127:     /// event args
 128:     /// </param>
 129:     private static void OnSetCommandParameterCallback(
 130:         DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
 131:     {
 132:         var dataGrid = dependencyObject as DataGrid;
 133:         if (dataGrid != null)
 134:         {
 135:             DataGridSelectionChangedCommandBehaviour behavior = GetOrCreateBehavior(dataGrid);
 136:             behavior.CommandParameter = e.NewValue;
 137:         }
 138:     }
 139: }

Now we can invoke DelegateCommand on SelectedChanged event and pass SelectedItems DataGrid property using relative binding.

   1: <data:DataGrid 
   2:     ItemsSource="{Binding Items}" 
   3:     Behaviours:SelectionChanged.Command="{Binding SelectedFilesChangedCommand}" 
   4:     Behaviours:SelectionChanged.CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=SelectedItems}" 
   5:     >               
   6: </data:DataGrid>

Hope it will help you in your coding wars with silverlight controls =)))

12 Comments

Comments have been disabled for this content.