WPF_IDataErrorInfoインターフェース
IDataErrorInfoインターフェース
IDataErrorInfoインターフェースとは
IDataErrorInfoインターフェースは、バインドしているプロパティの検証エラーを表示する方法を提供します。
最初期のMVVMパターンでは、ViewModelに「INotifyPropertyChanged」と「IDataErrorInfo」の2つのインターフェイスを実装することが求められていました。
さらに、IDataErrorInfo に通知イベントを発生する仕組みを付与した「INotifyDataErrorInfo インターフェイス」がWPF4.5で追加されています。
しかしながら、どちらも使い勝手がいいとはいえなく、有名なエラー機能ライブラリを利用する場合が多いといえます。
System.ComponentModel.IDataErrorInfo
// ユーザー インターフェイスにバインドできるカスタム エラー情報を提供する機能を提供します。
public interface IDataErrorInfo
{
// 指定した名前を持つプロパティのエラー メッセージを取得します。
string this[string columnName] { get; }
// このオブジェクトに関する問題を示すエラー メッセージを取得します。
string Error { get; }
}
Modelで検証してはいけないのか?
MVVMにおいてUI操作(ユーザー入力など)の検証はViewModelで行い、ビジネスロジックの検証ならばModelで行うことを基本ルールとしています。
例えば、ログインフォームを例にすると、入力規則(文字列パターン)判定をViewModelで実施して、パスワード照会をModelで行う方法です。
しかし、セキュリティ観点からはViewModelの検証は脆弱性を生む可能性がありますので、Modelのみで検証処理を行うことは合理的といえます。
IDataErrorInfoインターフェース実装例
xaml
バインディング時に「ValidatesOnDataErrors」をtrueにします。
<Window x:Class="WpfDevelop.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:WpfDevelop"
mc:Ignorable="d"
Title="MainWindow" Height="240" Width="360">
<Window.Resources>
<!--エラーテンプレートの定義-->
<ControlTemplate x:Key="ErrorTemplate">
<DockPanel>
<!--TextBoxの上側にエラーメッセージを表示-->
<TextBlock DockPanel.Dock="Top" Foreground="Red" Margin="0"
Text="{Binding ElementName=adornedElement, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
<!--AdornedElementPlaceholderはErrorTemplateが適用される要素を指す-->
<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="1*"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="1" Margin="0,0,5,0" Text="TextBox(IDataErrorInfo):"/>
<TextBox Grid.Column="1" Grid.Row="1"
Validation.ErrorTemplate="{StaticResource ErrorTemplate}"
Text="{Binding BindText, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
ViewModel
ViewModelにIDataErrorInfoを継承します。
namespace WpfDevelop
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
public class MainWindowViewModel : ViewModelBase, IDataErrorInfo
{
/// <summary>
/// プロパティに紐づいたエラーメッセージを格納します。
/// </summary>
private Dictionary<string, string> Errors = new Dictionary<string, string>();
private int _bindText;
public int BindText
{
get { return _bindText; }
set
{
if (_bindText == value)
{
return;
}
_bindText = value;
// データ検証
Errors["BindText"] = value < 0 ? "0以上の数値を入力してください。" : null;
OnPropertyChanged();
}
}
public string Error
{
get { throw new NotImplementedException(); }
}
/// <summary>
/// columnNameで指定したプロパティのエラーを返します。
/// </summary>
public string this[string columnName]
{
get
{
return Errors.ContainsKey(columnName) ? Errors[columnName] : null;
}
}
}
}
備考
xaml.cs
namespace WpfDevelop
{
using System.Windows;
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainWindowViewModel vm = new MainWindowViewModel();
this.DataContext = vm;
}
}
}
ViewModelBase
namespace WpfDevelop
{
using System.ComponentModel;
using System.Runtime.CompilerServices;
/// <summary>
/// ViewModel 基底クラス を表現します。
/// </summary>
public class ViewModelBase : INotifyPropertyChanged
{
/// <summary>
/// プロパティ値が変更されたことをクライアントに通知します。
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// PropertyChanged イベント を発生させます。
/// </summary>
/// <param name="propertyName">変更されたプロパティの名前</param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
関連ページ
- WPF_ViewModelデータバインディング
- WPF_MVVM構造について
- WPF_INotifyPropertyChangedインターフェース
- WPF_ICommandインターフェース
- WPF_IDataErrorInfoインターフェース
- C#