自作ビヘイビアを作ろう その2

せっかくなのでよく使いそうなビヘイビアをもう一つ書いてみた。

NotifyOnValidationErrorBehavior クラス
    public class NotifyOnValidationErrorBehavior : Behavior<FrameworkElement>
    {
        private static readonly Brush _errorBrush = new SolidColorBrush(Colors.Red);
        private readonly EventHandler<ValidationErrorEventArgs> _errorHandler;
        private Brush _saveForgroundBrush;
        private Brush _saveBorderBrush;

        public NotifyOnValidationErrorBehavior()
        {
            _errorHandler = (s, e) =>
            {
                var control = e.OriginalSource as Control;

                if (control == null) return;

                if (e.Action == ValidationErrorEventAction.Added)
                {
                    _saveForgroundBrush = control.Foreground;
                    _saveBorderBrush = control.BorderBrush;
                    control.Foreground = _errorBrush;
                    control.BorderBrush = _errorBrush;
                    control.FontWeight = FontWeights.Bold;
                }
                else if (e.Action == ValidationErrorEventAction.Removed)
                {
                    control.Foreground = _saveForgroundBrush;
                    control.BorderBrush = _saveBorderBrush;
                    control.FontWeight = FontWeights.Normal;
                }
            };
        }
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.BindingValidationError += _errorHandler;
        }
        protected override void OnDetaching()
        {
            AssociatedObject.BindingValidationError -= _errorHandler;
            base.OnDetaching();
        }
    }

前回同様ビヘイビアが関係オブジェクトに接続された際にBindingValidationErrorイベントに用意したハンドラを挿しこんでやる訳だが※、やっている処理自体は単純で、

1.ValidationErrorEventArgsクラスのプロパティActionの値がValidationErrorEventAction.Addedであれば、なんらかのエラーが発生していると判断し、エラーが発生したオブジェクト(OriginalSource)がControlであれば、ボーダーと、テキストのブラシの色を赤くして、フォントのウェイトをボールドにする。

2.その逆にValidationErrorEventAction.Removed、つまりエラーが解消されたならばエラー発生時に変更されたブラシとフォントのウェイトを元に戻している。

前回と大きく違うのが型パラメタにTextBoxではなく、ControlでもなくFrameworkElementを指定していること。Silverlightベースのプラットホームはイベントがバブルアップするため、全てのコントロールのイベントを上位のコンテナ・コントロールで捕捉することができるため、今回のようなビヘイビアは個々のコントロールに設定する必要が無く、制御下とするコントロールのルートになっているオブジェクト(大抵はGridやStackPanel)に一つだけ配置すれば良いのだ。

実行結果

この描画、どこかで見たことがあるだろうか。そう、Silverlightで標準的に実装されているエラー処理と似たものである。Windows Phone 7は例外を投じることでBindingValidationErrorイベントを発生させることができるものの、それをビューに反映する処理が無いので自ら用意する必要があるのだが、それを簡単に実現できないだろうかと考えてみた。

このビヘイビアを使用するにはコントロール(XAMLエレメント)がBindingValidationErrorイベントを投じることができることが条件だが、それに対応するにはデータバインドを記述するBindingプロパティの拡張マークアップでNotifyOnValidationError=trueを指定するだけで良い。

<TextBox 〜x:Name="textBox1" Text="{Binding Name, Mode=TwoWay, NotifyOnValidationError=True}" 〜>

今回もビヘイビアに少量のコードを書いただけで、あとはBlend4でポトペタするだけである。 簡単且つスマートだ。

※ビヘイビアを処理コンポーネントとして好きな位置に挿入していくのは、ASPECT-JのようなAOPに近い感覚がある。