[Silverlight][WPF] 動手實作Behaviors - 動手實作 Behavior

  • 6143
  • 0
  • C#
  • 2013-07-14

經過之前的介紹,相信大家已經對Behavior有了初步的概念,但是對於怎麼寫出一個自己的Behavior,應該還是比較沒Fu吧!?
這篇就來跟大家介紹Behavior程式的結構,順便帶大家來實作一個「能用滑鼠滾輪控制放大縮小倍率的放大鏡Behavior」吧!!



經過之前的介紹,相信大家已經對Behavior有了初步的概念,但是對於怎麼寫出一個自己的Behavior,應該還是比較沒Fu吧!?

這篇就來跟大家介紹Behavior程式的結構,順便帶大家來實作一個「能用滑鼠滾輪控制放大縮小倍率的放大鏡Behavior」吧!!

基本上,當你透過Blend建立一個Behavior的Class之後,它預設的程式內容如下:


public Behavior1()
{
    // Insert code required on object creation below this point.
 
    //
    // The line of code below sets up the relationship between the command and the function
    // to call. Uncomment the below line and add a reference to Microsoft.Expression.Interactions
    // if you choose to use the commented out version of MyFunction and MyCommand instead of
    // creating your own implementation.
    //
    // The documentation will provide you with an example of a simple command implementation
    // you can use instead of using ActionCommand and referencing the Interactions assembly.
    //
    //this.MyCommand = new ActionCommand(this.MyFunction);
}
 
protected override void OnAttached()
{
    base.OnAttached();
 
    // Insert code that you would want run when the Behavior is attached to an object.
}
 
protected override void OnDetaching()
{
    base.OnDetaching();
 
    // Insert code that you would want run when the Behavior is removed from an object.
}
 
/*
public ICommand MyCommand
{
    get;
    private set;
}
 
private void MyFunction()
{
    // Insert code that defines what the behavior will do when invoked.
}
*/

 

從class的宣告行,我們可以看出Behavior是繼承了System.Windows.Interactivity中的Behavior<DependencyObject>的類別,我們可以透過修改< DependencyObject >來的方式來設定該Behavior能寄生(講附身好像比較貼切)的型別(必需是衍生自DependencyObject的類別),例如修改成Behavior<Button>則代表該Behavior只能附身在Button的 身上,而被附身的物件實體,則可以透過名為AssociatedObject的Property來存取。

除此之外,Behavior必需override兩個重要的Method,第一個是OnAttached,我們得透過這個Method定義出當目標物件被附身之後,它必需處理的事件有哪些;另一個是OnDetaching,一看就知道和OnAttached相反,這邊定義的是當附身解除之後,要善後的動作。

除了上述兩個Method之外,剩下的就是自由發揮創意的時間了,我們可以定義額外的Method,來處理OnAttached中事件觸發的EventListener,或是實作ICommand,以供MVVM中EvnetListener的Binding。

直接以前面提到的放大鏡為例:

MagnifierBehavior.cs

public class MagnifierBehavior : Behavior<FrameworkElement>
{
 
    private MagnifyEffect _magnifyEffect;
 
    public MagnifierBehavior() :
        base()
    {
        this._magnifyEffect = new MagnifyEffect();
    }
 
    public double Amount
    {
        get
        {
            return _magnifyEffect.Amount;
        }
        set
        {
            _magnifyEffect.Amount = value;
        }
    }
 
    public double InnerRadius
    {
        get
        {
            return _magnifyEffect.InnerRadius;
 
        }
        set
        {
            _magnifyEffect.InnerRadius = value;
        }
    }
 
    public double OuterRadius
    {
        get
        {
            return _magnifyEffect.OuterRadius;
 
        }
        set
        {
            _magnifyEffect.OuterRadius = value;
        }
    }
 
    protected override void OnAttached()
    {
        base.OnAttached();
 
        this.AssociatedObject.MouseEnter += new MouseEventHandler( AssociatedObject_MouseEnter );
        this.AssociatedObject.MouseLeave += new MouseEventHandler( AssociatedObject_MouseLeave );
        this.AssociatedObject.MouseWheel += new MouseWheelEventHandler( AssociatedObject_MouseWheel );
    }
 
