在本章中,我们将了解验证.我们还将看一个干净的方法来验证WPF绑定已经支持但是将它绑定到MVVM组件.
MVVM中的验证
当您的应用程序开始接受最终用户的数据输入时,您需要考虑验证该输入.
确保它符合您的总体要求.
WPF在绑定系统中有一些很棒的构建和功能,用于验证输入,您仍然可以利用所有这些功能.做MVVM.
请记住支持验证的逻辑,并定义哪些属性应该是Model或ViewModel的一部分,而不是视图本身.
您仍然可以使用WPF数据绑定支持的所有表达验证方式,包括 :
设置属性上的抛出异常.
实现IDataErrorI nfo接口.
实现INotifyDataErrorInfo.
使用WPF验证规则.
一般情况下,建议使用INotifyDataErrorInfo并将其引入WPF .net 4.5,它支持查询对象以查找与属性相关的错误,并且还修复了所有其他选项的一些缺陷.具体来说,它允许异步验证.它允许属性有多个与之关联的错误.
添加验证
让我们来看一个我们将添加验证的示例支持我们的输入视图,在大型应用程序中,您可能需要在应用程序中的许多位置.有时在View上,有时在ViewModel上,有时在这些辅助对象上有模型对象周围的包装.
将验证支持放在一个公共基类中是一个很好的做法,然后你可以继承它来自不同场景.
基类将支持INotifyDataErrorInfo,以便在属性更改时触发该验证.
创建添加一个名为ValidatableBindableBase的新类.由于我们已经有了一个属性更改处理的基类,让我们从中派生基类并实现INotifyDataErrorInfo接口.
以下是ValidatableBindableBase类的实现.
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> _errors = new Dictionary >(); public event EventHandler 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 (ref T member, T val, [CallerMemberName] string propertyName = null) { base.SetProperty (ref member, val, propertyName); ValidateProperty(propertyName, val); } private void ValidateProperty (string propertyName, T value) { var results = new List (); //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的代码.
以下是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); } } } }
编译并执行上述代码时,您将看到以下窗口.
当您按添加客户按钮时,您将请参阅以下视图.当用户将任何字段留空时,它将突出显示并且保存按钮将被禁用.