`

[Silverlight入门系列]使用MVVM模式(5):异步Validation数据验证和INotifyDataErrorInfo接口

    博客分类:
  • MVVM
阅读更多

数据验证(Validation)是界面程序的常见需求,例如使用正则表达式验证用户输入的Email地址是否合法,然后在界面给出错误提示信息。在Sivlerlight的MVVM模式中,我们在Model和ViewModel可以做Validation,然后需要把Model和ViewModel的Validation结果和错误信息通知视图(View)。在WPF中,我们使用IDataErrorInfo,在Silverlight4中,建议使用INotifyDataErrorInfo

IDataErrorInfo

先简单说一下IDataErrorInfo,这个接口实现了简单的数据验证和错误报告功能,只能说聊胜于无吧。例子:

 

1 <TextBox Text="{Binding Path=CurrentEmployee.Name, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True }" />

 

INotifyDataErrorInfo

这个接口只有Silverlight4以上支持,非常强大,支持一个绑定属性多重错误、异步数据验证、自动通知视图错误信息、ErrorChanged事件、HasErrors属性、GetErrors方法等等。定义:

 

1 public interface INotifyDataErrorInfo 2 { 3 bool HasErrors { get; } 4 5 event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; 6 7 IEnumerable GetErrors(string propertyName); 8 }

 

实现这个INotifyDataErrorInfo接口也非常简单,来个简单的例子:

 

1 public class SimpleModel : INotifyDataErrorInfo 2 { 3 public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; 4 5 private Dictionary<string, List<String>> _errors = new Dictionary<string, List<String>>(); 6 7 private string _accountID = null; 8 9 public string AccountID 10 { 11 get { return _accountID; } 12 set 13 { 14 if (_accountID != value) 15 { 16 var propertyName = "AccountID"; 17 18 if (string.IsNullOrEmpty(value)) 19 { 20 if (!_errors.ContainsKey(propertyName)) 21 _errors.Add(propertyName, new List<string>()); 22 23 _errors[propertyName].Add("AccountID can't be null or empty"); 24 } 25 else 26 { 27 if (_errors.ContainsKey(propertyName)) 28 _errors.Remove(propertyName); 29 } 30 31 NotifyErrorsChanged(propertyName); 32 33 //Maybe you don't want to set this field to a value if the validation fails 34 _accountID = value; 35 } 36 } 37 38 } 39 40 public System.Collections.IEnumerable GetErrors(string propertyName) 41 { 42 if (_errors.ContainsKey(propertyName)) 43 return _errors[propertyName]; 44 45 return _errors.Values; 46 } 47 48 public bool HasErrors 49 { 50 get { return _errors.Count > 0; } 51 } 52 53 54 private void NotifyErrorsChanged(string propertyName) 55 { 56 if (ErrorsChanged != null) 57 ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName)); 58 } 59 }

 

异步Validation数据验证和INotifyDataErrorInfo接口

这个例子稍微复杂,实现了异步调用WCF RIA Service进行业务逻辑的validation并在ViewModel中把验证的错误提示通知视图,完整的代码下载,需要VS2010和Silverlight环境。

代码说明 ViewModel基类:

 

1 using System; 2 using System.Net; 3 using System.Windows; 4 using System.Linq; 5 using System.Windows.Controls; 6 using System.Windows.Documents; 7 using System.Windows.Ink; 8 using System.Windows.Input; 9 using System.Windows.Media; 10 using System.Windows.Media.Animation; 11 using System.Windows.Shapes; 12 using System.ComponentModel; 13 using System.Collections.Generic; 14 using System.Collections; 15 16 namespace AsycValidation 17 { 18 public class BasicViewModel : INotifyPropertyChanged, INotifyDataErrorInfo 19 { 20 public event PropertyChangedEventHandler PropertyChanged; 21 public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; 22 23 24 private Dictionary<string, List<ValidationErrorInfo>> _errors = 25 new Dictionary<string, List<ValidationErrorInfo>>(); 26 27 28 protected void RemoveErrorFromPropertyAndNotifyErrorChanges( 29 string propertyName, 30 int errorCode) 31 { 32 if (_errors.ContainsKey(propertyName)) 33 { 34 RemoveErrorFromPropertyIfErrorCodeAlreadyExist(propertyName, errorCode); 35 36 NotifyErrorsChanged(propertyName); 37 } 38 } 39 40 private void RemoveErrorFromPropertyIfErrorCodeAlreadyExist( 41 string propertyName, 42 int errorCode) 43 { 44 if (_errors.ContainsKey(propertyName)) 45 { 46 var errorToRemove = _errors[propertyName].SingleOrDefault( 47 error => error.ErrorCode == errorCode); 48 49 if (errorToRemove != null) 50 { 51 _errors[propertyName].Remove(errorToRemove); 52 53 54 55 56 if (_errors[propertyName].Count == 0) 57 _errors.Remove(propertyName); 58 } 59 } 60 } 61 protected void AddErrorToPropertyAndNotifyErrorChanges( 62 string propertyName, 63 ValidationErrorInfo errorInfo) 64 { 65 RemoveErrorFromPropertyIfErrorCodeAlreadyExist(propertyName, errorInfo.ErrorCode); 66 if (!_errors.ContainsKey(propertyName)) 67 _errors.Add(propertyName, new List<ValidationErrorInfo>()); 68 69 _errors[propertyName].Add(errorInfo); 70 71 NotifyErrorsChanged(propertyName); 72 } 73 74 75 public IEnumerable GetErrors(string propertyName) 76 { 77 if (!_errors.ContainsKey(propertyName)) 78 return _errors.Values; 79 80 return _errors[propertyName]; 81 } 82 83 84 public bool HasErrors 85 { 86 get { return this._errors.Count > 0; } 87 } 88 89 90 private void NotifyErrorsChanged(string propertyName) 91 { 92 if (ErrorsChanged != null) 93 ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName)); 94 } 95 96 97 protected void NotifyPropertyChanged(string propertyName) 98 { 99 if (PropertyChanged != null) 100 PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 101 } 102 103 } 104 }

 

Model:

 

1 using System; 2 using System.Net; 3 using System.Windows; 4 using System.Windows.Controls; 5 using System.Windows.Documents; 6 using System.Windows.Ink; 7 using System.Windows.Input; 8 using System.Windows.Media; 9 using System.Windows.Media.Animation; 10 using System.Windows.Shapes; 11 using System.ComponentModel; 12 13 namespace AsycValidation 14 { 15 public class CompanyModel : INotifyPropertyChanged 16 { 17 public event PropertyChangedEventHandler PropertyChanged; 18 19 public int CompanyID { get; set; } 20 21 private string _CompanyName; 22 public string CompanyName 23 { 24 get { return _CompanyName; } 25 set 26 { 27 _CompanyName = value; 28 29 if (PropertyChanged != null) 30 { 31 PropertyChanged(this, new PropertyChangedEventArgs("CompanyName")); 32 } 33 } 34 } 35 } 36 }

 

ViewModel,继承了BaseViewModel基类:

 

1 using System; 2 using System.Net; 3 using System.Windows; 4 using System.Windows.Controls; 5 using System.Windows.Documents; 6 using System.Windows.Ink; 7 using System.Windows.Input; 8 using System.Windows.Media; 9 using System.Windows.Media.Animation; 10 using System.Windows.Shapes; 11 using AsycValidation.Web; 12 13 namespace AsycValidation 14 { 15 public class CompanyViewModel : BasicViewModel 16 { 17 public CompanyModel CompanyModelData { get; set; } 18 19 public CompanyViewModel(CompanyModel model) 20 { 21 CompanyModelData = model; 22 } 23 24 private string _CompanyName = null; 25 private const int ACCOUNT_ALREADY_EXIST_ERROCODE = 100; 26 27 DomainService1 service = new DomainService1(); 28 29 public string CompanyName 30 { 31 get 32 { 33 return _CompanyName; 34 } 35 set 36 { 37 if (_CompanyName != value) 38 { 39 var propertyName = "CompanyName"; 40 41 ValidateAccountAlreadyExists( 42 value, 43 propertyName, 44 ACCOUNT_ALREADY_EXIST_ERROCODE, 45 string.Format("Company with the ID {0} already exists", value)); 46 47 _CompanyName = value; 48 NotifyPropertyChanged(propertyName); 49 } 50 } 51 } 52 53 private void ValidateAccountAlreadyExists( 54 string CompanyID, 55 string propertyName, 56 int errorCode, 57 string errorMsg) 58 { 59 service.DoesCompanyExists( 60 CompanyID, 61 invokeOperation => 62 { 63 if (invokeOperation.Value) 64 { 65 AddErrorToPropertyAndNotifyErrorChanges( 66 propertyName, 67 new ValidationErrorInfo() 68 { 69 ErrorCode = errorCode, 70 ErrorMessage = errorMsg 71 }); 72 } 73 else 74 { 75 RemoveErrorFromPropertyAndNotifyErrorChanges( 76 propertyName, 77 errorCode); 78 } 79 }, 80 null); 81 } 82 83 } 84 }

 

View / XAML

 

1 <UserControl x:Class="AsycValidation.MainPage" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:wm="clr-namespace:AsycValidation" 7 mc:Ignorablecolor: #0000f
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics