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

数据验证(Validation)是界面程序的常见需求,例如使用正则表达式验证用户输入的Email地址是否合法,然后在界面给出错误提示信息。在Sivlerlight的MVVM模式中,我们在Model和ViewModel可以做Validation,然后需要把Model和ViewModel的Validation结果和错误信息通知视图(View)。在WPF中,我们使用IDataErrorInfo,在Silverlight4中,建议使用INotifyDataErrorInfo。关于这个接口怎么使用,如何实现ErrorProvider的功能,如何做DataForm的Validatio,请参考我的旧一篇

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环境。

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

代码说明 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:Ignorable ="d"
8 d:DesignHeight ="300" d:DesignWidth ="400" >
9
10 < Grid Name ="Layout" >
11 < TextBlock Height ="32" HorizontalAlignment ="Left" Margin ="41,53,0,0" Name ="textBlock1" Text ="Company:" VerticalAlignment ="Top" Width ="66" />
12 < TextBox Height ="31" HorizontalAlignment ="Left" Margin ="120,45,0,0" Name ="textBox1" Text =" {Binding CompanyName, Mode=TwoWay, NotifyOnValidationError=True} " VerticalAlignment ="Top" Width ="119" />
13 < TextBox Height ="30" HorizontalAlignment ="Left" Margin ="120,104,0,0" Name ="textBox2" VerticalAlignment ="Top" Width ="119" />
14 < Button Content ="Button" Height ="36" HorizontalAlignment ="Left" Margin ="120,156,0,0" Name ="button1" VerticalAlignment ="Top" Width ="81" />
15 </ Grid >
16 </ UserControl >

XAML.CS

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Net;
5 using System.Windows;
6 using System.Windows.Controls;
7 using System.Windows.Documents;
8 using System.Windows.Input;
9 using System.Windows.Media;
10 using System.Windows.Media.Animation;
11 using System.Windows.Shapes;
12
13 namespace AsycValidation
14 {
15 public partial class MainPage : UserControl
16 {
17 public MainPage()
18 {
19 InitializeComponent();
20
21 CompanyModel m1 = new CompanyModel() { CompanyID = 1 , CompanyName = " abc " };
22
23 companyViewModel = new CompanyViewModel(m1);
24 this .DataContext = companyViewModel;
25 }
26
27 public CompanyViewModel companyViewModel { get ; set ; }
28 }
29 }

WCF Ria Service:

1 namespace AsycValidation.Web
2 {
3 using System;
4 using System.Collections.Generic;
5 using System.ComponentModel;
6 using System.ComponentModel.DataAnnotations;
7 using System.Linq;
8 using System.ServiceModel.DomainServices.Hosting;
9 using System.ServiceModel.DomainServices.Server;
10
11
12 // TODO: Create methods containing your application logic.
13 [EnableClientAccess()]
14 public class DomainService1 : DomainService
15 {
16 [Invoke]
17 public bool DoesCompanyExists( string companyID)
18 {
19 if (companyID == " 12345 " )
20 return true ;
21
22 return false ;
23 }
24 }
25 }

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

主要示例了MVVM的INotifyDataErrorInfo接口和INotifyPropertyChanged接口,异步Validation, WCF Ria Service调用。

你可能感兴趣的:(silverlight)