読者です 読者をやめる 読者になる 読者になる

i++

プログラム系のメモ書きなど

WPF/XAML : TextBox の入力内容を検証して不正入力の場合にエラーを表示する

C# WPF/XAML

ポイント

  • TextBox.Text に Binding.ValidationRules で Validation のルールを設定する
  • TextBox.ErrorTemplate で不正入力時の表示をカスタマイズできる
    • デフォルトではテキストボックスに赤枠表示
    • AdornedElementPlaceholder の中に元のテキストボックスが含まれている

f:id:tkyjhr:20150809172329p:plain

このスクリーンショットのような表示をするサンプルを以下に示します。

全体の xml

最低限エラーがわかることが良いだけであれば、ErrorTemplate も Style も設定不要なので Windows.Resources 部分の定義はいりません。

TextBox.Text 内の Binding で Path と UpdateSourceTrigger を定義セットし、ValidationRules の中に自作の ValidationRule を継承したクラスを入れるだけで、検証失敗時の赤枠表示は有効になります。

<Window.Resources>
    <!-- エラー時にテキストボックスの右の方に ! を表示するテンプレート -->
    <ControlTemplate x:Key="ExclamationOnError">
        <Grid>
            <!-- FontSize を元のテキストボックスから取得するために名前をつけて Binding -->
            <AdornedElementPlaceholder x:Name="TextBox"/>
            <TextBlock Text="! " HorizontalAlignment="Right" Foreground="Red"
                       FontSize="{Binding ElementName=TextBox, Path=AdornedElement.FontSize}"/>
        </Grid>
    </ControlTemplate>

    <!-- Validation がエラーを返した際にその内容をツールチップを表示するスタイル -->
    <Style x:Key="TextBoxHasError" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <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>
</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    
    <!-- ErrorTemplate に 上で定義した Template を設定 -->
    <TextBox Width="100" Grid.Row="0" Grid.Column="0" Margin="8"
             Validation.ErrorTemplate="{StaticResource ExclamationOnError}"
             Style="{StaticResource TextBoxHasError}">
        <TextBox.Text>
            <Binding Path="Text1" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:NumberValidationRule />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

    <!-- 同上 + FrontSize を大きくした版 -->
    <TextBox Width="100" Grid.Row="1" Grid.Column="0" Margin="8" FontSize="36"
             Validation.ErrorTemplate="{StaticResource ExclamationOnError}"
             Style="{StaticResource TextBoxHasError}">
        <TextBox.Text>
            <Binding Path="Text2" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:NumberValidationRule />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

    <!-- ErrorTemplate を設定しない版。赤枠が表示される -->
    <TextBox Width="100" Grid.Row="2" Grid.Column="0" Margin="8"
             Style="{StaticResource TextBoxHasError}">
        <TextBox.Text>
            <Binding Path="Text3" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:NumberValidationRule />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
</Grid>

ValidationRule クラス

class NumberValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        if( ((string)value).Any(c => !char.IsNumber(c)))
        {
            return new ValidationResult(false, "Invalid character.");
        }
        else
        {
            return new ValidationResult(true, null);
        }
    }
}

MainWindow クラス

public partial class MainWindow : Window
{
    public string Text1 { get; set; }
    public string Text2 { get; set; }
    public string Text3 { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        // DataContext = this;
    }
}

参考