概要

このサンプルでは、Expression BlendでサポートされているBehaviorの使い方、カスタムのBehaviorの作り方とXAMLでの設定方法について説明します。BehaviorはExpression Blendでサポートされていますが、Visual StudioでもExpression Blend SDKをダウンロードすることで使用できます。

このサンプルでは、上記SDKをインストールしたVisual Studio上でのBehaviorを使うことを前提にしています。Behaviorを使うときに、どのようなXAMLが記述されるのか把握することでExpression Blendでドラッグアンドドロップしたときに、どのようなXAMLが生成されるのかイメージしやすくなると思います。

Behaviorとは

Behaviorとは、添付ビヘイビアとしてコミュニティ内で広がっていた添付プロパティの使い方を、Expression Blend3で公式にツールサポートが提供されたものになります。それまで、ばらばらに実装されていた添付ビヘイビアの実装をBehavior<T>クラスとして基本実装を提供することで、必要最低限のメソッドを実装するだけで作成可能にしています。

Behaviorを使用することで、XAMLで部品化されたプレゼンテーションロジックを記述することが出来ます。添付ビヘイビアについての記事は、SharpLabの以下の翻訳記事が非常にわかりやすいです。

BehaviorをXAMLで記述するための手順

BehaviorをXAMLで記述するための手順は以下の通りになります。

以下のアセンブリを参照に追加します。

Behaviorを追加したいXAMLに以下の名前空間を定義します。

 

XAML
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
 
 
Behaviorを追加したいコントロールの下に、以下のようにi:Interaction.Behaviors/i:Interaction.Triggers添付プロパティを追加し、その下にBehavior(Triggersの下にはTriggerとTriggerAction)を設定します。以下にMessageBoxBehaviorというビヘイビアをButtonに追加するXAMLの例を示します。
XAML
<ButtonContent="MessageBox Behavior"<i:Interaction.Behaviors<!-- Behaviorにはプロパティも作成可能。添付プロパティとして作成するとBindingも可能。 --><local:MessageBoxBehavior Message="こんにちは世界"/></i:Interaction.Behaviors></Button>

カスタムBehaviorの作成方法

Behaviorを自作するには、System.Windows.Interactivity.Behavior<T>クラスを継承して作成します。Behaviorクラスには、Behaviorがコントロールにアタッチされたときの呼び出されるOnAttachedメソッドと、コントロールから切り離されるときによびだされるOnDetachingメソッドが定義されています。この2つのメソッドをオーバーライドすることで、コントロールのイベントを購読して、独自のプレゼンテーションロジックを実行していきます。

ボタンのClickイベントを購読してHello worldと表示するBehaviorは以下のようになります。

 

C#
namespace Behavior.Sample1 
{ 
    using System; 
    using System.Windows; 
    using System.Windows.Controls; 
    using System.Windows.Interactivity; 
 
    /// <summary>/// ボタンをクリックするとHello worldというメッセージを表示する/// 動作を追加するビヘイビア。/// </summary>publicclass HelloWorldBehavior : Behavior<Button> 
    { 
        /// <summary>/// ボタンにアタッチされたときに呼ばれる。/// </summary>protectedoverridevoid OnAttached() 
        { 
            base.OnAttached(); 
            // 一般的にイベントの設定のような初期化処理をここでおこなうthis.AssociatedObject.Click += this.Button_Click; 
        } 
 
        /// <summary>/// デタッチされたときに呼ばれる。/// </summary>protectedoverridevoid OnDetaching() 
        { 
            // 一般的にOnAttachedでした初期化の後始末をここで行うthis.AssociatedObject.Click -= this.Button_Click; 
            base.OnDetaching(); 
        } 
 
        /// <summary>/// Hello worldを表示する。/// </summary>/// <param name="sender"></param>/// <param name="e"></param>privatevoid Button_Click(object sender, EventArgs e) 
        { 
            MessageBox.Show("Hello world"); 
        } 
    } 
} 
 
 
BehaviorのAssociatedObjectでBehaviorに紐づけられたオブジェクトを参照することが出来ます。ここでは、OnAttachedでボタンのClickイベントを購読し、OnDetachingでボタンのClickイベントの購読を解除しています。そして、ボタンのClickイベントでメッセージボックスを表示しています。Behaviorのジェネリックパラメータには、Behaviorを適用できるクラスを指定します。(今回の例ではButton)
このBehaviorをボタンに設定するXAMLは以下のようになります。(local名前空間にはBehavior.Sample1名前空間が設定されています)
XAML
<ButtonContent="Hello world Behavior"<!-- i:Interaction.Behaviors添付プロパティにBehaviorを設定する --><i:Interaction.Behaviors<!-- 自作のHelloWorldBehaviorを追加。Behaviorは必要に応じて複数追加できる。 --><local:HelloWorldBehavior /></i:Interaction.Behaviors></Button>

TriggerとTriggerAction

Behaviorを実装していると、Behaviorは以下の要素で構成されていることがわかります。

  • Behaviorで実行する処理を起動するきっかけ
  • Behaviorで実行する処理

上記のHelloWorldBehaviorでは、処理を起動するきっかけはボタンのClickイベントで、処理は、メッセージボックスの表示になります。では、ボタンのクリックではなく、テキストボックスでEnterキーが押された時にメッセージボックスを表示するBehaviorを作成しようと考えます。そうするとOnAttachedとOnDetachingで購読/購読解除する処理が違うだけのコードになります。このままだと、処理を起動するきっかけx実行する処理の数だけBehaviorを作成しなければなりません。

TriggerとTriggerActionは、この処理のきっかけと実行する処理を分離したものになります。Triggerで、処理を起動するきっかけを定義して、TriggerActionで実行する処理を定義します。上記のHelloWorldBehaviorをTriggerとTriggerActionに分離すると以下のようになります。

まず、処理を実行するきっかけのTriggerはSystem.Windows.Interactivity.TriggerBase<T>を継承してBehaviorと同様にOnAttachedとOnDetachingを実装します。そして、処理を実行する部分でInvokeActionというメソッドを呼び出します。

 

 

C#
namespace Behavior.Sample3 
{ 
    using System; 
    using System.Windows.Controls; 
    using System.Windows.Interactivity; 
 
    /// <summary> 
    /// Triggerを作成するにはTriggerBaseを継承する。 
    /// ジェネリックパラメータには、Triggerを置けるコントロールを指定する。 
    /// </summary> 
    public class ButtonClickTrigger : TriggerBase<Button> 
    { 
        // OnAttachedとOnDetachingは、Behaviorと同じように作る。 
        protected override void OnAttached() 
        { 
            base.OnAttached(); 
            this.AssociatedObject.Click += this.Button_Click; 
        } 
 
        protected override void OnDetaching() 
        { 
            this.AssociatedObject.Click -= this.Button_Click; 
            base.OnDetaching(); 
        } 
 
        private void Button_Click(object sender, EventArgs e) 
        { 
            // 子要素のActionを実行する。 
            this.InvokeActions(e); 
        } 
    } 
} 
 
 
次に、メッセージボックスを表示するTriggerActionを定義します。TriggerActionは、System.Windows.Interactivity.TriggerAction<T>を継承して作成します。そして、実行する処理をInvokeメソッドをオーバーライドして定義します。
C#
namespace Behavior.Sample3 
{ 
    using System.Windows; 
    using System.Windows.Interactivity; 
 
    /// <summary> 
    /// HelloWorldとメッセージボックスに表示するアクション。 
    /// </summary> 
    public class HelloWorldAction : TriggerAction<DependencyObject> 
    { 
        protected override void Invoke(object parameter) 
        { 
            MessageBox.Show("Hello world"); 
        } 
    } 
} 
 
 
TriggerとTriggerActionが出来たので、XAMLで指定します。Triggerはi:Interaction.Triggersに以下のように指定します。
XAML
<Button Content="Custom trigger"    <i:Interaction.Triggers        <!-- 自作トリガーの設定 --> 
        <local:ButtonClickTrigger> 
            <!-- HelloWorldと表示する --> 
            <local:HelloWorldAction /> 
        </local:ButtonClickTrigger> 
    </i:Interaction.Triggers> 
