Validationの事に触れたのが半年前。ということで前回のエントリはこちら。
http://blogs.wankuma.com/kazuki/archive/2008/02/11/122718.aspx
今回は、Validationクラスの細かいところをちょっと見てみようと思う。とりあえず復習がてら簡単なValidatorをこさえてみようと思う。前に作ったのと同じ偶数のみOKなバリデータ。さくっと実装。
using System.Windows.Controls;
namespace WpfValidation
{
/// <summary>
/// 偶数のみ通すバリデータ
/// </summary>
public class MyValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string inputValue = value as string;
// 入力は文字列だよね常識的に考えて
if (value == null)
{
return new ValidationResult(false, "おかしい");
}
// 数値だよね
int inputNumber = 0;
if (!int.TryParse((string)value, out inputNumber))
{
return new ValidationResult(false, "数値ではありません");
}
// 偶数だよね
if (inputNumber % 2 != 0)
{
return new ValidationResult(false, "偶数ではありません");
}
// OK!偶数だ
return ValidationResult.ValidResult;
}
}
}
Window1.xaml.csは、下のような感じ。DataContextの設定と、ボタンが押されたときに、値を表示するだけ。
using System.Windows;
namespace WpfValidation
{
public partial class Window1 : Window
{
// これにテキストボックスの値をバインドする
public int TargetValue { get; set; }
public Window1()
{
InitializeComponent();
// お試し用なので、自分自身をDataContextにセット
this.DataContext = this;
}
private void AlertButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(this.TargetValue.ToString());
}
}
}
XAML側も、前回とほとんど同じなのでさくさくっといくよ。
<Window x:Class="WpfValidation.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfValidation="clr-namespace:WpfValidation"
Title="Validation" Height="99" Width="248">
<StackPanel>
<TextBox Margin="3">
<TextBox.Text>
<!-- UpdateSourceTriggerをPropertyChangedにしてテキストボックスが
変更されたら即ソースを書き換えるようにする。 -->
<Binding Path="TargetValue" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<!-- 自作のバリデーションルール -->
<WpfValidation:MyValidationRule />
<!-- 何か例外でたらエラーにしてくれるルール -->
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Button Margin="3" Content="Alert" Click="AlertButton_Click" />
</StackPanel>
</Window>
これで、OK。実行してみよう。偶数以外では、赤枠になってる。
Validationプロパティの中身をのぞいてみよう
ということで!バリデーションエラーが起きたときどうなってるのかをちょっと観察してみようと思う。Validation.ErrorsとValidation.HasErrorという添付プロパティがあったりする。その値をAlertを押したときに表示してみようと思う。
まずは、TextBoxにName属性をつけてC#側から触れるようにする。
<TextBox Name="textBox" Margin="3">
次に、AlertのクリックイベントでValidation.ErrorsとValidation.HasErrorを表示するように書き換え。
private void AlertButton_Click(object sender, RoutedEventArgs e)
{
var hasError = Validation.GetHasError(textBox);
var errors = Validation.GetErrors(textBox);
var sb = new StringBuilder();
// 情報を文字列にして表示
sb.AppendFormat("Validation.HasError: {0}\n", hasError);
sb.Append("Validation.Errors: [");
foreach (var error in errors)
{
sb.AppendFormat("{0}, ", error.ErrorContent);
}
sb.Append("]");
MessageBox.Show(sb.ToString());
}
これを実行してみて動きを確認すると、下のような感じになる。
実行直後は、エラーも何も無い。奇数を入力してみると…
Validation.HasErrorがちゃんとTrueになってValidation.Errorsには、エラーの内容が入っている。エラーがあるときだけ、エラーの内容を表示しようとするとなるとStyleのTriggerを使うと出来る。
要は、TriggerでValidation.HasErrorがTrueのときにToolTipにValidation.Errorsの最初の要素のErrorContentを設定してやればいい。Styleの設定だけ抜粋。
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<!-- エラーがあるときにメッセージをToolTipに表示 -->
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
本当にやるときには、共通のStyleとしてくくりだすのがいいだろう。実行してみると、エラーメッセージがToolTipとして表示される。すばらしい。
エラー無し状態
数字じゃないとき
偶数じゃないとき