Showing posts with label Behavior. Show all posts
Showing posts with label Behavior. Show all posts

Monday, September 14, 2009

[2009.09.14] WPF Actions and Behaviors: A closer look

There are two assemblies that house the classes necessary for extending interactivity in WPF. They are:

  • System.Windows.Interactivity.dll
  • Microsoft.Expression.Interactions.dll

I am using Windows Server 2008 Enterprise and for my setup, these assemblies are found in:
C:\Program Files (x86)\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries. In the Libraries folder, you will find two other folders: WPF and Silverlight and each has two assemblies listed above to be used for the particular project type. I mentioned these files now because you need to do an "Add Reference" to them in order to create your own behaviors, actions or triggers.

In the System.Windows.Interactivity.dll, you will find a set of classes which you will inherit from depending on what you are trying to do. Here are some of the classes you can inherit from if you want to create:

  1. Actions + Behavior: (with an associated trigger)
    TriggerAction,
    TriggerAction<T>,
    TargetedTriggerAction,
    TargetedTriggerAction<T>
  2. Behavior: (without an associated trigger)
    Behavior,
    Behavior<T>
  3. Triggers:
    TriggerBase,
    TriggerBase<T>

All of these classes share a common property in that they all inherit from Animatable. As such, they exhibit a similar behavioral pattern as related to each other.

