[Silverlight入门系列]使用MVVM模式(5):异步Validation数据验证和INotifyDataErrorInfo接口
数据验证(Validation)是界面程序的常见需求,例如使用正则表达式验证用户输入的Email地址是否合法,然后在界面给出错误提示信息。在Sivlerlight的MVVM模式中,我们在Model和ViewModel可以做Validation,然后需要把Model和ViewModel的Validation结果和错误信息通知视图(View)。在WPF中,我们使用IDataErrorInfo,在Silverlight4中,建议使用INotifyDataErrorInfo。
IDataErrorInfo
先简单说一下IDataErrorInfo,这个接口实现了简单的数据验证和错误报告功能,只能说聊胜于无吧。例子:
?
代码说明 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 AsycValidation14 {15 public class CompanyModel : INotifyPropertyChanged16 {17 public event PropertyChangedEventHandler PropertyChanged;18 19 public int CompanyID { get; set; }20 21 private string _CompanyName;22 public string CompanyName23 {24 get { return _CompanyName; }25 set26 {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 AsycValidation14 {15 public class CompanyViewModel : BasicViewModel16 {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 CompanyName30 {31 get32 {33 return _CompanyName;34 }35 set36 {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 = errorMsg71 });72 }73 else74 {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