</Button> 
 
 

今回作成したButtonClickTriggerのようにイベントを処理の実行のきっかけとするTriggerクラスの汎用的な実装としてEventTriggerというTriggerがExpression Blend SDKで定義されています。単純にイベントを処理のきっかけにするには、こちらのTriggerを使用します。上記の例をEventTriggerを使用した方法に書き換えると以下のようになります。

 

XAML
<Button Content="Event Trigger"    <i:Interaction.Triggers        <!-- 自作トリガーの設定 --> 
        <i:EventTrigger EventName="Click"            <!-- HelloWorldと表示する --> 
            <local:HelloWorldAction /> 
        </i:EventTrigger> 
    </i:Interaction.Triggers> 
</Button> 
 
 
BehaviorとTrigger&TriggerActionの使い分け

 

BehaviorとTrigger&TriggerActionの使い分けですが、基本的にTrigger&TriggerActionを使用するほうが再利用性が高くなります。Behaviorを使用するのは、処理のきっかけと実行する処理が分離できない特殊なケースの場合に使用します。Expression Blend SDKでは、ドラッグすることで移動可能にするMouseDragElementBehaviorが提供されています。これはドラッグすることと、移動することはきっても切れない(普通クリックだけで移動するという動作は考えられないですよね?)ためBehaviorとして提供されていると考えられます。
XAML
<Ellipse Width="50" Height="50" Fill="Pink"    <i:Interaction.Behaviors        <!-- Expression Blendのアセンブリには、いくつか組み込みのBehaviorが提供されている。 --> 
        <ei:MouseDragElementBehavior ConstrainToParentBounds="True" /> 
    </i:Interaction.Behaviors> 
</Ellipse> 
 
 
その他には、デモとして一番魅力的なFluidMoveBehaviorも動作のきっかけと動作が切り離せない例になると思います。FluidMoveBehaviorは、コンテナの子要素の移動をなめらかにするものです。以下に動作例を示します。このBehaviorの使用方法としてListBoxなどのItemsPanelに設定することで、要素の削除や挿入時の動作を滑らかにすることができます。

Behaviorへのプロパティの定義

BehaviorやTrigger&TriggerActionにはプロパティを定義することが出来ます。これによって、Behaviorの挙動を利用者がカスタマイズすることが出来ます。Behaviorのプロパティは一般的に依存プロパティとして定義します。こうすることでBindingに対応する柔軟な設定が出来るようになります。以下に指定したメッセージをMessageBoxに表示するTriggerActionの実装例を示します。

C#
namespace Behavior.Sample3 
{ 
    using System.Windows; 
    using System.Windows.Interactivity; 
 
    /// <summary> 
    /// Messageプロパティに設定した文字列をMessageBoxに表示するアクション。 
    /// </summary> 
    public class MessageBoxAction : TriggerAction<DependencyObject> 
    { 
        public static readonly DependencyProperty MessageProperty = 
            DependencyProperty.Register( 
                "Message", 
                typeof(string), 
                typeof(MessageBoxAction), 
                new PropertyMetadata(string.Empty)); 
 
        public string Message 
        { 
            get { return (string)GetValue(MessageProperty); } 
            set { SetValue(MessageProperty, value); } 
        } 
 
        protected override void Invoke(object parameter) 
        { 
            // Messageプロパティの値を表示する 
            MessageBox.Show(this.Message); 
        } 
    } 
} 
 
 
以上で、Behaviorについての基本的な説明は終わりです。

サンプルプログラムの説明

サンプルプログラムは、以下の3つのプロジェクトで構成されています。

  • Behavior.Sample1プロジェクト
    独自のBehaviorの定義と使用例と、MouseDragElementBehaviorの使用例です。
  • Behavior.Sample2プロジェクト
    FluidMoveBehaviorの使用例です。 
  • Behavior.Sample3プロジェクト
    独自のTriggerとTriggerActionの実装と使用例です。