    protected override void OnDetaching()
    {
        base.OnDetaching();
 
        this.AssociatedObject.MouseEnter -= new MouseEventHandler( AssociatedObject_MouseEnter );
        this.AssociatedObject.MouseLeave -= new MouseEventHandler( AssociatedObject_MouseLeave );
        this.AssociatedObject.MouseWheel -= new MouseWheelEventHandler( AssociatedObject_MouseWheel );
    }
 
    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._magnifyEffect;
    }
 
    void AssociatedObject_MouseWheel( object sender , MouseWheelEventArgs e )
    {
        if( this._magnifyEffect != null )
        {
            if( e.Delta > 0 && this.Amount <= 0.9 )
            {
                this.Amount += 0.1;
            }
            else if( e.Delta < 0 && this.Amount >= -0.9 )
            {
                this.Amount -= 0.1;
            }
        }
    }
 
    private void AssociatedObject_MouseMove( object sender , MouseEventArgs e )
    {
 
        //設定放大鏡的中心位置
        ( this.AssociatedObject.Effect as MagnifyEffect ).Center = e.GetPosition( this.AssociatedObject );
 
        //設定放大鏡的中心點為滑鼠的相對位置
        Point mousePosition = e.GetPosition( this.AssociatedObject );
        mousePosition.X /= this.AssociatedObject.ActualWidth;
        mousePosition.Y /= this.AssociatedObject.ActualHeight;
        this._magnifyEffect.Center = mousePosition;
 
        //撥放動畫
        Storyboard zoomInStoryboard = new Storyboard();
        DoubleAnimation zoomAnimation = new DoubleAnimation();
        zoomAnimation.To = this._magnifyEffect.Amount;
        zoomAnimation.Duration = TimeSpan.FromSeconds( 0.5 );
        Storyboard.SetTarget( zoomAnimation , this.AssociatedObject.Effect );
        Storyboard.SetTargetProperty( zoomAnimation , new PropertyPath( MagnifyEffect.AmountProperty ) );
 
        //動畫撥放完為停止狀態
        zoomAnimation.FillBehavior = FillBehavior.HoldEnd;
        zoomInStoryboard.Children.Add( zoomAnimation );
        zoomInStoryboard.Begin();
    }
}

 

MagnifierBehaviorDemo.xaml

<navigation:Page 
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:Ouch_Silverlight_Behaviors="clr-namespace:Ouch.Silverlight.Behaviors;assembly=Ouch.Silverlight.Behaviors" x:Class="Ouch.Silverlight.Behaviors.Demo.Pages.MagnifierBehaviorDemo"
           mc:Ignorable="d"
           d:DesignWidth="640" d:DesignHeight="480"
           Title="MagnifierBehaviorDemo Page">
    <Grid x:Name="LayoutRoot">
        <Image Source="/Ouch.Silverlight.Behaviors.Demo;component/Images/Photo1.jpg">
            <i:Interaction.Behaviors>
                <Ouch_Silverlight_Behaviors:MagnifierBehavior/>
            </i:Interaction.Behaviors>
        </Image>
    </Grid>
</navigation:Page>

因為我希望到時候所有繼承自FrameworkElement的物件都能被放大鏡附身,因此我把Behavior<T>設為Behavior<FrameworkElement>,而Blend中也內建了一個放大鏡的PixelShaderEffect(這個之後會再找機會向大家介紹),我就直接拿它來用。而我希望當物件被附身之後,可以透過滑鼠移動來改變放大鏡的位置,並且可以透過滑鼠滾輪來改變放大的倍率,因此我在OnAttached Method中設定了兩個EventListener,程式碼有點長,但是其實並不複雜吧!?就這樣,我就做完一個擁有放大鏡效果的Behavior囉!!

 

最後來看看實作出來的效果吧!!(若想體驗透過滾輪控制縮放程度的功能--請點此開啟新視窗)

 

最後的最後,附上原始碼,請自行取用: