2013年12月19日星期四

[Silverlight入门系列]使用MVVM模式

[Silverlight入门系列]使用MVVM模式(1):MVVM核心概念


MVVM模式是Model、View、ViewModel的简称,最早出现在WPF,现在Silverlight中也使用该模式,MVVM模式是对MVC模式的变种。哪儿变了?我认为MVVM和MVC的主要变化在于MVVM更适合于XAML。

2011-05-03 14h43_20

MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处

1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model不可以不变,当Model变化的时候View也可以不变。
2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
mvvm 2011-07-02 15h23_34
如果用Interface接口来表达,基本就是这么个意思:
复制代码
1 public interface IView 2 { 3 IViewModel ViewModel { get; set; } 4 } 5 6  public interface IViewModel 7 { 8 IModel Model { get; set; } 9 10 /// <summary> 11 /// a property that states the controller is busy doing something (like fetching data from a service), 12 /// usually the iterface should be blocked 13 /// </summary> 14   bool IsBusy { get; } 15 }
复制代码
MVVM_1

MVVM的Model、View、ViewModel分工

1. View
  • 负责界面和显示,界面构成元素有window, controls, page, dataTemplete, custom controls….
  • 代码通常有XAML和XAML.CS组成,但后台代码应该很少
  • 通过DataContext和ViewModel绑定
  • 不直接和Model交互!
  • 控件可以和ViewModel的公共属性绑定,update需要双向绑定
  • 控件可以触发Behavior/Command调用ViewModel的方法,Command是View到ViewModel的单向通讯 (View中触发事件,ViewModel中处理事件)
2. ViewModel
  • 主要包括界面逻辑和模型数据封装,Behavior/Command事件响应,绑定的属性定义等
  • ViewModel继承Model类,或者是Model的继承类
  • 是view和model的桥梁,是对Model的抽象,例如,model中数据格式是“年月日”,可以在viewModel中转换model中的数据为“日月年”以供视图(view)显示。
  • 维护视图状态
  • 实现属性或集合的change notification
  • 2011-07-02 15h21_12
3. Model
  • 数据和业务逻辑
  • 客户端领域模型
  • 由data entities, business objects, repositories and services构成
  • 可以实现属性或集合的change notification
  • 可以实现validation 接口例如 IDataErrorInfo
MVVM
View和ViewModel主要通过数据绑定和Command/Behavior进行交互,如下图所示:
2011-05-03 10h24_31
一个例子并且附代码下载(Command未示例)

有关Model(模型)和DTO的问题

2011-05-03 17h33_31
前面说的Model是客户端的,但实际上Domail Model存在服务器端(靠近数据库)和那就需要和客户端搞映射DTO(Data Transfer Ojbect,数据传输对象,带序列化标记,用来远程调用)。在Silverlight中有个很方便的东西来实现这个DTO过程和序列化,那就是WCF RIA Service和DomainService。如果你创建一个简单的Silverlight应用并且调用WCF RIA Service,基本上会生成DTO Model: ObjectContext(EntityObject)。(也有人喜欢在Model里面调用RiaSerivce实现load,save等等,个人认为不太合适,可以参考这篇文章),此外,参考这篇文章:《WCF RIA Services and a guide to use DTO/”Presentation Model”
DataFlow
代码例:
复制代码
public partial class MyModelsEntities : ObjectContext { … } [EdmEntityTypeAttribute(NamespaceName="MyModels", Name="MyEntity")] [Serializable()] [DataContractAttribute(IsReference=true)] public partial class MyEntity: EntityObject { … }
复制代码

MVVM的实践要点

1. View分离要彻底,不要有坏味道
视图(view)部分,xaml.cs 应该只有很少的代码或没有代码,如果你的xaml.cs包含大量的代码,那么很可能你MVVM用的不对头,需要检查其中代码的坏味道。Xaml和xaml.cs 只能包含处理界面、视图、显示样式、视图元素之间的交互、视图元素动画,等等的内容
2. ViewModel要可测试
从重构的观点看,如果你的代码中ViewModel是可测试的,有详细的单元测试Unit Test,你的代码是OK的,否则需要检查其中的坏味道。
MVVM Basic
更多MVVM的内容且看下回分解。(附:本例子且附代码下载(Command未示例),channel9有一个MVVM的视频很好,PPT和视频演示的源码
http://www.cnblogs.com/Mainz/archive/2011/05/03/2035558.html

---------------------------------------------------------------------------------------------------------------------------------------------------------------
[Silverlight入门系列]使用MVVM模式(2):集合Model /ObservableCollection/ICollectionView
INotifyCollectionChanged在上一篇讲了《[Silverlight入门系列]使用MVVM模式(1):MVVM核心概念 》,今天讲一下集合Model。所谓集合Model就是给ItemsControl,如DataGrid,Combobox等绑定的集合数据模型。另外,通过定制ItemsControl的数据模板(DataTemplate),还可以控制集合对象中每一项的显示。实现很简单,但数据源集合对象必须继承IEnumerable接口,为了让目标属性与数据源集合的更新(不但包括元素的修改,还包括元素的增加和删除)保持同步,数据源集合还必须实现INotifyPropertyChanged接口和INotifyCollectionChanged接口。
ObservableCollection
在Silverlight中创建数据源集合可以使用内建的ObservableCollection类,因为ObservableCollection类既实现了INotifyPropertyChanged接口,又实现了INotifyCollectionChanged接口。使用ObservableCollection类不但可以实现Add、Remove、Clear和Insert操作,还可以触发PropertyChanged事件。
复制代码
 1     using System.Collections.ObjectModel; 2     using System.Collections; 3 
 4     public class MyModelCollection : ObservableCollection<MyModel>
 5     { 6         public MyModelCollection() 7         { 8         } 9 
10         public MyModelCollection(IEnumerable MyModels)11         {12             foreach (MyModel h in MyModels)13                 this.Add(h);14         }15 
16         public void AddRange(IEnumerable MyModels)17         {18             foreach (MyModel h in MyModels)19                 this.Add(h);20         }21     }22 
23     public class MyModel24     {25         public int ModelID { get; set; }26         public string ModelName { get; set; }27     }28 
29     public class MyViewModel30     {31         public MyModelCollection MyModelDataCollection { get; set; }32         public MyViewModel(MyModelCollection modelCollection)33         {34             MyModelDataCollection = modelCollection;35         }36     }
复制代码
集合Model绑定到ItemControl也很简单:
复制代码
 1         private MyViewModel viewModel; 2 
 3         public MainPage() 4         { 5             InitializeComponent(); 6 
 7             MyModel m1 = new MyModel() { ModelID = 1, ModelName = "abc" }; 8             MyModel m2 = new MyModel() { ModelID = 2, ModelName = "def" }; 9 
10             viewModel = new MyViewModel(new MyModelCollection(new List<MyModel>() { m1, m2 }));11 
12             this.DataContext = viewModel;13 
14             this.dataGrid1.ItemsSource = viewModel.MyModelDataCollection;15             this.dataGrid1.AutoGenerateColumns = true;16 
17             this.comboBox1.ItemsSource = viewModel.MyModelDataCollection;18             this.comboBox1.DisplayMemberPath = "ModelName";19             this.comboBox1.SelectedValuePath = "ModelID";20 
21             this.comboBox1.SelectionChanged += new SelectionChangedEventHandler(comboBox1_SelectionChanged);22         }23 
24         void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)25         {26             this.textBlock1.Text = "Selected value: " + this.comboBox1.SelectedValue;27         }
复制代码
ICollectionView / PagedCollectionView
虽然上述的ObservableCollection很好用而且继承了INotifyPropertyChanged接口,有些时候我们需要对集合进行排序、过滤、分页等操作,这个时候就需要用ICollectionView接口,同样继承了
INotifyCollectionChanged接口。ICollectionView接口在Silverlight下有个子类:PagedCollectionView,在WPF下有个子类:ListCollectionView.
PagedCollectionView例子:
C#:
复制代码
 1 public class MyViewModel : INotifyPropertyChanged 2 { 3     public ICollectionView Customers { get; private set; } 4 
 5     public MyViewModel( ObservableCollection<Customer> customers ) 6     { 7         // Initialize the CollectionView for the underlying model 8         // and track the current selection.
 9          Customers = new PagedCollectionView( customers );10         Customers.CurrentChanged +=
11                            new EventHandler( SelectedItemChanged );12     }13 
14     private void SelectedItemChanged( object sender, EventArgs e )15     {16         Customer current = Customers.CurrentItem as Customer;17         ...18     }19 }
复制代码
XAML:
复制代码
1 <ListBox ItemsSource="{Binding Path=Customers}">
2     <ListBox.ItemTemplate>
3         <DataTemplate>
4             <StackPanel>
5                 <TextBlock Text="{Binding Path=Name}"/>
6             </StackPanel>
7         </DataTemplate>
8     </ListBox.ItemTemplate>
9  </ListBox>
复制代码
http://www.cnblogs.com/Mainz/archive/2011/05/04/2036680.html

----------------------------------------------------------------------------------------------------------------------------
[Silverlight入门系列]使用MVVM模式(3):Model的INotifyPropertyChanged接口实现


当客户端绑定一个数据模型以后,数据模型变化以后可以自动通知客户端更新界面显示,这就是INotifyPropertyChanged接口要做的工作。INotifyPropertyChanged 接口用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知例如,考虑一个带有名为FirstName 属性的 Person 对象。 若要提供一般性属性更改通知,则 Person 类型实现 INotifyPropertyChanged 接口并在 FirstName 更改时引发PropertyChanged 事件。 若要在将客户端与数据源进行绑定时发出更改通知,则绑定类型应具有下列功能:实现INotifyPropertyChanged接口。
 当绑定的属性改变时,它可以通知客户端,并进行界面数据更新。而我们不用写很多复杂的代码来更新界面数据,这样可以做到方法简洁而清晰,松耦合和让方法变得更通用。可用的地方太多了:例如上传进度,实时后台数据变更等地方。
(另:对于集合Model:INotifyCollectionChanged结接口会有ObservableCollection自动实现(见上一篇))
一个简单的例子:(下面的例子中TextBlock绑定了DataContext的ModelName,点击按钮以后model更新,TextBlock会自动更新)
Model: 
复制代码
1 public class MyModel : INotifyPropertyChanged 2 { 3 public event PropertyChangedEventHandler PropertyChanged; 4 5 public int ModelID { get; set; } 6 7 private string _ModelName; 8 public string ModelName 9 {10 get { return _ModelName; }11 set 12 {13 _ModelName = value; 14 15 if (PropertyChanged != null)16 {17 PropertyChanged(this, new PropertyChangedEventArgs("ModelName"));18 }19 }20 }21 }
复制代码

Silverlight页面: 
复制代码
1 <Grid x:Name="LayoutRoot" Background="White"> 2 <TextBlock Height="46" HorizontalAlignment="Left" Margin="187,51,0,0" Name="textBlock1" Text="{Binding ModelName}" VerticalAlignment="Top" Width="94" /> 3 <Button Content="update" Height="39" HorizontalAlignment="Left" Margin="187,120,0,0" Name="button1" VerticalAlignment="Top" Width="106" Click="button1_Click" /> 4  </Grid>
复制代码

页面代码: 
复制代码
1 public MyView() 2 { 3 InitializeComponent(); 4 5 MyModel m1 = new MyModel() { ModelID = 1, ModelName = "abc" }; 6 7 this.DataContext = m1; 8 } 9 10  private void button1_Click(object sender, RoutedEventArgs e)11 {12 (this.DataContext as MyModel).ModelName = "abc_changed";13 }
复制代码
甚至还有一个VS2010插件来完成这个工作:NotifyPropertyWeaver
ViewModel也可以实现INotifyPropertyChanged接口,很简单:
XAML绑定集合:
1 <DataGrid ItemsSource="{Binding Path=LineItems}" />

ViewModel代码:
复制代码
1 public class OrderViewModel : INotifyPropertyChanged 2 { 3 public OrderViewModel( IOrderService orderService ) 4 { 5 this.LineItems = new ObservableCollection<OrderLineItem>( 6 orderService.GetLineItemList() ); 7 } 8 9 public ObservableCollection<OrderLineItem> LineItems { get; private set; }10 }
复制代码

-------------------------------------------------------------------------------------------------------------------------
[Silverlight入门系列]使用MVVM模式(4):Prism的NotificationObject自动实现INotifyPropertyChanged接口


上一篇写了Model的INotifyPropertyChanged接口实现,在Prism中有一个NotificationObject自动实现了这个接口,位于Microsoft.Practices.Prism.ViewModel命名空间下。也就是说,Prism推荐ViewModel继承这个NotificationObject类来自动实现INotifyPropertyChanged接口。看看NotificationObject都有啥:

复制代码
1 public abstract class NotificationObject : INotifyPropertyChanged2 {3       protected NotificationObject();4 
5       protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression);6       protected void RaisePropertyChanged(params string[] propertyNames);7       protected virtual void RaisePropertyChanged(string propertyName);8 }
复制代码
提供了几个很方面的接口,调用更方便了,例如:
复制代码
 1 public string ModelName 2 { 3        get { return _ModelName; } 4        set
 5        { 6             _ModelName = value;  7 
 8             RaisePropertyChanged("ModelName"); 9                 10        }11  }
复制代码
第二个RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression);是一个表达式,编译为一个Func委托,返回一个T类型。
例如可以这样用:
1 this.RaisePropertyChanged(() => this.MyDataSummary);
ViewModel的INotifyPropertyChanged接口和Model的INotifyPropertyChanged接口
ViewModel和Model它们二者都实现INotifyPropertyChanged接口并不矛盾。用途不一样。例如一个ViewModel可以包含多个其它的ViewModel,而它们有一个整体的HasChanged属性来标识是否有改变。这个时候这个整体的ViewModel的HasChanged属性就可以用整体的INotifyPropertyChanged,而局部的INotifyPropertyChanged实现了这个整体的INotifyPropertyChanged。看个例子:
复制代码
 1 using Microsoft.Practices.Prism.ViewModel; 2 
 3  public class MyViewModel3: NotificationObject 4 { 5         public MyModel MyModelData { get; set; } 6         public MyModel2 MyModelData2 { get; set; } 7 
 8         public bool HasChanges { get; set; } 9         public bool CanSave { get; set; }10 
11         public MyViewModel3(MyModel model, MyModel2 model2)12         {13             MyModelData = model;14             MyModelData2 = model2;15 
16             model.PropertyChanged += this.OnPropertyChanged;17         }18 
19         private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)20         {21             if (args.PropertyName == "Name")22             {23                 this.HasChanges = true;24                 this.RaisePropertyChanged(() => this.CanSave);25             }26         }27 }28 
29  public class MyModel2 : INotifyPropertyChanged30 {31         public event PropertyChangedEventHandler PropertyChanged;32 
33         public int ModelID { get; set; }34 
35         private string _ModelName;36         public string ModelName37         {38             get { return _ModelName; }39             set
40             {41                 _ModelName = value;42 
43                 if (PropertyChanged != null)44                 {45                     PropertyChanged(this, new PropertyChangedEventArgs("ModelName"));46                 }47             }48         }49 }50 
51 public class MyModel : INotifyPropertyChanged   52 {53         public event PropertyChangedEventHandler PropertyChanged;   54 
55         public int ModelID { get; set; }56 
57         private string _ModelName;58         public string ModelName59         {60             get { return _ModelName; }61             set
62             {63                 _ModelName = value; 64 
65                 if (PropertyChanged != null)66                 {67                     PropertyChanged(this, new PropertyChangedEventArgs("ModelName"));68                 }69             }70    }71 }
复制代码
此外,Validation既可以放在Model里面也可以放在ViewModel里面,看你的规则是否简单,是否涉及业务逻辑,有的复杂的业务逻辑validation的需要调用后台service的建议放到ViewModel中去做。
http://www.cnblogs.com/Mainz/archive/2011/05/04/2036855.html

------------------------------------------------------------------------------------------------------------------------------------------------------------- [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 INotifyDataErrorInfo2 {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 AccountID10     {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 HasErrors49     {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 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             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 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             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 = errorMsg71                              });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 AsycValidation14 {15     public partial class MainPage : UserControl16     {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 : DomainService15     {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调用。
http://www.cnblogs.com/Mainz/archive/2011/05/05/2037480.html