Recently i read a excellent blog form Silverlight Show , this article is based on the above blog. Link
Prerequisite
In order to write behaviors and triggers you need to have installed the Microsoft Expression Blend SDK on your machine. After that you need to add a reference to the System.Windows.Interactivity.dll assembly which is located in:{Program Files}\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries\Silverlight
Behavior types
Currently you can use three types of bevahors: Behavour, TriggerAction and TargetedTriggerAction.
the Behavior class
For simple scenarios the generic Behavior
The first thing you should do when creating behaviors is to create a new class which inherits from the generic class Behavior
public class InverseColorClickBehavior :
Behavior
{
public InverseColorClickBehavior() :
base()
{
}
}
For that particular case I am interested in the mouse click event. That’s why in the OnAttached method I will attach to the MouseLeftButtonDown event of the associated with that behavior object. And respectively in the OnDetaching method I will detach from that event.
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.MouseLeftButtonDown +=
new MouseButtonEventHandler( AssociatedObject_MouseLeftButtonDown );
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.MouseLeftButtonDown -=
new MouseButtonEventHandler( AssociatedObject_MouseLeftButtonDown );
}
The only thing that's left is to add an inverse color effect to the associated object in the mouse Click event handler.
private void AssociatedObject_MouseLeftButtonDown(
object sender, MouseButtonEventArgs e )
{
this.AssociatedObject.Effect =
this.AssociatedObject.Effect == null ?
this.AssociatedObject.Effect = this.inverseColor :
this.AssociatedObject.Effect = null;
}
Once we have created the bahavior we should attach it to a particular object.
<Image Stretch="Fill"
Source="/Photos/Image1.jpg">
<interactivity:Interaction.Behaviors>
<local:InverseColorClickBehavior/>
interactivity:Interaction.Behaviors>
Image>
My second behavior is little more complicated. It hits more common scenario where you want to add an animation for example on the mouse over event. Again as the first example I will create a new class which inherits from the generic Behavior
I want when the mouse is over the element to add a magnifier shader effect on the element and when the mouse leaves the element area to remove the effect. That’s why I want to handle the MouseEnter and MouseLeave events in order to enable and disable the effect. On the analogy of the previous case in the OnAttached method I will attach to the MouseEnter and MouseLeave events of the associated with that behavior object. And respectively in the OnDetaching method I will detach from that events. When the mouse is over the element I want to track the mouse move event. That’s why I will attach also to the mouse move event on entering and will detach from it on leaving the element area.
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.MouseEnter +=
new MouseEventHandler( AssociatedObject_MouseEnter );
this.AssociatedObject.MouseLeave +=
new MouseEventHandler( AssociatedObject_MouseLeave );
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.MouseEnter -=
new MouseEventHandler( AssociatedObject_MouseEnter );
this.AssociatedObject.MouseLeave -=
new MouseEventHandler( AssociatedObject_MouseLeave );
}
private void AssociatedObject_MouseLeave( object sender, MouseEventArgs e )
{
this.AssociatedObject.MouseMove -=
new MouseEventHandler( AssociatedObject_MouseMove );
this.AssociatedObject.Effect = null;
}
private void AssociatedObject_MouseEnter( object sender, MouseEventArgs e )
{
this.AssociatedObject.MouseMove +=
new MouseEventHandler( AssociatedObject_MouseMove );
this.AssociatedObject.Effect = this.magnifier;
}
The whole work is done in the mouse move event handler.
private void AssociatedObject_MouseMove( object sender, MouseEventArgs e )
{
( this.AssociatedObject.Effect as Magnifier ).Center =
e.GetPosition( this.AssociatedObject );
Point mousePosition = e.GetPosition( this.AssociatedObject );
mousePosition.X /= this.AssociatedObject.ActualWidth;
mousePosition.Y /= this.AssociatedObject.ActualHeight;
this.magnifier.Center = mousePosition;
Storyboard zoomInStoryboard = new Storyboard();
DoubleAnimation zoomInAnimation = new DoubleAnimation();
zoomInAnimation.To = this.magnifier.Magnification;
zoomInAnimation.Duration = TimeSpan.FromSeconds( 0.5 );
Storyboard.SetTarget( zoomInAnimation, this.AssociatedObject.Effect );
Storyboard.SetTargetProperty( zoomInAnimation,
new PropertyPath( Magnifier.MagnificationProperty ) );
zoomInAnimation.FillBehavior = FillBehavior.HoldEnd;
zoomInStoryboard.Children.Add( zoomInAnimation );
zoomInStoryboard.Begin();
}
You can add this behavior in XAML the same way as the first one. And here is the demo for the second behavior (which covers maybe the most commonly used events for animations, effects, etc.). Just move your mouse cursor over the image.
To summarize before continuing with the triggers, in my opinion the behaviors is very similar to the extension methods in C#. The only difference is that the behavior is a component. It encapsulates some functionality and can be attached to another component to extend its built-in functionality.
Using the TriggerAction class
In simple cases the Behavior
I will create a trigger that will apply an animation on the click event. The first step is to create a new class which inherits from the generic TriggerAction
public class WaveTrigger : TriggerAction
{
protected override void Invoke( object parameter )
{
this.waveStoryboard.Begin();
}
}
As you can see the Invoke method is the only required method that you need to have in your trigger. But often in the practice you will need to override several other methods of the class. In the constructor of our trigger I need to configure the animation and the storyboard.
public WaveTrigger() :
base()
{
this.waveAnimation = new DoubleAnimation();
this.waveStoryboard = new Storyboard();
this.waveEffect = new WaveEffect();
this.InitializeTrigger();
this.waveAnimation.AutoReverse = false;
this.waveStoryboard.Children.Add( this.waveAnimation );
}
private void InitializeTrigger()
{
this.waveAnimation.From = -this.WaveFrequency;
this.waveAnimation.To = this.WaveFrequency;
this.waveAnimation.Duration = this.WaveDuration;
}
In order to set the animated object as well as the animated property I need to override that the OnAttached method. If you try to do that, for example, in the constructor you won’t succeed due to the fact that the associated object is still unknown.
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Effect = this.waveEffect;
Storyboard.SetTarget( this.waveAnimation,
this.AssociatedObject.Effect );
Storyboard.SetTargetProperty( this.waveAnimation,
new PropertyPath( WaveEffect.WavinessProperty ) );
}
The only required method you need to do in the Invoke method is to start the animation.
protected override void Invoke( object parameter )
{
this.waveStoryboard.Begin();
}
In order to make my WaveTrigger configurable I will add several dependency properties (for the duration and for the frequency). This is pretty straightforward. Once we have our trigger the final step is to use it in the XAML.
<Image Stretch="Fill"
Source="/Photos/Image3.jpg">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger
EventName="MouseLeftButtonUp">
<local:WaveTrigger WaveFrequency="1.9"
WaveDuration="00:00:05"/>
interactivity:EventTrigger>
interactivity:Interaction.Triggers>
Image>
6. Using the TargetedTriggerAction class
The third type behavior is offered by the generic TargetedTriggerAction
public class TurnImageTargetedTrigger :
TargetedTriggerAction
{
protected override void Invoke( object parameter )
{
}
protected override void OnAttached()
{
base.OnAttached();
( this.AssociatedObject as FrameworkElement ).Loaded +=
new RoutedEventHandler( TurnImageTargetedTrigger_Loaded );
}
protected override void OnDetaching()
{
base.OnDetaching();
( this.AssociatedObject as FrameworkElement ).Loaded -=
new RoutedEventHandler( TurnImageTargetedTrigger_Loaded );
}
private void TurnImageTargetedTrigger_Loaded( object sender, RoutedEventArgs e )
{
}
}
The trick here is that the target object can be access only when the associated object is loaded. That’s why I need to attach to the Loaded event of the associated object. An Invoke method must also be defined with the only purpose to start the animation. After the trigger is ready we need to use it in the XAML. You can see how this can be done in the next code snippet.
<Image x:Name="imgToFlip" Stretch="Fill"
Source="/Photos/Image4.jpg"/>
<Button Content="TurnImage">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger
EventName="Click">
<local:TurnImageTargetedTrigger
TargetName="imgToFlip"/>
interactivity:EventTrigger>
interactivity:Interaction.Triggers>
Button>
Behaviors and Expression Blend 3
The Properties pane you may adjust the behavior (setting properties, which event will fire it, etc.).
If you note in the Assets library except our behaviors created for the current solution there are several other behaviors which are listed in all Expression Blend projects. If you want your own custom behaviors also to be listed for all Blend project you need to register your assembly in the registry. You need to use the following path in the Registry Editor:
HKEY_CURRENT_USER(or HKEY_LOCAL_MACHINE) \Software\Microsoft\Expression\Blend\v3.0\Toolbox\Silverlight\v3.0
Using the DefaultTriggerAttribute
By default when you create a new TriggerAction, Expression Blend will associate it with the MouseLeftButtonDown event where it can, or with the Loaded event if the MouseLeftButtonDown is not available. However sometimes you may want a custom Action to be connected with a different default event. In order to do this you should use the DefaultTriggerAttibute:
public DefaultTriggerAttribute(Type targetType, Type triggerType,
params object[] parameters)
The first parameter is the type for which to create this Trigger, the second is the type of Trigger to create and the final is a list of constructor arguments to the Trigger when it is created. If you specify more than one attribute, the most derived targetType that is applicable when the user create the behavior will be used.
[DefaultTrigger(typeof(UIElement), typeof(EventTrigger),
"MouseLeftButtonDown")]
[DefaultTrigger(typeof(ButtonBase), typeof(EventTrigger),
"Click")]
public class WaveAction : TriggerAction
{
}
If the WaveAction is dragged onto a Button, the Action will be created under a Click EventTrigger. However, if the WaveAction is dragged onto any other UIElement, a MouseLeftButtonDown trigger will be created.
Using the TypeConstraintAttribute
You can use the TypeConstraintAttribute to specify type constraints on the AssociatedObjectTargetedTriggerAction and EventTriggerBase. of
10. Final words
The motivation for adding behaviors in Silverlight 3 is twofold. First, the behaviors are somehow work-around for the missing triggers in Silverlight. They allow closing the gap between WPF and Silverlight. Second, they allow designers to add interactivity without needing to write any code. I like the behaviors and definitely will use them in any future projects. This post covers quite a bit ground. I hope it was useful for you. In the official siteReferences of Expression Blend you can find a lot of ready for use behaviors. Also see the section for more information.
11. References
- http://gallery.expression.microsoft.com/en-us/site/search?f[0].Type=RootCategory&f[0].Value=behaviors
- http://joel.neubeck.net/2009/07/silverlight-3-flip-triggeraction/
- http://wildermuth.com/2009/05/16/Writing_Behaviors_for_Silverlight_3_-_Part_1
- http://wildermuth.com/2009/05/16/Writing_Behaviors_for_Silverlight_3_-_Part_2
- http://www.wintellect.com/CS/blogs/jprosise/archive/2009/08/15/custom-behaviors-in-silverlight-3-and-blend-3.aspx
No comments:
Post a Comment