fc2ブログ

INotifyDataErrorInfoを使用したデータ検証

 これまで例外を使用したデータ検証IDataErrorInfoを使用したデータ検証を行いましたが、今回はWPF4.5で加わったINotifyDataErrorInfoを使用したデータ検証を行ってみます。
 IDataErrorInfoを使用したデータ検証と似ていますが、INotifyDataErrorInfoを使用した場合以下の点が異なります。
・HasErrorプロパティを持っている。
・エラー情報の変更をイベントで通知する。
・1つのプロパティに対して複数のエラーメッセージを返すことができる。
・エラー情報はstring型以外でもOK。

 それでは、ViewModelのサンプルコードを見てみましょう。
using Livet;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;

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

        public MainWindowViewModel()
        {
            Value1 = 0;
            Value2 = "";
        }

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

        public int Value1
        {
            get
            { return _Value1; }
            set
            { 
                if (_Value1 == value)
                    return;
                _Value1 = value;

                // データ検証
                Errors["Value1"] = value < 0 ? new[] { "0以上の数値を入力してください。" } : null;
                RaiseErrorsChanged();   // エラー情報が変更されたことを通知

                RaisePropertyChanged();
            }
        }
        #endregion


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

        public string Value2
        {
            get
            { return _Value2; }
            set
            { 
                if (_Value2 == value)
                    return;
                _Value2 = value;

                // データ検証
                Errors["Value2"] = string.IsNullOrWhiteSpace(value)
                    ? new[] { "値が入力されていません。", "何か入力してください。" }
                    : null;
                RaiseErrorsChanged();

                RaisePropertyChanged();
            }
        }
        #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); }
        }
    }
}
 INotifyDataErrorInfoではErrorsChangedイベントでエラー情報の変更を通知します。
 このイベントではどのプロパティのエラー情報が変更されたかが通知されます。
 エラーメッセージはGetErrorsメソッドによって取得されます。このメソッドの戻り値はIEnumerable型なので、string以外のエラー情報を返すことも可能です。(今回はstring型で返していますが)
 IDataErrorInfoには無かったHasErrorプロパティも実装されています。このプロパティによってエラーの有無を取得することが可能になっています。
 エラー情報の受け取り方がIDataErrorInfoと異なっていますが、データ検証の方法はそれほど変わりません。
 今回のコードも、プロパティのsetterでデータ検証を行い、エラーメッセージをErrorsに格納しています。
 エラー情報が複数あっても良いのでErrorsのValueはstring配列にしています。
 そして、エラー情報が変更されたことを通知するためにErrorsChangedイベントを発生させています。
 今回はValue1では今までと同じように1つのエラーメッセージを返していますが、Value2では試しに2つのエラーメッセージを返しています。

 次にView側のコードです。
<Window x:Class="Validation3.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:Validation3.Views"
        xmlns:vm="clr-namespace:Validation3.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="15"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
        
        <TextBlock Grid.Row="0" Grid.Column="0" Text="数値:"/>
        <TextBox Grid.Row="0" Grid.Column="1"
                 Validation.ErrorTemplate="{StaticResource ErrorTemplate}"
                 Text="{Binding Value1, ValidatesOnNotifyDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
        
        <TextBlock Grid.Row="2" Grid.Column="0" Text="文字列:"/>
        <TextBox Grid.Row="2" Grid.Column="1"
                 Validation.ErrorTemplate="{StaticResource ErrorTemplate}"
                 Text="{Binding Value2, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
</Window>
 INotifyDataErrorInfoの場合もエラーメッセージをエラーテンプレートを使用して表示しますが、エラーメッセージが複数あることが前提なので前回のコードとは少し異なります。
 エラーメッセージが複数あるのでItemsControlを使ってエラーメッセージを表示しています。
 バインディングの際には「ValidatesOnNotifyDataErrors」をtrueにします。ただ、ValidatesOnNotifyDataErrorsはデフォルトでtrueになっているので省略することが可能です。(Value1では説明のために記載していますが、Value2では省略しています)
 このコードを実行し、エラーを発生させると以下の様になります。
データ検証3
 こんな感じで複数のエラーメッセージを表示できます。
 IDataErrorInfoとINotifyDataErrorInfoどちらを使うかは人それぞれだと思うので、使いやすい方を使えばよいかなと思います。

 WPFでデータ検証を行う方法は、これまでに説明した3種類のどれかを使用することになると思います。
 データ検証はModelで行ったほうが良いのではないかと考える人も多いと思います。ただWPFのデータ検証機能が見ての通りViewModelよりなので、若干の違和感を感じますがViewModelでデータ検証を行っています。
 次回はDataAnnotationsを利用して、もう少し楽にデータ検証を行う方法を紹介します。
スポンサーサイト



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

コメントの投稿

非公開コメント

質問させてください。

NineWorks様

いつも参考にさせていただいております。
突然で申し訳ございませんが、こちらの記事を元に検証処理を組み込んでみたのですが、なかなか上手くいかず悩んでいます。

MSDNのフォーラムに質問を挙げていますので、お手隙の時にでも一度見て頂けるとありがたいのですが。。。

不躾なお願いで心苦しいのですが、何かしらご助言でも頂けると助かります。

https://social.msdn.microsoft.com/Forums/ja-JP/44a47df3-fdd0-4bff-9bd0-3b3db5fbff49/controltemplate?forum=wpfja

既に解決済みだった

MSDNフォーラムに行ってみたら既に解決方法が投稿されていました。
なるほど、ControlTemplateでコントロールをいじるときはAdornerDecoratorを使わないとエラーテンプレートが表示されないのか。
勉強になりました。

ありがとうございました

NineWorks様

お騒がせして申し訳ございませんでした。
gekka様より頂いた回答で無事に改善できました。

今後もこちらのブログの更新を楽しみに、いろいろと勉強させていただきます。
カレンダー
10 | 2023/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 - -
全記事表示リンク

全ての記事を表示する

カテゴリ
タグリスト

月別アーカイブ
04  10  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