Using Behaviors to Synchronize Selected Items of Infragistics Silverlight controls to a ViewModel

[Infragistics] Devin Rader / Wednesday, June 1, 2011

Behaviors, which were introduced with Blend 3, are a great way to package up bits of functionality that attached to existing objects using XAML.  They also are a great way to create man in the middle connections between objects in your View and properties on your ViewModel. 

In this post I’ll show you three different examples of using Behaviors to synchronize selected items between different Infragistics Silverlight controls and properties on a ViewModel. 

Note that Behaviors take advantage of the System.Windows.Interactivity assembly, which is not included in the Silverlight SDK.  If you want to create your own custom Behaviors, you can get the assembly by installing Blend.

XamComboEditor SelectedItems Behavior

Like the native Silverlight ComboBox, the XamComboEditor does not expose a SelectedItems property, but it does raise a SelectionChanged event whose arguments include the added and removed selected items.  You can use this to create a Behavior that synchronizes the controls selected items with a property on your ViewModel.

The XamComboEditorSelectedItemsBehavior attaches to the XamComboEditor and allows you to bind to its SelectedItems property.  Internally the Behavior simply listens to the XamComboEditors SelectedItemsChanged event and shuffles the selected items between itself and the bound VM property.

The sample shows how you use the behavior with the XamComboEditor:

<ig:XamComboEditor ItemsSource="{Binding Customers}"
           DisplayMemberPath="ContactName" 
           AllowMultipleSelection="True">
    <i:Interaction.Behaviors>
        <b:XamComboEditorSelectedItemsBehavior 
            SelectedItems="{Binding SelectedCustomers}" />
    </i:Interaction.Behaviors>
</ig:XamComboEditor>

The XamComboEditorSelectedItemsBehavior supports two-way binding, so changes to the SelectedCustomers collection exposed by the ViewModel will be reflected by the XamComboEditor.

XamGrid SelectedItems Behavior

The XamGrid exposes an event called SelectedRowsCollectionChanged which can be used to create a Behavior that lets you synchronize the currently selected grid rows to a property on your ViewModel. 

This sample shows how you use the XamGridSelectedItemsBehavior with the XamGrid.

<ig:XamGrid ItemsSource="{Binding Customers}">           
    <i:Interaction.Behaviors>
        <b:XamGridSelectedItemsBehavior 
            SelectedItems="{Binding SelectedCustomers}" />
    </i:Interaction.Behaviors>
    <ig:XamGrid.SelectionSettings>
        <ig:SelectionSettings 
             CellClickAction="SelectRow" 
             RowSelection="Multiple" />
    </ig:XamGrid.SelectionSettings>
</ig:XamGrid>

 

In this sample I’ve enabled row selection and attached the Behavior, who will listen for the SelectedRows collection to change and update the bound collection in my ViewModel. 

There are two limitations with sample Behavior.  First, because the XamGrid lazily loads data in order to reduce its memory footprint, the XamGridSelectedItemsBehavior included in the sample code will only support one-way binding. 

In order for the Behavior to support two-way binding, when the ViewModel collection changed, you would need to enumerate all of the rows in the XamGrid, searching for the row that matched the selected data object.  This potentially means forcing the control to load all of the data from the XamGrids ItemsSource.

Second, the Behavior is designed to work with only a single level grid. Selected rows from child bands will not be synchronized.  I’ll look at some ways you could enhance the behavior to support multi-level selections in the next section.

XamDataTree SelectedItems Behavior

The final behavior in the sample code is the XamDataTreeSelectedItemsBehavior.  This Behavior allows you to synchronize the selected nodes of the XamDataTree with a collection in your ViewModel.

Creating a selection behavior for the XamDataTree has a number of challenges.  Because the tree is inherently a multi-level structure, the behavior needs to handle selection of nodes at different levels of the tree.   Additionally, the nodes at the different tree levels can be different types.

There are several ways that you could handle this including:

  • Creating a behavior that exposes several different collections, where each one matches a level or type that can be selected in the tree
  • Expose a collection that accepts the type Object from your ViewModel so that any selected object from the tree can be added to it.
  • Expose a collection that accepts an interface from your ViewModel, and implement that interface on the data objects in your tree.

For the purposes creating my sample Behavior, I choose to use the last option.

First I created a marker interface called ISelectable and I implemented this on my data objects which are bound to the XamDataTree.  Next I exposed an ObservableCollection<ISelectable> from my ViewModel.

Below is an example of using the XamDataTreeSelectedItemsBehavior:

<ig:XamDataTree ItemsSource="{Binding Customers}" 
                DisplayMemberPath="ContactName">
    <ig:XamDataTree.NodeLayouts>
        <ig:NodeLayout Key="Orders" DisplayMemberPath="OrderID">
            <ig:NodeLayout.NodeLayouts>
                <ig:NodeLayout Key="Order_Details" 
                               DisplayMemberPath="ProductID" />
            </ig:NodeLayout.NodeLayouts>
        </ig:NodeLayout>
    </ig:XamDataTree.NodeLayouts>
    
    <i:Interaction.Behaviors>
        <b:XamDataTreeSelectedItemsBehavior 
            SelectedItems="{Binding SelectedItems}" />
    </i:Interaction.Behaviors>
</ig:XamDataTree>

My Behavior simply deals with the ISelectable interface, and its up to my ViewModel to know how to deal with the actual selected types.

Conclusion

Using Behaviors is a easy and XAML friendly way to augment the capabilities of existing controls, in this case making them more friendly for using when building applications using the MVVM pattern.

All of the code for the samples can be found here.