The Silverlight Data Trigger May Be The Answer To All Your View Model MVVM Issues

I have found that you either love View Model (MVVM) or you hate it. if you hate it, I know why... USING CODE BEHIND IS EASIER. :) The primary thing that code behind allows you to do is, A) Do one thing, then B) Do another. The problem with View Model style programming is that while bindings are great for most situations, many have found it frustrating and sometimes seemingly impossible to implement certain functionality with View Model that would normally be easy to do using code behind.

In this article, I hope to show you that the key tool that should make most of your View Model (MVVM) issues go away is the SilverlightDataTrigger.

With code behind, this would not be a problem. When the user clicks the Button, the code selects the Categories, then the code expands the Tree Nodes. The problem with View Model is that even if the Button were set to trigger setting the Categories,and triggering the Behavior to select the Tree Nodes, the selected Categories may not set before the Behavior runs, so the Behavior will not expand the Tree Nodes. Grrrrrr!

The Solution

The solution is to execute the BehaviorONLY AFTER the Categories have been selected.

This is done by setting a Boolean property in the View Model AFTER the Categories have been selected. A Silverlight Expression Blend DataTrigger (If you don't have Expression Blend, you can download the SDK at this link), is used to bind the Behavior to the property in the View Model, and will trigger the Behavior and expand the Tree Nodes.

The key place that this solution addresses your "normal code behind way of doing things", is that when the SetCategoryCommand is triggered by the Button, you can "run all the code you want" before you set the ExpandSelectedTreeNodes property.

In normal code behind, this is all you can do anyway; run code and then set (or not set) a value on the UI (the View in this case). The thing that normally drives people crazy about View Model (MVVM) is that they feel they lose the:

Click a Button

Run code

Change the UI

The end

Now you can:

Click a Button

Run code

Change property in View Model

Behavior (bound to the property in the View Model) changes the UI

The end

The Example Application

In the example application, we have two Behaviors. One that expands the Tree Control (TreeExpandBehavior) and one that collapses it (TreeCollapseBehavior).

In the properties for the Behaviors, we click the NewButton to change the TriggerType to DataTrigger, and we set the Binding to the ExpandSelectedTreeNodes property in the View Model. For the TreeExpandBehavior, we set the Comparison to True, and for the TreeCollapseBehavior, we set the Comparison to False.

Note: If the window does not pop up to allow you to change the TriggerType, you can drop a ChangePropertyAction Behavior on the page and then remove it. This appears to add references needed to the project to make Expression Blend pop up the TriggerType selection box when you click the New Button.

The Code

When the Set Nodes Button is clicked, it calls the SetCategory method in the View Model:

public ICommand SetCategoryCommand { get; set; }
publicvoid SetCategory(object param)
{
// Set Nodes as Selected
foreach (var Cat in colCategory)
{
// Find the Node
var result = from objCategory in Cat.AllChildren()
where
(objCategory.CategoryName == "Category Sub2-1" ||
objCategory.CategoryName == "Category Two Sub2")
select objCategory;
foreach (var item in result)
{
// Set the IsSelected property so the checkbox
// will be checked
item.IsSelected = true;
}
// Change the value of ExpandSelectedTreeNodes to True
ExpandSelectedTreeNodes = true;
// This forces anything bound to is, to be notified
// That the value has been updated so that
// Any Behavior bound to it is fired
this.NotifyPropertyChanged("ExpandSelectedTreeNodes");
}
}

The ExpandSelectedTreeNodes property is not set until AFTER the method has set the Categories.

Yes that's it. :)

DataTrigger + Behaviors = No Code Behind

If you are told of the benefits of using View Model and bindings, you expect that you should be able to use them for everything. If bindings are 'good', then you should be able to use them 100% of the time.

Hopefully we have demonstrated that the Silverlight DataTrigger allows you to "raise an event" that a Behavior can "Detect" on the UI (the View). The View Model is still fully decoupled from the View (it doesn't even know if 'anyone is listening').

Behaviors are easy to write, in many cases you will find you have less code than if you used code behind. Furthermore Behaviors are easily reused and can easily be consumed by non-programmers, for example Designers.

It is quite common for TreeView controls ( COM and .Net ) to load nodes at certain level ( siblings) let's say via WCF with dummy nodes as children ( if there are any ) then in OnExpand event ( code behind ) delete dummy node and add children/siblings. Could it be addressed with MVVM ?

To be honest with my little knowledge it is not exactly clear how to adjust your article for my needs.Already mentioned WPF example includes "Load on Demand" Simplifying the WPF TreeView by Using the ViewModel Pattern[^]and shows how it can be done in WPF. Ideally I'd like to see as much as possible close variant of that in Silverlight. As for using your article as a foundation - I don't want any checkboxes, I don't want expanded node to look selected. I am not sure whether your first article will suffice or should I use second as a base. So I just attach a behavior and do deletion of dummy and adding chidren/siblings there and I am done ? Perhaps, you may consider writing the third article as load on demand starting from root level one of the most common usage of treeview when it is populated from database and quite large , but I didn't see anything yet for Silverlight as far as MVVM is concerned.

It should be possible to make a behavior that overrides the normal tab behavior and moves more efficiently. I think the problem is that the control is virtualized (it loads data only when needed - when the node needs to show).

As you can see my behavior is able to walk the Tree and expand nodes quite quickly, so simply determining what node to select next when the tab key is hit (and the Tree has focus) should not be a problem.

Thanks for the vote! However, DataTriggers are the "Star" in this case

Even that Silverlight Advanced View Model Style Video Player http://www.codeproject.com/KB/silverlight/AdvancedMVVMVideo.aspx that I did 6 months ago could have been done using DataTriggers (rather than pass an instance of the MediaElement to the View Model like I am doing now).