俗にValidationと呼ばれていることをSilverlight2でしてみるときどうやるのか?についてみてみようと思う。
因みにWPFでは、ValidationRuleというものを作ってBindingにセットすればOK。詳細は、前に書いたエントリにあります。
[WPF][C#]ついに妥当性検証します
[WPF][C#]ついに妥当性検証します! その2
さて、Silverlight2では?と思ってみてみるとBindingにValidationRulesプロパティも無ければ、ValidationRuleクラスもない。
ということで、WPFと同じ方法では出来ないということになる。
さて、じゃぁどうやるの?ということになるけど、ぱっと思いつく限りだと以下の3つの方法が思いついた。
因みに、試行錯誤して考えた結果なので、正しいとは限りません。もっといい方法があるかも?
こういう方法があるよ!ということを知っている方がいたらコメントなどでフォローいただければ嬉しいです。
- IValueConverterで妥当性検証もしちゃう
- プロパティのsetの部分で値のチェックをしちゃう
- 自分で仕組みをゴリゴリっと作りこんでやる
1は、IValueConveretrの役割的に無いな~といった感じ。2が妥当かな?と思う。3は、そんな元気ないのでパス。ということで、2の方法で簡単なサンプルを作ってみようと思う。
下準備
SilverlightValidationAppという名前でプロジェクトを作成する。そこに、画面に表示するためのデータを保持するPageModelという名前のクラスを作成する。
Bindingを使って画面とバインドすることも考えてINotifyPropertyChangedも実装しておく。
PageModelには、Textという名前のstring型のプロパティを1つ定義した。
using System.ComponentModel;
namespace SilverlightValidationApp
{
// INotifyPropertyChangedのデフォルト実装
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
// 画面のDataContextにセットするクラス
public class PageModel : NotifyPropertyChangedBase
{
#region Textプロパティ
private string _text;
public string Text
{
get
{
return _text;
}
set
{
_text = value;
OnPropertyChanged("Text");
}
}
#endregion
}
}
データの入れ物ができたので、PageのDataContextにコンストラクタで設定しておく。ついでに、DataContextからPageModelにキャストして返すプロパティも追加しておいた。
using System.Windows.Controls;
namespace SilverlightValidationApp
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
DataContext = new PageModel();
}
// 毎回DataContextからキャストするのがめんどくさいのでプロパティを定義
public PageModel Model
{
get { return DataContext as PageModel; }
}
}
}
準備が出来たので、画面の作成にとりかかる。といってもTextBoxとButtonがあるだけのシンプル設計。
Buttonを押すとPageModelのTextの中身をメッセージボックスに出すようにした。これで、Bindingした結果がPageModelに反映されているかどうか確認できるって寸法。
<UserControl x:Class="SilverlightValidationApp.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox x:Name="textBox1"
Grid.Column="0"
Text="{Binding Path=Text,
Mode=TwoWay}" />
<Button x:Name="button1"
Grid.Column="1"
Content="内容確認"
Click="button1_Click" />
</Grid>
</UserControl>
button1_Clickイベントハンドラ
private void button1_Click(object sender, System.Windows.RoutedEventArgs e)
{
// Textプロパティの中身をメッセージボックスで表示
MessageBox.Show(Model.Text ?? "");
}
実行直後に内容確認ボタンを押したところ。空のメッセージボックスが表示される。
テキストボックスに文字を入力して内容確認ボタンを押したところ。ちゃんとテキストボックスに入力した値がDataContextの中身にも反映されている。
入力値チェック処理の追加
今まで作成したアプリケーションに入力値チェックを追加してみる。
今回は、テキストボックスに「ハズレ」と入力したら入力値チェックで弾いてしまおうと思う。
早速実装に入る。PageModelのTextプロパティのsetの部分を書き換えてハズレという文字列が入ってきたらArgumentExceptionをスローする。
#region Textプロパティ
private string _text;
public string Text
{
get
{
return _text;
}
set
{
if (value == "ハズレ")
{
throw new ArgumentException("ハズレは駄目よ");
}
_text = value;
OnPropertyChanged("Text");
}
}
#endregion
これだけでも、一応Textプロパティに「ハズレ」という文字列が入らなくなる。(当然だけど)
じゅげむと入力してボタンを押した後に、ハズレと入力してボタンを押してもじゅげむのままになる。
ArgumentExceptionは何処に行った?っていう話になるけど、デバッグモードで起動すると、ArgumentExceptionはユーザー コードによってハンドルされませんでしたというメッセージが出るけど無視しても先に進んでちゃんと動く。
このArgumentExceptionを補足するためには、以下の3つの手順を踏めばできるようになる。
- BindingのValidatesOnExceptionsをTrueにする
→TwoWayバインディングのターゲットからソースオブジェクトを更新するときに発生した例外をバインディングエンジンがキャッチするようになる
- BindingのNotifyOnValidationErrorをTrueにする
→BindingValidationErrorイベントが発生するようになる
- BindingValidationErrorイベントの処理を書く
→FrameworkElementで定義されているイベントで、バインディング ソースによってデータの妥当性確認エラーが報告されたときに発生するイベント
ということでやっていこうと思う。
TextBoxのTextに設定しているBindingを書き換えるのと、TextBoxにイベントハンドラを1つ定義する。
<TextBox x:Name="textBox1"
Grid.Column="0"
Text="{Binding Path=Text,
Mode=TwoWay,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}"
BindingValidationError="textBox1_BindingValidationError"/>
BindingValidationErrorでは、ValidationErrorEventArgs.Actionの中身に応じて処理を振り分ける。
ValidationErrorEventArgusのActionプロパティはValidationErrorEventAction型で以下のような値を持つ。
- ValidationErrorEventAction.Added
ValidationErrorが発生した
- ValidationErrorEventAction.Removed
ValidationErrorが削除された
つまり、Addedの時にエラーを通知するようにして、Removedでエラーの通知を消せばいいって寸法になる。
今回は、エラーのときはメッセージをツールチップにセットして背景をオレンジにしてみた。
private void textBox1_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
e.Handled = true;
var textBox = (TextBox)e.OriginalSource;
switch (e.Action)
{
case ValidationErrorEventAction.Added:
// バリデーションエラーが発生したとき
ToolTipService.SetToolTip(textBox, e.Error.Exception.Message);
textBox.Background = new SolidColorBrush(Colors.Orange);
break;
case ValidationErrorEventAction.Removed:
// バリデーションエラーが消えたとき
ToolTipService.SetToolTip(textBox, null);
textBox.Background = null;
break;
}
}
これを実行してみると、ハズレと入力してテキストボックスからフォーカスを外すと色がついたりツールチップが出たりするのがわかる。
もちろんハズレ以外のときは大丈夫。
ということで、入力値の検証でした。