DataAnnotationsを利用してデータ検証を行う

 WPFでデータ検証を行う為の方法を紹介してきましたが、今回はデータ検証をサポートしてくれるSystem.ComponentModel.DataAnnotationsについて紹介します。
 System.ComponentModel.DataAnnotationsにはデータ検証用の属性とデータ検証用属性を使用したデータ検証機構が用意されています。
 データ検証用の属性には以下の様なものがあります。
・RangeAttribute:値の範囲を指定します
・RegularExpressionAttribute:正規表現によるパターンマッチ
・RequiredAttribute:必須(nullや空文字ではないことを示します)
・StringLengthAttribute:文字列の長さ(最大長/最低長)を指定します
 以下のサンプルコードはINotifyDataErrorInfo + DataAnnotationsによるデータ検証の例です。
 (IDataErrorInfoを使っても同じようにデータ検証が可能です。)
using Livet;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.CompilerServices;

namespace Validation4.ViewModels
{
    public class MainWindowViewModel : ViewModel, INotifyDataErrorInfo
    {
        private Dictionary<string, string[]> Errors = new Dictionary<string, string[]>();

        public MainWindowViewModel()
        {
            Value1 = 1;
            Value2 = "a";
        }

        #region Value1変更通知プロパティ
        private int _Value1;

        [Range(1, 10, ErrorMessage="1から10の数値を入力してください。")]
        public int Value1
        {
            get
            { return _Value1; }
            set
            { 
                if (_Value1 == value)
                    return;
                _Value1 = value;
                RaisePropertyChanged();
                UpdateErrors(value);
            }
        }
        #endregion


        #region Value2変更通知プロパティ
        private string _Value2;

        [Required(ErrorMessage="値を入力してください。")]
        public string Value2
        {
            get
            { return _Value2; }
            set
            { 
                if (_Value2 == value)
                    return;
                _Value2 = value;
                RaisePropertyChanged();
                UpdateErrors(value);
            }
        }
        #endregion

        // エラー情報が変更された際に発生するイベント
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        // ErrorsChangedイベントを発生させる
        protected virtual void RaiseErrorsChanged([CallerMemberName]string propertyName = "")
        {
            var h = this.ErrorsChanged;
            if (h != null)
            {
                h(this, new DataErrorsChangedEventArgs(propertyName));
            }
        }

        // エラーメッセージを取得する
        public System.Collections.IEnumerable GetErrors(string propertyName)
        {
            if (string.IsNullOrWhiteSpace(propertyName)) return null;
            if (!Errors.ContainsKey(propertyName)) return null;
            return Errors[propertyName];
        }

        // エラーの有無
        public bool HasErrors
        {
            get { return Errors.Values.Any(x => x != null); }
        }

        // エラー情報を更新する。
        private void UpdateErrors(object value, [CallerMemberName]string propertyName = "")
        {
            if (string.IsNullOrWhiteSpace(propertyName)) return;

            var results = new List<ValidationResult>();

            if (Validator.TryValidateProperty(
                value,
                new ValidationContext(this, null, null) { MemberName = propertyName },
                results))
            {
                // エラー無し
                Errors[propertyName] = null;
            }
            else
            {
                Errors[propertyName] = results.Select(x => x.ErrorMessage).ToArray();
            }

            RaiseErrorsChanged(propertyName);
        }
    }
}
 基本はINotifyDataErrorInfoを使用したデータ検証と同じです。
 プロパティが変更された際にデータを検証し、エラーがある場合はErrorsにエラーメッセージを格納します。
 また、ErrorsChangedイベントを発生させてViewにエラー情報の変更を通知します。
 DataAnnotationsを利用したデータ検証では、データ検証を行うプロパティに検証用の属性を付けます。
 Value1には値の範囲を指定するRangeAttributeを、Value2には入力を必須にするためのRequiredAttributeを指定しています。複数の検証用属性を付けることも可能です。
 値の検証はValidatorクラスを使用して行います。このサンプルコードでは値の検証とエラー変更通知を行うUpdateErrorsというメソッドを作成し、そこでデータ検証を行っています。
 CallerMemberName属性を使用して、UppdateErrorsメソッドが呼ばれたメンバー(プロパティ名)を取得するようにしています。
 ValidatorクラスのTryValidatePropertyメソッドを使用して値の検証を行います。
 第1引数には検証する値を、第2引数には条件(検証内容)を、第3引数には検証結果の入れ物を指定します。
 検証内容の情報は、以下のコードで取得しています。
new ValidationContext(this, null, null) { MemberName = propertyName },
 検証の結果、エラーがある場合はfalseを返します。
 エラー情報はresultsに格納されるので、そこからエラーメッセージを取り出しています。

 View側のコードは前回までに紹介した方法と同じです。
<Window x:Class="Validation4.Views.MainWindow"
        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:ei="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
        xmlns:v="clr-namespace:Validation4.Views"
        xmlns:vm="clr-namespace:Validation4.ViewModels"
        Title="MainWindow" Height="350" Width="525">
    
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>

    <Window.Resources>
        <ControlTemplate x:Key="ErrorTemplate">
            <DockPanel>
                <ItemsControl DockPanel.Dock="Right" Margin="5,0"
                              ItemsSource="{Binding ElementName=adornedElement, Path=AdornedElement.(Validation.Errors)}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding ErrorContent}" Foreground="Red"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>

                <Border BorderBrush="Red" BorderThickness="1"
                        Width="{Binding ElementName=adornedElement, Path=ActualWidth}"
                        Height="{Binding ElementName=adornedElement, Path=ActualHeight}">
                    <AdornedElementPlaceholder Name="adornedElement"/>
                </Border>
            </DockPanel>
        </ControlTemplate>
    </Window.Resources>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="50"/>
        </Grid.ColumnDefinitions>
        
        <TextBlock Grid.Row="0" Grid.Column="0" Text="数値:"/>
        <TextBox Grid.Row="0" Grid.Column="1"
                 Text="{Binding Value1, UpdateSourceTrigger=PropertyChanged}"
                 Validation.ErrorTemplate="{StaticResource ErrorTemplate}"/>
        
        <TextBlock Grid.Row="1" Grid.Column="0" Text="文字列:"/>
        <TextBox Grid.Row="1" Grid.Column="1"
                 Text="{Binding Value2, UpdateSourceTrigger=PropertyChanged}"
                 Validation.ErrorTemplate="{StaticResource ErrorTemplate}"/>
    </Grid>
</Window>
 このコードを実行し、エラーが出るように値を入力すると以下の様になります。
データ検証4
 データ検証用の属性を複数指定することもできますが、エラーが出現した時点で検証を終了する場合もあるようです。
 そのため、本当なら2つエラーメッセージが出るはずなのに1つしかエラーメッセージが表示されないこともあります。
 今回はDataAnnotationsに用意されている属性を使用しましたが、オリジナルの検証用属性を作成することもできます。
 次回はオリジナルの検証用属性の作り方を紹介します。
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

カレンダー
10 | 2017/11 | 12
- - - 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 - -
全記事表示リンク

全ての記事を表示する

カテゴリ
タグリスト

月別アーカイブ
11  09  08  07  06  05  04  03  02  01  12  11  10  09  08  07  06  04  03  02  01  12  11  10  09  08  07  06  05  04  03  02  01  12  11  10  09 
最新記事
リンク
最新コメント
検索フォーム
Amazon