Expression Blend 3: More on Actions, Triggers, Behaviors

I figured that it was time for me to try and fathom Behaviors in Expression Blend 3.

I’d seen quite a bit about them, I’ve seen them used but I hadn’t sat down and tried to do anything with them myself.

First off, I read some very good backgrounders over here;

Blend 3 Behaviors- Interactivity Without Code

Blend 3- Triggers, Actions, Behaviors

Blend 3 Behaviors- A Sample Action

and then I followed up with some bits over here which are equally good;

A Quick Walkthrough

Writing Your Own Triggers

Looking at Triggers and Actions

and, in terms of trying to write something original around these topics, I’m not sure that there’s much that I can add but I did want to write something down as it forces me to actually walk through it and think about it properly.

Prior to Blend 3 Preview, both WPF and Silverlight have the idea of;

  • Trigger – something that flags when an interesting thing happens like a property hitting some value or an event being fired
  • Action – something to do like running a Storyboard, displaying a MessageBox

and there are framework classes like TriggerBase, Trigger, EventTrigger, DataTrigger, MultiTrigger, MultiDataTrigger and then TriggerAction, [Begin/Pause/Resume/etc]Storyboard and SoundPlayerAction which bring these things into life. So, in WPF I can do something along the lines of;

<Grid> 
  <Grid.Resources>
    <Storyboard x:Key="sbSpin">
      <DoubleAnimation  Storyboard.TargetName="rotation"
                        Storyboard.TargetProperty="Angle"
                        From="0"
                        To="360"
                        Duration="00:00:01"/>
    </Storyboard>
  </Grid.Resources>
  <Button Content="Click Me" x:Name="myButton">
    <Button.Triggers>
      <EventTrigger RoutedEvent="Button.Click">
        <EventTrigger.Actions>
          <BeginStoryboard Storyboard="{StaticResource sbSpin}"/>
        </EventTrigger.Actions>
      </EventTrigger>
    </Button.Triggers>
    <Button.LayoutTransform>
      <RotateTransform x:Name="rotation" Angle="0"/>
    </Button.LayoutTransform>
  </Button>
</Grid>

and that all works quite nicely. There is some support for this in Silverlight but it’s not nearly as rich and so Expression Blend 3 comes along with its own definition of Trigger and Action that spans both WPF and Silverlight ( in a library named Microsoft.Expression.Interactivity.dll ) and also goes beyond what was present in WPF and Silverlight by adding Behavior as well.

For me, actions are the easier thing to understand in that they just “do something” and the canonical example seems to be the sort of action that displays a MessageBox.

Looking at actions, in the new library there looks to be;

image

and so IAttachedObject becomes an object that attaches/detaches itself from some other object and then TriggerAction is one of those that also has an enabled/disabled flag, an Invoke method and an OnAttaching/OnDetaching pair of methods – to some extent this reminds me of a Workflow Activity – i.e. an object that’s part of a bigger thing and knows how to do “something” as represented by the Invoke method.

Beyond that, there’s TriggerAction<T> with T being used to constrain the AssociatedObject to be a specific type and then 2 specific actions for Storyboard and InvokeCommand ( that sounds like something to push on the stack and take a look at in the future ) and then finally TargetedTriggerAction which looks to represent an action that you want to perform on somebody else – i.e. a property to specify which object it is that’s the target of the action and then the generic version which looks to restrict the type of the target around the generic parameter T.

That seems easy enough. I reckon that I could write a vanilla action. I thought I’d try and write one that made a target blur as in;

  public class BlurAction : TargetedTriggerAction<UIElement>
  {
    public Duration BlurDuration { get; set; }

    public int BlurRadius { get; set; }

    public BlurAction()
    {
      effect = new BlurEffect();
      effect.Radius = 0;
    }
    protected override void Invoke(object parameter)
    {
      Storyboard sb = new Storyboard()
      {
        AutoReverse = true,
        FillBehavior = FillBehavior.Stop
      };
      DoubleAnimation da = new DoubleAnimation()
      {
        To = BlurRadius,
        Duration = BlurDuration 
      };
      Storyboard.SetTarget(da, effect);
      Storyboard.SetTargetProperty(da, new PropertyPath("Radius"));
      sb.Children.Add(da);
      sb.Begin();
    }
    protected override void OnTargetChanged(UIElement oldTarget, UIElement newTarget)
    {
      base.OnTargetChanged(oldTarget, newTarget);

      if (oldTarget != null)
      {
        oldTarget.Effect = null;
      }
      newTarget.Effect = effect;
    }
    BlurEffect effect;
  }

now, some of those properties should perhaps be dependency properties and the action creates quite a few Storyboards and I’m not sure this is a very useful action but nonetheless it allows me to wander into Blend, draw a couple of Buttons and then attach this action to the Click event of one button and set the Target of the action to be the other button. More visually;

