Building Expression Blend 3 behaviors for Silverlight – An example

Not all developers might know this, but Expression Blend comes with an SDK that allows developers to build triggers and behaviors for their WPF and Silverlight applications. It allows you to create various pieces of behavior that can be dragged onto the designer. These pieces of behaviors add interactivity to graphical pieces of user interface without having the need to write any C#. Pretty neat when you have designers on the team that don’t know C# but still want to add small pieces of behavior to the user interface.
In this article I’m going to show you a short sample on how you can use a custom behavior to make the Actions API of Caliburn designable in Expression blend.
Basics of a behavior
A behavior in Expression blend can be created in Visual Studio 2008 and later in Visual Studio 2010 when Microsoft Releases the next version of Silverlight and WPF. To create custom behavior for Silverlight projects you need to create a new Silverlight class library and add the following references to it:
- Microsoft.Expression.Interactions
- System.Windows.Interactivity
You can find both of these assemblies in the global assembly cache, so they should be in the list when you open up the Add reference dialog.
After you’ve added the required references to the project you will need to create a new class that derives from System.Windows.Interactivity.Behavior<T>. When you override the OnAttach and the OnDetach methods you will end up with a skeleton class that looks like the following snippet:
1: public class MessageBehavior : System.Windows.Interactivity.Behavior<FrameworkElement>
2: {
3: #region Behavior logic
4: 
5: protected override void OnAttached()
6: {
7: base.OnAttached();
8: }
9: 
10: protected override void OnDetaching()
11: {
12: base.OnDetaching();
13: }
14: 
15: #endregion
16: }
This is all that is required to get started building a behavior.
Introducing the message behavior
In Caliburn you can use the Action API to establish communication between the view and the presentation model of a view. The idea is that you can specify one or more triggers that cause messages to be send to the model. These messages translate into method calls. The message behavior will encapsulate this into a single piece of logic that you can drag onto the designer. The final result will look the same as shown in the following picture.
When the behavior is dragged on the designer it will show up in the element tree as a child of the element it’s dragged on. You can select the behavior to show it’s properties in the property editor. In case of the Message behavior it will contain two properties, namely the properties MethodName and Trigger. You can set the trigger to the name of an event that will cause the method with the name specified in the MethodName property to be invoked by the Caliburn runtime.
Dependency properties
Behaviors in Silverlight and WPF can be animated and databound just like any other control. Any properties that you want to be able to be databound or be animated should be defined as a dependency property. In the case of the MessageBehavior class there are two dependency properties. You can see them in the next table.
Property | Type |
MethodName | String |
Trigger | String |
Attaching behavior
The main task of a behavior is to attach custom behavior to the element it’s associated to. For this you can make use of the OnAttach method to attach the custom behavior and the OnDetach method to detach the custom behavior from the element. The MessageBehavior class will try to attach a trigger to the associated element. If there’s more than one instance of the MessageBehavior element attached to one element in the user interface it will merge the triggers defined by them. The following snippet shows the method that will initialize the Caliburn action logic.
1: /// <summary>
2: /// Attaches message logic to the associated element
3: /// </summary>
4: private void AttachMessageLogic()
5: {
6: // Do not attach the behavior when the trigger or the method name are empty
7: if (string.IsNullOrEmpty(Trigger) || string.IsNullOrEmpty(MethodName))
8: {
9: return;
10: }
11: 
12: RoutedMessageTriggerCollection triggers = GetTriggersCollection();
13: EventMessageTrigger trigger = new EventMessageTrigger();
14: ActionMessage message = new ActionMessage();
15: 
16: trigger.EventName = this.Trigger;
17: trigger.Message = message;
18: 
19: message.MethodName = this.MethodName;
20: 
21: triggers.Add(trigger);
22: 
23: // Attach the trigger collection to the associated object
24: if (this.AssociatedObject != null)
25: {
26: Message.SetTriggers(this.AssociatedObject, triggers);
27: }
28: }
29: 
30: /// <summary>
31: /// Tries to get the triggers collection from the associated element.
32: /// Will create a new triggers collection when there isn't one found
33: /// on the associated element.
34: /// </summary>
35: /// <returns></returns>
36: private RoutedMessageTriggerCollection GetTriggersCollection()
37: {
38: RoutedMessageTriggerCollection triggers = null;
39: 
40: if (this.AssociatedObject != null)
41: {
42: triggers = Message.GetTriggers(this.AssociatedObject);
43: }
44: 
45: if (triggers == null)
46: {
47: triggers = new RoutedMessageTriggerCollection();
48: }
49: 
50: return triggers;
51: }
The AttachMessageLogic gets called in the OnAttach method or when either the Trigger or the MethodName property changes. This ensures that the action logic is attached correctly. To prevent invalid behavior with the Caliburn action logic I’ve added an extra method that detaches the old behavior when the Trigger or the MethodName property changes. The following demonstrates it’s behavior:
1: /// <summary>
2: /// Detaches the message logic for the specified trigger
3: /// </summary>
4: /// <param name="trigger"></param>
5: private void DetachMessageLogic(string triggerName)
6: {
7: RoutedMessageTriggerCollection triggers = GetTriggersCollection();
8: var foundTrigger = triggers.OfType<EventMessageTrigger>()
9: .FirstOrDefault(trigger => trigger.EventName == triggerName);
10: 
11: if (foundTrigger != null)
12: {
13: triggers.Remove(foundTrigger);
14: }
15: }
 
What’s next
Not only allows the Blend SDK you to develop custom behaviors, it allows you to develop custom triggers. In the next blogpost I will show you what you can do with triggers. In the mean time I suggest you check out the Expression Blend gallery. There a lot of standard behaviors available that you can use in Blend.