Mvvm-validations

提供:Dev Guides
移動先:案内検索

MVVM –検証

この章では、検証について学習します。 また、WPFバインディングが既にサポートしているがMVVMコンポーネントに結び付けているもので検証を行うためのクリーンな方法も検討します。

MVVMでの検証

  • アプリケーションがエンドユーザーからのデータ入力の受け入れを開始したら、その入力の検証を検討する必要があります。
  • 全体的な要件に準拠していることを確認してください。
  • WPFのバインディングシステムには、入力を検証するための優れたビルドと機能がいくつかありますが、MVVMを実行するときにこれらの機能をすべて活用できます。
  • 検証をサポートし、どのプロパティに存在するルールを定義するロジックは、ビュー自体ではなく、モデルまたはViewModelの一部であることに注意してください。

あなたはまだWPFデータバインディングによってサポートされている検証を表現するすべての方法を使用することができます-

  • プロパティでの例外のスローが設定されます。
  • IDataErrorInfoインターフェイスを実装します。
  • INotifyDataErrorInfoを実装します。
  • WPF検証ルールを使用します。

一般に、INotifyDataErrorInfoが推奨され、WPF .net 4.5に導入され、プロパティに関連付けられたエラーのオブジェクトのクエリをサポートし、他のすべてのオプションのいくつかの欠陥も修正します。 具体的には、非同期検証を許可します。 プロパティに複数のエラーを関連付けることができます。

検証の追加

入力ビューに検証サポートを追加する例を見てみましょう。大規模なアプリケーションでは、おそらくこれをアプリケーションのいくつかの場所に必要とします。 ビュー、ViewModel、およびこれらのヘルパーオブジェクトには、モデルオブジェクトの周りにラッパーが存在する場合があります。

検証サポートを共通の基本クラスに配置して、さまざまなシナリオから継承できるようにすることをお勧めします。

基本クラスはINotifyDataErrorInfoをサポートするため、プロパティが変更されたときに検証がトリガーされます。

ValidatableBindableBaseという新しいクラスを作成します。 プロパティ変更処理の基本クラスが既にあるので、それから基本クラスを派生させ、INotifyDataErrorInfoインターフェイスも実装しましょう。

以下は、ValidateableBindableBaseクラスの実装です。

using System;
using System.Collections.Generic;
using System.ComponentModel;

//using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace MVVMHierarchiesDemo {

   public class ValidatableBindableBase : BindableBase, INotifyDataErrorInfo {
      private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();

      public event EventHandler<DataErrorsChangedEventArgs>
         ErrorsChanged = delegate { };

      public System.Collections.IEnumerable GetErrors(string propertyName) {

         if (_errors.ContainsKey(propertyName))
            return _errors[propertyName];
         else
            return null;
      }

      public bool HasErrors {
         get { return _errors.Count > 0; }
      }

      protected override void SetProperty<T>(ref T member, T val,
         [CallerMemberName] string propertyName = null) {

         base.SetProperty<T>(ref member, val, propertyName);
         ValidateProperty(propertyName, val);
      }

      private void ValidateProperty<T>(string propertyName, T value) {
         var results = new List<ValidationResult>();

        //ValidationContext context = new ValidationContext(this);
        //context.MemberName = propertyName;
        //Validator.TryValidateProperty(value, context, results);

         if (results.Any()) {
           //_errors[propertyName] = results.Select(c => c.ErrorMessage).ToList();
         } else {
            _errors.Remove(propertyName);
         }

         ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
      }
   }
}

次に、それぞれのフォルダーにAddEditCustomerViewとAddEditCustomerViewModelを追加します。 以下は、AddEditCustomerView.xamlのコードです。

