DataTrigger, Bindings on non-FrameworkElements, TypeConverters, DataStateBehavior & DataStateSwitchBehavior
One of the behaviors that we've been interested in for quite a while is a DataTrigger which works nicely in Silverlight. If you aren't familiar with WPF, the DataTrigger is used to trigger state changes based on something in your view model rather than in the UI. Normal triggers are great for representing UI state such as 'when the mouse is over this element', but data triggers capture state of the view model such as 'when MyDataObject.UserState == UserState.Online'. It's great for triggering UI states based on changes in your data.
There are a couple of features which aren't in Silverlight yet which made creating a Silverlight DataTrigger interesting, but with a bit of trickery I was able to get a passable version of one working.
I was very interested in preserving the simple XAML syntax of WPF's DataTriggers-
<id:DataTrigger Binding="{Binding Value}" Value="True">
...
</id:DataTrigger>
Bindings
The first issue issue is that Silverlight doesn't currently allow databinding on types which are not FrameworkElements. Since none of the Behaviors system derives from FrameworkElement it's not as simple as exposing a DependencyProperty and setting a binding on it.
One trick to adding bindings on behaviors is to expose a property of type Binding on the behavior- you can set a property of type binding, but you can't actually bind to something. To then actually bind this property I declared a dummy attached dependency property and set the binding on the attached DP on the FrameworkElement to which the behavior is attached to. Just listen to when the attached DP changes and presto! you have a functional binding :). It's not the most obvious solution, but I think it's by far the most elegant and functional that I've found. It actually works out quite well and works very well within Blend too.
In the source code which is attached, most of the heavy work here is encapsulated in the BindingListener helper class, you can see it being used in the DataTrigger class.
TypeConverters
Once you have the binding working you also need to get the type of the value to match the type of property that is being bound to. The reason for this is that in XAML the type is just represented as a string and you don't know what type to convert it to until the binding is actually evaluated.
To do this we use type converters when they're available, but for a number of types they are not and in this case we use a little hack which we also use in some places in Blend- serialize the property to XAML and pass that through Silverlight's parser :)
In the source code which is attached, this is encapsulated in the ConverterHelper class.
DataStateBehavior
A DataTrigger in Silverlight is nice, but a really common use of it is combined with a GoToState action to control states. This works fine, but the syntax gets a bit cumbersome since you'd need triggers for both states- the true state and the false state. To make this a bit easier, I made a simple DataStateBehavior which encapsulates the trigger and two actions into a single behavior.
In the scenario where I have a data source 'User' with a property 'IsOnline', I want to have two states in my UI- 'Online' and 'Offline'. When the user is online the 'Online' state should be active, when the user of offline the 'Offline' state should be active.
To do this, it's just:
<i:Interaction.Behaviors>
<id:DataStateBehavior Binding='{Binding IsOnline}' Value='True' TrueState='Online' FalseState='Offline'/>
</i:Interaction.Behaviors>
DataStateSwitchBehavior
Of course not all states are quite black and white, for the case of representing a cascade of states to represent something such as the values of an enum, I put together DataStateSwitchBehavior which allow an arbitrary number of values to check against and picks the first valid one.
An example of this is if you have the enum in your view model:
public enum UserSpeed {
Slow,
Medium,
Fast,
}
Then you can set up states for each of these enum values using the DataStateSwitchBehavior which allows specifying a state for each of the enum values:
<id:DataStateSwitchBehavior Binding='{Binding Speed}'>
<id:DataStateSwitchCase Value='Slow' State='SlowState'/>
<id:DataStateSwitchCase Value='Medium' State='MediumState'/>
<id:DataStateSwitchCase Value='Fast' State='FastState'/>
</id:DataStateSwitchBehavior>
Source
Source code and a real basic sample for all of this can be found here.