The above list is a bit confusing when you first look at it but basically you can create a Behavior by inheriting from the exact same classes as you would use when creating an Action. The things that really separates them (and sometimes doesn't) boils down to which of the virtual methods defined in the base class you override and implement.

To create just an Action, you derive from all the classes listed in 1 above but you need to only override the Invoke() method. Here is where you write code to do what you want when the Action is invoked.

To create a Behavior that uses Triggers, you can derive from classes both in 1 and 2 but instead you override the OnAttached() and OnDetaching() methods. However, depending on what your behavior entails, you may choose to override Invoke() as well. For a Behavior without any Triggers, derive from classes in 2 and override the methods mentioned above. Of course with this approach, once you attach the behavior, it will do do what the code written tells it to do without need for triggers. Note that you can also use Commands with Behaviors.

Blend 3 and Visual Studio 2008 SP1 comes with templates to get you started in creating actions, behaviors and triggers as in Fig. 1 and 2.

[2009.09.13].01.Blend.new.item
Fig. 1. Blend 3 interactivity templates

[2009.09.13].02.VS.new.item
Fig. 2. Visual Studio interactivity templates

Each time you add one of these items to your project, a skeleton implementation is created. For example, if you are creating a new Behavior, your class will already derive from Behavior<T>, and OnAttached() and OnDetaching() methods will be overridden. In addition, each comes commented to point you in the right direction as shown in Listing 1.

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Text;
  4: using System.Windows;
  5: using System.Windows.Controls;
  6: using System.Windows.Data;
  7: using System.Windows.Documents;
  8: using System.Windows.Input;
  9: using System.Windows.Media;
 10: using System.Windows.Media.Imaging;
 11: using System.Windows.Shapes;
 12: using System.Windows.Interactivity;
 13: //using Microsoft.Expression.Interactivity.Core;
 14: 
 15: namespace ActionBehaviorBlogDemo01
 16: {
 17:   public class Behavior2 : Behavior<DependencyObject>
 18:   {
 19:     public Behavior2()
 20:     {
 21:       // Insert code required on object creation below this point.
 22: 
 23:       //
 24:       // The line of code below sets up the relationship between the command and the function
 25:       // to call. Uncomment the below line and add a reference to Microsoft.Expression.Interactions
 26:       // if you choose to use the commented out version of MyFunction and MyCommand instead of
 27:       // creating your own implementation.
 28:       //
 29:       // The documentation will provide you with an example of a simple command implementation
 30:       // you can use instead of using ActionCommand and referencing the Interactions assembly.
 31:       //
 32:       //this.MyCommand = new ActionCommand(this.MyFunction);
 33:     }
 34: 
 35:     protected override void OnAttached()
 36:     {
 37:       base.OnAttached();
 38: 
 39:       // Insert code that you would want run when the Behavior is attached to an object.
 40:     }
 41: 
 42:     protected override void OnDetaching()
 43:     {
 44:       base.OnDetaching();
 45: 
 46:       // Insert code that you would want run when the Behavior is removed from an object.
 47:     }
 48: 
 49:     /*
 50:     public ICommand MyCommand
 51:     
 52:       get;
 53:       private set;
 54:     }
 55:      
 56:     private void MyFunction()
 57:     {
 58:       // Insert code that defines what the behavior will do when invoked.
 59:     }
 60:     /*
 61:   }
 62: }

Listing 1: Generated template to implement a Behavior

Examples of Actions and Behaviors

In all the samples, I am basically running the same animation, which changes the Opacity of the targeted or associated element. The main areas to look at is the base classes being derived from in each case as well as the comments in each class.

Example 1: Action which derives from TriggerAction

  1: using System;
  2: using System.Windows;
  3: using System.Windows.Interactivity;
  4: using System.Windows.Media.Animation;
  5: 
  6: namespace ActionBehaviorBlogDemo02
  7: {
  8: 
  9:     /// <summary>
 10:     /// Class showing a simple action that can be invoked by a trigger
 11:     /// </summary>
 12:     // Change the type parameter to be a FrameworkElement to access
 13:     //  the AssociatedObject's Opacity property
 14:     public class OpacityAction : TriggerAction<FrameworkElement>
 15:     {
 16:         // variables to hold Storyboard and
 17:         //  DoubleAnimation objects
 18:         Storyboard myStoryboard = new Storyboard();
 19:         DoubleAnimation myDoubleAnimation = new DoubleAnimation();
 20:  
 21:         public OpacityAction(){ }
 22: 
 23:         protected override void Invoke(object o)
 24:         {
 25:             // Insert code that defines what the Action 
 26:             //  will do when triggered/invoked.
 27:             this.Animate();
 28:         }
 29: 
 30:         // method to be called by Invoke()
 31:         public void Animate()
 32:         {
 33:             // set up the animation properties 
 34:             myDoubleAnimation.To = 0.2;
 35:             myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
 36:             myDoubleAnimation.AutoReverse = true;
 37:             myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
 38: 
 39:             // the target of the animation is the AssociatedObject
 40:             //  i.e the object the Action is attached to
 41:             Storyboard.SetTarget(myDoubleAnimation, AssociatedObject);
 42:             // animate the Opacity property
 43:             Storyboard.SetTargetProperty(myDoubleAnimation,
 44:                 new PropertyPath(FrameworkElement.OpacityProperty));
 45:             myStoryboard.Children.Add(myDoubleAnimation);
 46:             myStoryboard.Begin();
 47:         }
 48:     }
 49: }

Listing 2: A simple Action

Example 2: Action which derives from TargetedTriggerAction

  1: using System;
  2: using System.Windows;
  3: using System.Windows.Interactivity;
  4: using System.Windows.Media.Animation;
  5: 
  6: namespace ActionBehaviorBlogDemo02
  7: {
  8: 
  9:     /// <summary>
 10:     /// Class used to set the target of the action other to the element it is attached to
 11:     /// </summary>
 12:     // If you want your Action to target elements other than its parent, extend your class
 13:     // from TargetedTriggerAction instead of from TriggerAction
 14:     public class OpacityTargetedTriggerAction : TargetedTriggerAction<FrameworkElement>
 15:     {
 16:         // variables to hold Storyboard and
 17:         //  DoubleAnimation objects
 18:         Storyboard myStoryboard = new Storyboard();
 19:         DoubleAnimation myDoubleAnimation = new DoubleAnimation();
 20:         public OpacityTargetedTriggerAction()
 21:         {
 22:             // Insert code required on object creation below this point.
 23:         }
 24: 
 25:         protected override void Invoke(object o)
 26:         {
 27:             if (Target == null)
 28:             {
 29:                 return;
 30:             }
 31: 
 32:             // Insert code that defines what the Action 
 33:             //  will do when triggered/invoked.
 34:             this.Animate();
 35:         }
 36: 
 37:         // method to be called by Invoke()
 38:         public void Animate()
 39:         {
 40:             // set up the animation properties 
 41:             myDoubleAnimation.To = 0.2;
 42:             myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
 43:             myDoubleAnimation.AutoReverse = true;
 44:             myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
 45: 
 46:             // The action is meant to be executed on the Target
 47:             //  element, which can be 
 48:             //  or another element. By using the Target property here,
 49:             //  both cases are covered.
 50:             Storyboard.SetTarget(myDoubleAnimation, base.Target);
 51:             // animate the Opacity property
 52:             Storyboard.SetTargetProperty(myDoubleAnimation,
 53:                 new PropertyPath(FrameworkElement.OpacityProperty));
 54:             myStoryboard.Children.Add(myDoubleAnimation);
 55:             myStoryboard.Begin();
 56:         }
 57:     }
 58: }

Listing 3: An Action that can execute on the attached object or on another Target

Example 3: A simple Behavior

  1: using System;
  2: using System.Windows;
  3: using System.Windows.Interactivity;
  4: using System.Windows.Media.Animation;
  5: 
  6: namespace ActionBehaviorBlogDemo02
  7: {
  8:     /// <summary>
  9:     /// Basic independent behavior
 10:     /// </summary>
 11:     public class OpacityTriggerlessBehavior : Behavior<FrameworkElement>
 12:     {
 13:         Storyboard myStoryboard = new Storyboard();
 14:         DoubleAnimation myDoubleAnimation = new DoubleAnimation();
 15:         public OpacityTriggerlessBehavior()
 16:         {
 17:         }
 18: 
 19:         protected override void OnAttached()
 20:         {
 21:             base.OnAttached();
 22: 
 23:             // Since this behavior depicts an animation,
 24:             //  the animation will run as soon as the app
 25:             //  is up and running
 26:             // Here, the behavior is doing something without
 27:             //  being asked/triggered
 28:             this.Animate();
 29:         }
 30: 
 31:         protected override void OnDetaching()
 32:         {
 33:             base.OnDetaching();
 34:         }
 35: 
 36:         public void Animate()
 37:         {
 38:             // set up the animation properties 
 39:             myDoubleAnimation.To = 0.2;
 40:             myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
 41:             myDoubleAnimation.AutoReverse = true;
 42:             myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
 43: 
 44:             // the target of the animation is the AssociatedObject
 45:             //  i.e the object the behavior is attached to
 46:             Storyboard.SetTarget(myDoubleAnimation, AssociatedObject);
 47:             // animate the Opacity property
 48:             Storyboard.SetTargetProperty(myDoubleAnimation,
 49:                 new PropertyPath(FrameworkElement.OpacityProperty));
 50:             myStoryboard.Children.Add(myDoubleAnimation);
 51:             myStoryboard.Begin();
 52:         }
 53:     }
 54: }

Listing 4: A simple Behavior

Example 4: A Behavior that can be triggered

  1: using System;
  2: using System.Windows;
  3: using System.Windows.Input;
  4: using System.Windows.Interactivity;
  5: using System.Windows.Media.Animation;
  6: 
  7: namespace ActionBehaviorBlogDemo02
  8: {
  9:     /// <summary>
 10:     /// Showing how behaviors can be infulenced by a trigger
 11:     /// </summary>
 12:     public class OpacityTriggeredBehavior : Behavior<FrameworkElement>
 13:     {
 14:         Storyboard myStoryboard = new Storyboard();
 15:         DoubleAnimation myDoubleAnimation = new DoubleAnimation();
 16:         public OpacityTriggeredBehavior()
 17:         {
 18: 
 19:             // set up the animation properties 
 20:             myDoubleAnimation.To = 0.2;
 21:             myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
 22:             myDoubleAnimation.AutoReverse = true;
 23:             myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
 24: 
 25:             // the target of the animation is the AssociatedObject
 26:             //  i.e the object the behavior is attached to
 27:             Storyboard.SetTargetProperty(myDoubleAnimation,
 28:                 new PropertyPath(FrameworkElement.OpacityProperty));
 29:             myStoryboard.Children.Add(myDoubleAnimation);
 30:         }
 31: 
 32: 
 33:         protected override void OnAttached()
 34:         {
 35:             base.OnAttached();
 36:             // respond to mouse events to control the behavior
 37:             this.AssociatedObject.MouseEnter += new MouseEventHandler(AssociatedObject_MouseEnter);
 38:             this.AssociatedObject.MouseLeave += new MouseEventHandler(AssociatedObject_MouseLeave);
 39:         }
 40:         protected override void OnDetaching()
 41:         {
 42:             base.OnDetaching();
 43:             this.AssociatedObject.MouseEnter -= new MouseEventHandler(AssociatedObject_MouseEnter);
 44:             this.AssociatedObject.MouseLeave -= new MouseEventHandler(AssociatedObject_MouseLeave);
 45:         }
 46: 
 47:         void AssociatedObject_MouseEnter(object sender, MouseEventArgs e)
 48:         {
 49:             FrameworkElement fe = sender as FrameworkElement;
 50:             Storyboard.SetTarget(myDoubleAnimation, fe);
 51:             myStoryboard.Begin();
 52:         }
 53:         void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
 54:         {
 55:             myStoryboard.Stop();
 56:         }
 57:     }
 58: }

Listing 5: Behavior with triggers

[2009.09.13].03.Sample.of.actions.and.behaviors

Fig. 3. Sample of actions and behaviors

Download the Code!




[2009.09.14] Introduction to WPF Actions and Behaviors

I noticed something called Behaviors in Blend 3 under the Asset panel and decided to take a look into it some more. It took quite a bit of reading the Expression Team blog posts to get a sense of what is going on with this new addition to WPF.

Triggers, Actions and Behaviors

Triggers and Actions are words you have probably heard if you are playing around with WPF. Behaviors basically builds on the that infrastructure to extend it to a nice, interesting level. Because the three concepts - triggers, actions and behaviors are closely related, it gets a bit confusing telling what is what. For the most part, triggers and actions are perhaps what you may most familiar with. For instance, when you fire the MouseEnter property on an element, its Background property changes.

You can think of triggers and actions as a stimulus/response or a cause/effect pair. Based on that analogy, let's try to expand that a bit. The trigger/action pairs is related by the statement:
when something happens, do something

A Trigger is the stimulus/cause that motivates something to happen. This is the "when something happens" part. From the example above, the trigger will be when the MouseEnter property changes.

An Action is the response/effect to a trigger. This is the "do something" part. Continuing from the above example, the action would be the changing of the element's Background property.
Note: When I say Action from now on I mean an Action/Trigger pair

A Behavior take this one step further. It has two types of functionality.
   [1] It can do something due to a trigger or
   [2] It can do something on its own

As you can see, a Behavior will let you write code that is both trigger dependent and trigger independent.

When I was first going through the literature of these concepts, I admit I was a bit confused. But here is the bottom line as I understand it:
Both an Action and a Behavior is a piece of reusable, modular piece of interactive code that can be attached to an element. The difference between the two is that an Action is always invoked/called/notified by a Trigger, whereas a Behavior can either be invoked independently of a trigger or it may be called using a trigger.

Therefore, while the action/trigger pair are pretty much inseparable, a Behavior is more loosely coupled. So with behaviors, you get all the pros of using triggers and actions as well as the ability to ditch the triggers completely if you want to.

Target and Source

There are two additional parts that makes these all work like a nice, well-oiled machine. When you drop an action(+ trigger - these naturally go together) or a behavior(+ trigger) onto an element, the element acts both as the Source and the Target of the action or behavior. Depending on what you are creating (a new Trigger, Action, or Behavior), you will have access to properties to change both of these. The SourceName property lets you change the source of the trigger from the element the action is attached to to something else. The TargetName property lets you change the target of the action other than what it is attached to. These will all make a bit more sense later as these two properties are visible as a result of deriving from certain base classes.

Attaching Behaviors and Actions in Blend 3

This could not be any easier. Blend 3 ships with a few stock behaviors and actions. You can get more from the Microsoft Expression Gallery. You can navigate to the Behaviors and Actions that comes with Blend 3 either using the new Assets tab as shown in Fig. 1 or going to the Assets panel as shown in Fig. 2.

[2009.09.12].01.Actions.and.behaviors.in.blend.assets.01
Fig. 1. Accessing Behaviors and Actions using Asset Tab

[2009.09.12].02.Actions.and.behaviors.in.blend.assets.02
Fig. 2. Accessing Behaviors and Actions using Asset Panel

All you have to do is to drag the Action or Behavior you want either onto the element on the artboard or on the Objects and Timeline panel as in Fig. 3. The Action or Behavior then becomes a child of the element it is attached to as in Fig. 4.

[2009.09.12].03.Drag.behavior.to.element
Fig. 3. Dragging a behavior onto an element

[2009.09.12].04.Behavior.child.of.element
Fig. 4. Attached behavior

An action or a behavior may expose properties that you can use to configure each item. These are displayed in the Properties tab as shown in Fig. 5 and 6.

[2009.09.12].05.Behavior.properties
Fig. 5. Properties for the MouseDragElementBehavior

[2009.09.12].06.Action.properties
Fig. 6. Properties for the GoToStateAction

Notice that the properties for the behavior in Fig. 5 does not have a trigger, so this is a type of trigger-independent behavior. Fig. 6 shows an action and the properties indicate several modifiable areas. Note particularly that in this Action you can change both the SourceName and TargetName properties, which essentially changes the Source and Target of the Action from the attached element (the Rectangle) to another element of your choice. Notice also that both of these properties have a little bullseye next to their modifiable textboxes. This is a super handy way of choosing a new Source or Target. Simply click the bullseye and point it to the element you want to set. For instance, in Fig. 7, you can set the Ellipse as the Target as shown.

[2009.09.12].07.Using.artboard.picker
Fig. 7. Setting the TargetName property using the Artboard element picker

Here is a nice little handy trick (thanks!) - if you have overlapping elements, to see the list of all the elements, hold done Ctrl, choose the Artboard picker, then Left Click on the topmost element and a new context menu pops up, allowing you to choose the element to apply the property to. This is in Fig. 8.

[2009.09.12].08.Applying.property.to.overlapping.elements
Fig. 8. Applying properties to overlapping elements

The final thing I would like to point out is that you can change the type of trigger that will be used to fire the action (or behavior if it supports triggers). Clicking the New button next to the TriggerType label will bring up a list of triggers you can set for the action or behavior. This is in Fig. 9 where the current trigger is an EventTrigger. Note that if you create your own triggers in a project, you can set them from this screen.

[2009.09.12].09.Trigger.types
Fig. 9. Changing the type of the trigger

I hope this post has provided an overview of what triggers, actions and behaviors are and how to use them. Next will be about the classes involved and how to write your own actions and behaviors.