Prism.Mvvmを使ってみる・その2 ~ErrorsContainerの使い方~
↓の続きです。
Prism.Mvvmを使ってみる - SourceChord
Prism.Mvvmには、ErrorsContainerというINotifyDataErrorInfo実装のためのヘルパークラスがあります。
今回はこれを使ってみます。
INotifyDataErrorInfo実装時に、発生中のエラー情報を保持するためにDictionary型のメンバを用意したりします。これを簡単に扱えるようにするためのクラスがErrorsContainerです。
使い方
1. ErrorsContainerのフィールド作成
こんな風に、保持したいエラー情報の型を指定して、ErrorsContainerのフィールドを作ります。
private ErrorsContainer<string> errorsContainer;
2. コンストラクタで、ErrorsContainerのインスタンス作成
引数として、INotifyDataErrorInfoのErrorsChangedイベントを発行するためのメソッドを渡します。
こうすると、エラー内容に変化があったときに、ErrorsChangedイベントを通知してくれるようになります。
public MainWindowViewModel() { errorsContainer = new ErrorsContainer<string>(OnErrorsChanged); }
3. INotifyDataErrorInfoの実装
INotifyDataErrorInfoを実装します。
ErrorsContainerを使って、以下のようにスッキリと実装します。
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; public System.Collections.IEnumerable GetErrors(string propertyName) { return this.errorsContainer.GetErrors(propertyName); } public bool HasErrors { get { return this.errorsContainer.HasErrors; } }
4. 入力値検証用のメソッド作成
DataAnnotationsの機能を使って以下のような入力値の検証用メソッドを作ります。
(System.ComponentModel.DataAnnotationsの参照が必要になります。)
protected void ValidateProperty(object value, [CallerMemberName]string propertyName = null) { var context = new ValidationContext(this) { MemberName = propertyName }; var validationErrors = new List<ValidationResult>(); if (!Validator.TryValidateProperty(value, context, validationErrors)) { var errors = validationErrors.Select(error => error.ErrorMessage); this.errorsContainer.SetErrors(propertyName, errors); } else { this.errorsContainer.ClearErrors(propertyName); } }
5. プロパティに入力値検証のための実装を行う
DataAnnotationsのRequired属性を使って、プロパティが空の時に警告を出すようにしてます。
また、setterでは4.で作ったValidatePropertyメソッドを呼んで入力値の検証をするようにします。
private string name; [Required(ErrorMessage = "何か入力してください")] public string Name { get { return name; } set { this.SetProperty(ref this.name, value); this.ValidateProperty(value); } }
サンプル・コード
MainWindow.xaml
<Window x:Class="PrismTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Width="300" Height="200"> <Window.Resources> <ControlTemplate x:Key="ValidationTemplate"> <StackPanel> <TextBlock Foreground="Red" Text="{Binding AdornedElement.(Validation.Errors)[0].ErrorContent, ElementName=adornedelem}" /> <AdornedElementPlaceholder x:Name="adornedelem" /> </StackPanel> </ControlTemplate> </Window.Resources> <StackPanel> <TextBlock Margin="20,10" HorizontalAlignment="Left" Text="{Binding Message}" TextWrapping="Wrap" /> <TextBox Width="120" Height="23" Margin="20,10" HorizontalAlignment="Left" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" TextWrapping="Wrap" Validation.ErrorTemplate="{StaticResource ValidationTemplate}" /> <Button Width="75" Margin="20,10" HorizontalAlignment="Left" Command="{Binding ShowMessageCommand}" Content="Button" /> </StackPanel> </Window>
MainWindow.xaml.cs
/// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new MainWindowViewModel(); } }
MainWindowViewModel.cs
class MainWindowViewModel : BindableBase, INotifyDataErrorInfo { private string name; [Required(ErrorMessage = "何か入力してください")] public string Name { get { return name; } set { this.SetProperty(ref this.name, value); this.ValidateProperty(value); } } private string message; public string Message { get { return message; } set { this.SetProperty(ref this.message, value); } } private DelegateCommand showMessageCommand; public DelegateCommand ShowMessageCommand { get { return showMessageCommand = showMessageCommand ?? new DelegateCommand(ShowMessage); } } private void ShowMessage() { this.Message = string.Format("こんにちは、 {0} さん", this.Name); } public MainWindowViewModel() { errorsContainer = new ErrorsContainer<string>(OnErrorsChanged); this.Name = string.Empty; } protected void ValidateProperty(object value, [CallerMemberName]string propertyName = null) { var context = new ValidationContext(this) { MemberName = propertyName }; var validationErrors = new List<ValidationResult>(); if (!Validator.TryValidateProperty(value, context, validationErrors)) { var errors = validationErrors.Select(error => error.ErrorMessage); this.errorsContainer.SetErrors(propertyName, errors); } else { this.errorsContainer.ClearErrors(propertyName); } } #region INotifyDataErrorInfoの実装 private ErrorsContainer<string> errorsContainer; protected void OnErrorsChanged([CallerMemberName]string propertyName = null) { var handler = this.ErrorsChanged; if (handler != null) { handler(this, new DataErrorsChangedEventArgs(propertyName)); } } public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; public System.Collections.IEnumerable GetErrors(string propertyName) { return this.errorsContainer.GetErrors(propertyName); } public bool HasErrors { get { return this.errorsContainer.HasErrors; } } #endregion }