<UserControl x:Class = "MVVMHierarchiesDemo.Views.AddEditCustomerView"
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
   xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
   xmlns:local = "clr-namespace:MVVMHierarchiesDemo.Views"
   mc:Ignorable = "d"
   d:DesignHeight = "300" d:DesignWidth = "300">

   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height = "Auto"/>
         <RowDefinition Height = "Auto"/>
      </Grid.RowDefinitions>

      <Grid x:Name = "grid1"
         HorizontalAlignment = "Left"
         DataContext = "{Binding Customer}"
         Margin = "10,10,0,0"
         VerticalAlignment = "Top">

         <Grid.ColumnDefinitions>
            <ColumnDefinition Width = "Auto"/>
            <ColumnDefinition Width = "Auto"/>
         </Grid.ColumnDefinitions>

         <Grid.RowDefinitions>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "Auto"/>
         </Grid.RowDefinitions>

         <Label Content = "First Name:"
            Grid.Column = "0"
            HorizontalAlignment = "Left"
            Margin = "3"
            Grid.Row = "0"
            VerticalAlignment = "Center"/>

         <TextBox x:Name = "firstNameTextBox"
            Grid.Column = "1"
            HorizontalAlignment = "Left"
            Height = "23"
            Margin = "3"
            Grid.Row = "0"
            Text = "{Binding FirstName, ValidatesOnNotifyDataErrors = True}"
            VerticalAlignment = "Center"
            Width = "120"/>

         <Label Content = "Last Name:"
            Grid.Column = "0"
            HorizontalAlignment = "Left"
            Margin = "3"
            Grid.Row = "1"
            VerticalAlignment = "Center"/>

         <TextBox x:Name = "lastNameTextBox"
            Grid.Column = "1"
            HorizontalAlignment = "Left"
            Height = "23"
            Margin = "3"
            Grid.Row = "1"
            Text = "{Binding LastName, ValidatesOnNotifyDataErrors = True}"
            VerticalAlignment = "Center"
            Width = "120"/>

         <Label Content = "Email:"
            Grid.Column = "0"
            HorizontalAlignment = "Left"
            Margin = "3"
            Grid.Row = "2"
            VerticalAlignment = "Center"/>

         <TextBox x:Name = "emailTextBox"
            Grid.Column = "1"
            HorizontalAlignment = "Left"
            Height = "23"
            Margin = "3"
            Grid.Row = "2"
            Text = "{Binding Email, ValidatesOnNotifyDataErrors = True}"
            VerticalAlignment = "Center"
            Width = "120"/>

         <Label Content = "Phone:"
            Grid.Column = "0"
            HorizontalAlignment = "Left"
            Margin = "3"
            Grid.Row = "3"
            VerticalAlignment = "Center"/>

         <TextBox x:Name = "phoneTextBox"
            Grid.Column = "1"
            HorizontalAlignment = "Left"
            Height = "23"
            Margin = "3"
            Grid.Row = "3"
            Text = "{Binding Phone, ValidatesOnNotifyDataErrors = True}"
            VerticalAlignment = "Center"
            Width = "120"/>

      </Grid>

      <Grid Grid.Row = "1">
         <Button Content = "Save"
            Command = "{Binding SaveCommand}"
            HorizontalAlignment = "Left"
            Margin = "25,5,0,0"
            VerticalAlignment = "Top"
            Width = "75"/>

         <Button Content = "Add"
            Command = "{Binding SaveCommand}"
            HorizontalAlignment = "Left"
            Margin = "25,5,0,0"
            VerticalAlignment = "Top"
            Width = "75"/>

         <Button Content = "Cancel"
            Command = "{Binding CancelCommand}"
            HorizontalAlignment = "Left"
            Margin = "150,5,0,0"
            VerticalAlignment = "Top"
            Width = "75"/>
      </Grid>

   </Grid>

</UserControl>

以下は、AddEditCustomerViewModelの実装です。

using MVVMHierarchiesDemo.Model;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMHierarchiesDemo.ViewModel {

   class AddEditCustomerViewModel : BindableBase {

      public AddEditCustomerViewModel() {
         CancelCommand = new MyIcommand(OnCancel);
         SaveCommand = new MyIcommand(OnSave, CanSave);
      }

      private bool _EditMode;

      public bool EditMode {
         get { return _EditMode; }
         set { SetProperty(ref _EditMode, value);}
      }

      private SimpleEditableCustomer _Customer;

      public SimpleEditableCustomer Customer {
         get { return _Customer; }
         set { SetProperty(ref _Customer, value);}
      }

      private Customer _editingCustomer = null;

      public void SetCustomer(Customer cust) {
         _editingCustomer = cust;

         if (Customer != null) Customer.ErrorsChanged -= RaiseCanExecuteChanged;
         Customer = new SimpleEditableCustomer();
         Customer.ErrorsChanged += RaiseCanExecuteChanged;
         CopyCustomer(cust, Customer);
      }

      private void RaiseCanExecuteChanged(object sender, EventArgs e) {
         SaveCommand.RaiseCanExecuteChanged();
      }

      public MyIcommand CancelCommand { get; private set; }
      public MyIcommand SaveCommand { get; private set; }

      public event Action Done = delegate { };

      private void OnCancel() {
         Done();
      }

      private async void OnSave() {
         Done();
      }

      private bool CanSave() {
         return !Customer.HasErrors;
      }
   }
}

次に、SimpleEditableCustomerクラスの実装を示します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMHierarchiesDemo.Model {

   public class SimpleEditableCustomer : ValidatableBindableBase {
      private Guid _id;

      public Guid Id {
         get { return _id; }
         set { SetProperty(ref _id, value); }
      }

      private string _firstName;
      [Required]

      public string FirstName {
         get { return _firstName; }
         set { SetProperty(ref _firstName, value); }
      }

      private string _lastName;
      [Required]

      public string LastName {
         get { return _lastName; }
         set { SetProperty(ref _lastName, value); }
      }

      private string _email;
      [EmailAddress]

      public string Email {
         get { return _email; }
         set { SetProperty(ref _email, value); }
      }

      private string _phone;
      [Phone]

      public string Phone {
         get { return _phone; }
         set { SetProperty(ref _phone, value); }
      }
   }
}

上記のコードをコンパイルして実行すると、次のウィンドウが表示されます。

MVVM Validations MainWindow1

[顧客の追加]ボタンを押すと、次のビューが表示されます。 ユーザーがフィールドを空のままにすると、そのフィールドが強調表示され、保存ボタンが無効になります。

MVVM Validations MainWindow2