KatsuYuzuのブログ

.NET の開発をメインとした日記です。

Blend対応のBehavior、TriggerActionを作る方法 #win8dev_jp

この記事はXAML Advent Calendar 2013 - Adventarの13日目の記事です。昨日は@f_swallowさんの独自のBehaviorとTrigger Actionを定義して利用する « 空談録です。
帰路中に考えたネタが被ってしまって焦りましたがなんとか味付けが出来たので同じネタで。
ビヘイビアー、アクションの基本的な作り方については昨日の@f_swallowさんの記事をご覧になってください。また、この記事中の MessageNotifyBehavior, MessageNotifyAction につきましても@f_swallowさんの記事の物です。

Blend対応のBehavior、TriggerActionを作る方法

ビヘイビアーもアクションもそのままではBlendのデザイナでポトペタしにくいですね。例えば、MessageNotifyBehaviorの例ですとButtonを想定しているのにすべてのコントロールにドロップできてしまいます。
f:id:KatsuYuzu:20131213020359p:plain
この操作をButtonに限定してみましょう。

TypeConstraintAttribute

TypeConstraintAttributeはIBehaviorのAssociatedObjectに型制約を指定します。

[TypeConstraint(typeof(Button))]
public class MessageNotifyBehavior : DependencyObject, IBehavior

f:id:KatsuYuzu:20131213020921p:plain
Button制約通りにTextBoxにペタっとできなくなりました。
ただし、@f_swallowさんもおっしゃってましたが一応制限できるという程度で、WPFのBehaviorのようにビルドエラーになるわけではありません。また、少し残念なのが実行時にもエラーになりません。あくまでもBlendの補助の位置づけです。
……気を取り直して、このアトリビュートはIBehaviorやIActionと同様にMicrosoft.Xaml.Interactivity名前空間にあります。DLLの参照が必要ですがBlendでビヘイビアーを使えば参照をかけてくれます。Visual Studioの時は参照マネージャーの[Windows] > [拡張]から[Behaviors SDK (XAML)]を参照してください。
f:id:KatsuYuzu:20131213080350p:plain
Microsoft.Xaml.Interactivity名前空間には、あと2つ便利なアトリビュートがあります。DefaultEventAttributeとCustomPropertyValueEditorです。

DefaultEventAttribute

DefaultEventAttributeはEventTriggerBehaviorのEventNameに情報を与えます。
通常、ButtonにActionを設定するとClickイベントに対して設定されます。

<Button Content="トリガーアクション">
	<Interactivity:Interaction.Behaviors>
		<Core:EventTriggerBehavior EventName="Click">
			<Common:MessageNotifyAction/>
		</Core:EventTriggerBehavior>
	</Interactivity:Interaction.Behaviors>
</Button>

DefaultEventAttributeを下記のように設定するとペタっとした時に指定したイベントに対して設定されるようになります。

[DefaultEvent(typeof(Button), "DoubleTapped")]
public class MessageNotifyAction : DependencyObject, IAction
<Button Content="トリガーアクション">
	<Interactivity:Interaction.Behaviors>
		<Core:EventTriggerBehavior EventName="DoubleTapped">
			<Common:MessageNotifyAction/>
		</Core:EventTriggerBehavior>
	</Interactivity:Interaction.Behaviors>
</Button>

TypeConstraintAttributeは1つのクラスに1つですが、DefaultEventAttributeは複数つけられますので、複数の種類のコントロールで動作するアクションを作成したときには適宜設定しておくとポトペタビリティが上がります。

[DefaultEvent(typeof(TextBox), "Loaded")]
[DefaultEvent(typeof(Button), "DoubleTapped")]

CustomPropertyValueEditorAttribute

CustomPropertyValueEditorAttributeはプロパティに特定のエディターの種類を関連付けます。
下記のようなアクションを作りました。

class CustomPropertyActionSample : DependencyObject, IAction
{
    public DependencyObject ElementBinding { get; set; }
    public DependencyObject PropertyBinding { get; set; }
    public string StateName { get; set; }
    public Storyboard Storyboard { get; set; }

    public object Execute(object sender, object parameter)
    {
        throw new NotImplementedException();
    }
}

これをBlendで設定しようとしてもすべて[新規作成]と書かれているだけで意図する設定ができません。
f:id:KatsuYuzu:20131213023718p:plain
CustomPropertyValueEditorAttributeを各プロパティに設定します。

[CustomPropertyValueEditor(CustomPropertyValueEditor.ElementBinding)]
public DependencyObject ElementBinding { get; set; }
[CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
public DependencyObject PropertyBinding { get; set; }
[CustomPropertyValueEditor(CustomPropertyValueEditor.StateName)]
public string StateName { get; set; }
[CustomPropertyValueEditor(CustomPropertyValueEditor.Storyboard)]
public Storyboard Storyboard { get; set; }

そうするとBlendのパワーが発揮できます。このCustomPropertyValueEditorAttributeに関してはビヘイビアーやアクションに限らず、Converterやコントロールのカスタマイズの際にも使用できるので覚えておいてください。
f:id:KatsuYuzu:20131213024154p:plain

おまけ

Blendではデザイナにガイドを出すことができます。Officeにもついているアレです。
方法は簡単です。ルーラーからマウスをドラッグするだけです。何本でも出すことができます。もちろんスナップもききますよ。
f:id:KatsuYuzu:20131213075532p:plain
そしてメニューバーの[表示]からガイドの表示、非表示、管理が行えます。
f:id:KatsuYuzu:20131213075840p:plain
こういう趣旨と関係ないようなTipsって発信、共有されないので意外と知らなかったりしますよね。

まとめ

Microsoft.Xaml.Interactivityのアトリビュートを使うことでBlendデザイナにフレンドリーなポトペタビリティの高いビヘイビアー、トリガーアクションを作れます。もろんXAMLの古参であるWPFやSilverlightは同様のことができます。Windows ストアアプリは8.1でビヘイビアーが待望の復活をとげて開発しやすくなりましたね。あとはBlendの機能がVisual Studioに統合されたらいいなーと思ってます。
明日のアドベントカレンダーは縄神様と呼ばれている@twit_ahfさんです。