An Introduction to Behaviors, Triggers, and Actions
The following post is written by Jeff Kelly, one of the developers who worked extensively on both the behaviors runtime as well as the UI inside Blend that makes behaviors easier to use! In this post, he will provide an overview of the three components that make up what we collectively call “Behaviors” in the Expression Blend 3 Preview - Kirupa
One of the major goals of Blend 3 is to make it easier to build interactivity into your applications. In Blend 3, we’ve introduced several new concepts that make writing and reusing interactivity easier than ever before. By writing triggers, actions and behaviors, developers can provide functionality that is cleanly encapsulated and reusable by both developers and designers. Likewise, designers are empowered to add a new level of interactive functionality to their work without ever having to touch a code file.
Triggers and Actions
To anyone familiar with WPF, triggers and actions should sound familiar. Blend 3 introduces a similar model, extends support to Silverlight as well as WPF, and allows you to write your own triggers and actions - opening a whole new world of possibilities for what kinds of functionality you can create and reuse in your own applications. Let’s look at what triggers and actions are at a conceptual level, and then dive a little deeper into the basics of the API.
An action is an object that can be invoked to perform an operation. If you think that sounds pretty vague, you’re right. The scope of an action is not constrained: if you can write the code to do something, you could write an action to do the same thing. That said, actions are best written to perform operations that are largely atomic in nature. That is, actions work best when they don’t rely on external state that needs to be persisted between invocations of the action, and that don’t have any dependencies on other actions existing or running in a particular order relative to their invocation.
- Change a property
- Call a method
- Open a window
- Navigate to a page
- Set focus
Actions aren’t particularly useful on their own: they provide functionality to do something, but no way to activate that functionality. In order to invoke an action, we need a trigger. Triggers are objects that contain one or more actions and invoke those actions in response to some stimulus. One very common trigger is one that fires in response to an event (an EventTrigger). Other examples might include a trigger that fires on a timer, or a trigger that fires when an unhandled exception is thrown.
One important thing to note is that triggers and actions are generally meant to be used together arbitrarily. In other words, you should avoid writing an action that makes assumptions about the type of trigger that invokes it, or a trigger that makes assumptions about the actions that belong to it. If you find yourself needing a tight coupling between a trigger and action, you should instead consider a behavior. Speaking of which….
Whereas the concepts of triggers and actions have been previously established in an earlier incarnation in WPF, the concept of a behavior is a new one. At a glance, a behavior looks similar to an action: a self-contained unit of functionality. The main difference is that actions expect to be invoked, and when invoked, they will perform some operation. A behavior does not have the concept of invocation; instead, it acts more as an add-on to an object: optional functionality that can be attached to an object if desired. It may do certain things in response to stimulus from the environment, but there is no guarantee that the user can control what this stimulus is: it is up to the behavior author to determine what can and cannot be customized.
As an example, consider a behavior that allows me to drag the object the behavior is attached to around with the mouse. I need to listen to the mouse down, mouse move, and mouse up events on the attached object. In response to the mouse down, I’ll record the mouse position, hook up the mouse move and mouse up handlers and capture the mouse input. On mouse move, I’ll update the position of the object as well as the mouse position. On mouse up, I’ll release mouse capture and unhook my mouse move and mouse down handlers.
My first inclination might be to try and use EventTriggers for each of these events, and write a StartDragAction, MoveDragAction and StopDragAction to invoke in each case. However, it soon becomes apparent that this scenario is not well-addressed by actions: I need to store state between invocations (previous mouse position and the state of the drag), and the operation isn’t atomic. Instead, I can write a behavior that wraps the exact functionality outlined above into a reusable component.
Hopefully this post gives you an overview of what makes up a Behavior – the catch all word we use for behaviors, actions, and triggers. In my next post, I will describe the API a bit more and provide some code snippets on how to write each of the behavior components.