C#
WPF
Xaml
MVVM

WPFのビヘイビア


ビヘイビアとは

言葉の意味は「振る舞い」。WPFでMVVMに準拠する際、Viewの状態の変化をきっかけにして、Viewで実行される処理の実装方法のことを指す。MVVMに準拠するとコードビハインドが使えないので、その代替手段ということになる。


実装方法


添付プロパティ

添付プロパティの実装に、振る舞いを含めてしまう。


コールバック

添付プロパティは初期化時にコールバックが登録できる。そこに実行したい処理を記述することで、プロパティの値の変更をきっかけとしたビヘイビアが作れる。


添付プロパティのコールバックを使用したビヘイビア

using System.Windows;

namespace Sample {
public class AttachedXXX {
public static DependencyProperty XXXProperty
= DependencyProperty.RegisterAttached(
"XXX",
typeof(bool),
typeof(AttachedXXX),
new PropertyMetadata(XXX_PropertyChanged) //コールバックを登録
);

public static void SetXXX(DependencyObject obj, bool value)
=> obj.SetValue(XXXProperty, value);

public static bool GetXXX(DependencyObject obj)
=> (bool)obj.GetValue(XXXProperty);

//コールバック
private static void XXX_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
if (e.NewValue != null) {
//値の変更に対する振る舞いを記述する
}
}
}
}



XAML

<Window x:Class="Sample.MainView"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Sample"
Title="MainView" Height="300" Width="300">
<Grid>
<TextBox local:AttachedXXX.XXX="{Binding XXX}" />
</Grid>
</Window>

これだけではできることが限られるため、実際は次項の添付ビヘイビアとして利用されることが多い。


添付ビヘイビア

コールバック内でViewのイベントハンドラを登録する。この方法は「添付ビヘイビア」と呼ばれており、ビヘイビアの実装方法としてはメジャーだが、Microsoftのサイトには解説がない。


添付ビヘイビア

using System.Windows;

namespace Sample {
public class AttachedXXX {
public static DependencyProperty XXXProperty
= DependencyProperty.RegisterAttached(
"XXX",
typeof(bool),
typeof(AttachedXXX),
new PropertyMetadata(XXX_PropertyChanged) //コールバックを登録
);

public static void SetXXX(DependencyObject obj, bool value)
=> obj.SetValue(XXXProperty, value);

public static bool GetXXX(DependencyObject obj)
=> (bool)obj.GetValue(XXXProperty);

//コールバック
private static void XXX_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var textBox = d as TextBox;
if (textBox == null) return;

//イベントハンドラの登録
if (e.NewValue != null) {
textBox.PreviewTextInput += TextBox_PreviewTextInput;
} else {
textBox.PreviewTextInput -= TextBox_PreviewTextInput;
}
}

//イベントハンドラ
private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) {
//振る舞い
}
}
}


コールバックでは、イベントハンドラの登録/解除のみ行う。プロパティと振る舞いが連動しないため、何をしているのかわかりにくいコードになる。

この方法は、ビヘイビアがプロパティを必要としない場合には使えない。一方で、プロパティなのでスタイル指定できるというメリットがある。


Blend SDK

Blend SDKのSystem.Windows.Interactivity.dllに用意された専用のクラスを使う。


Behaviorクラス

ビヘイビアを実装するための専用のクラス。このクラスを継承してビヘイビアを実装する。


Behaviorクラスを使用したビヘイビア

using System;

using System.Windows.Controls;
using System.Windows.Interactivity;

namespace Sample {
public class YYYBehavior : Behavior<Control> {
protected override void OnAttached() {
base.OnAttached();

AssociatedObject.Loaded += AssociatedObject_Loaded;
}

protected override void OnDetaching() {
base.OnDetaching();

AssociatedObject.Loaded -= AssociatedObject_Loaded;
}

//イベントハンドラ
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) {
//振る舞い
}
}
}



XAML

<Window x:Class="Sample.MainView"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:Sample"
Title="MainView" Height="300" Width="300">
<Grid>
<Button Content="OK">
<i:Interaction.Behaviors>
<local:YYYBehavior/>
</i:Interaction.Behaviors>
</Button>
</Grid>
</Window>

添付ビヘイビアとは違い、専用のクラスなのでわかりやすいコードになるが、xamlの構造が複雑になるというデメリットがある。また、添付ビヘイビアと違いスタイルでの指定ができない。(工夫すればできるが、xamlがより複雑になる。


TriggerBaseクラスとTriggerActionクラス

今までの方法はきっかけと振る舞いを同時に定義しているが、両者を個別に指定する方法が用意されており、きっかけは「トリガー」、振る舞いは「アクション」と呼ばれる。主なトリガーとアクションが最初から用意されており、これらを組み合わせることで、単純なビヘイビアなら簡単に実現できる。


既存のトリガーとアクションを組み合わせた例

<Window x:Class="Sample.MainView"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:Sample"
Title="MainView" Height="300" Width="300">
<Grid>
<Label Content="OK">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<i:InvokeCommandAction Command="{Binding XXXCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</Window>

自作する場合は、TriggerBaseクラスを継承してトリガーを、TriggerActionクラスを継承してアクションを実装する。

PrismのInteractionRequestTriggerを使えばViewModel側からアクションを実行できるので、MVVMを守りながらViewを操作する手段として重宝する。(例:MVVMでファイルダイアログを使用する