30
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

WPFのビヘイビア

Posted at

#ビヘイビアとは

言葉の意味は「振る舞い」。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でファイルダイアログを使用する

30
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?