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

代码说明 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调用。

Powered By D&J (URL:http://www.cnblogs.com/Areas/)
原文地址:https://www.cnblogs.com/Areas/p/2169878.html