image image image image

and it does what you’d expect.

So I feel relatively happy about what an action might be. What’s a trigger? In the Framework, there look to be different kinds of Trigger – I tried to capture them all with this class diagram so I hope I didn’t miss any;

image

and what I take from that is that a Trigger is anything that invokes a bunch of actions at a point when it decides to do that. So, I guess that I might write a TimerTrigger that invokes a bunch of actions every so often with something like;

  public class TimerTrigger : TriggerBase<DependencyObject>
  {
    public TimeSpan Interval { get; set; }

    protected override void OnAttached()
    {
      timer = new DispatcherTimer();
      timer.Interval = Interval;
      timer.Tick += OnTick;
      timer.Start();
    }
    void OnTick(object sender, EventArgs e)
    {
      this.InvokeActions(null);
    }
    protected override void OnDetaching()
    {
      timer.Tick -= OnTick;
      timer = null;
    }
    DispatcherTimer timer;
  }

Note – I’m not at all sure that the way I’m using the Timer there is appropriate in terms of throwing it away in OnDetaching and re-creating it in OnAttaching but, as a quick sketch, it works out ok. From within Blend I can then go and wire it up;

image image image

and I have a BlurAction that runs every 10 seconds.

Ok, so I think I “get” actions and triggers at least at a high level and where they show up in Blend but what’s a Behavior? Going back to my trusty class diagram it looks like we have;

image

this leaves Behavior feeling a bit opaque to me. It’s an attached object but what does it do? It doesn’t seem to have any methods or properties that you could easily spot but it’s not a Trigger and it’s not an Action.

I thought I’d browse the gallery of behaviors up here;

http://gallery.expression.microsoft.com/site/items/behaviors

at the time of writing there are 11 samples up there so I took down the “Sample Silverlight 3 Behaviors”. In that package there’s source and binaries for;

  • FullScreenAction (Action)
  • GoToStateAction (Action – drives a control via VSM to a particular state)
  • HyperlinkAction (Action)
  • PlayStoryboardAction (Action)
  • RemoveElementAction (Action – removes a child element from a parent )
  • ShowMessageBoxAction (Action)
  • StopStoryboardAction (Action)
  • DragBehavior (Behavior!)
  • DeepZoomBehavior (Behavior)
  • RandomMovementBehavior (Behavior)

one of the things about this is that I think it’s easy as a developer to think “FullScreenAction? That’s crazy – it’s only a line of code!”.

BUT…from a designer’s perspective it’s a line of code that they don’t want to write, they just expect to drag-and-drop it in Blend and the power of all this stuff is how easy is is for developers to encapsulate actions this way and get massive re-use from them reducing the amount of code that’ll actually end up in the Silverlight application itself. I think it’s very powerful.

Anyway…there are a couple of behaviours in that assembly and the one that I liked the look of most was DragBehavior so I looked at the code for it and it’s pretty simple;

  • In the OnAttached override, it finds the AssociatedObject and hooks up to its MouseLeftButtonDown, MouseLeftButtonUp, MouseMove events. It also captures the root visual of the application as a UserControl.
  • In its mouse handlers, it captures or releases the mouse to the element we’re attached to and watches for dragging.
  • When it detects dragging, it works out relative positions to the root visual of the application and modifies the RenderTransform of the element.

so, the way I read this is that a Behavior like this one is just anything that you might conceive of doing with a UI element. There’s no pre-canned idea as to what a Behavior should or should not be, it can do whatever it likes.

There’s also a DeepZoomBehavior in that package which works with a MultiScaleImage to add mouse behaviours which is pretty cool and is a great example of how that piece of code can be encapsulated once and then re-used as I’ve written similar code myself in the past and also seen others write it.

It all seems pretty cool to me and I can think of a bunch of actions that you might write straight away like actions to play/pause/resume media and actions to enable/disable UI ( I guess that’s just a special case of the “go to state” action! ) and maybe some triggers that fire on changes to network status or that kind of thing.

One thing I was wondering was how far you could take this – i.e. to what extent you could bind these things together?

One immediate thought was whether you could combine a “drag behaviour” with a “drop behavior” and how that might work? Questions;

  • would that need a Draggable behaviour on some elements
  • maybe a Drop trigger on some other elements that fired a drop action of some sort?
  • As you were dragging, would the Draggable behaviour need to be looking to see if the element that you were trying to drop onto had Drop triggers on them or would you have some other registration system that stored which elements were drop-zones for various types of draggable items?

I guess I’m thinking of 2 Listboxes displaying templated Customer items and the ability to drag the Customer instance (rather than its UI) from one to the other all built with behaviours. I’’ll try to return to this and see if I can cook something up on it.