[Silverlight入门系列]使用MVVM模式(1):MVVM核心概念
MVVM模式是Model、View、ViewModel的简称,最早出现在WPF,现在Silverlight中也使用该模式,MVVM模式是对MVC模式的变种。哪儿变了?我认为MVVM和MVC的主要变化在于MVVM更适合于XAML。
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处
1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model不可以不变,当Model变化的时候View也可以不变。
2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
如果用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的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
3. Model
- 数据和业务逻辑
- 客户端领域模型
- 由data entities, business objects, repositories and services构成
- 可以实现属性或集合的change notification
- 可以实现validation 接口例如 IDataErrorInfo
View和ViewModel主要通过数据绑定和Command/Behavior进行交互,如下图所示:
有关Model(模型)和DTO的问题
前面说的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”》
代码例:
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的,否则需要检查其中的坏味道。
http://www.cnblogs.com/Mainz/archive/2011/05/03/2035558.html
---------------------------------------------------------------------------------------------------------------------------------------------------------------
[Silverlight入门系列]使用MVVM模式(2):集合Model /ObservableCollection/ICollectionView
ObservableCollection
INotifyCollectionChanged接口。ICollectionView接口在Silverlight下有个子类:PagedCollectionView,在WPF下有个子类:ListCollectionView.
----------------------------------------------------------------------------------------------------------------------------
[Silverlight入门系列]使用MVVM模式(3):Model的INotifyPropertyChanged接口实现
当绑定的属性改变时,它可以通知客户端,并进行界面数据更新。而我们不用写很多复杂的代码来更新界面数据,这样可以做到方法简洁而清晰,松耦合和让方法变得更通用。可用的地方太多了:例如上传进度,实时后台数据变更等地方。
-------------------------------------------------------------------------------------------------------------------------
[Silverlight入门系列]使用MVVM模式(4):Prism的NotificationObject自动实现INotifyPropertyChanged接口
---------------------------------------------------------------------------------------------------------------------------------------------------------------
[Silverlight入门系列]使用MVVM模式(2):集合Model /ObservableCollection/ICollectionView
INotifyCollectionChanged在上一篇讲了《[Silverlight入门系列]使用MVVM模式(1):MVVM核心概念 》,今天讲一下集合Model。所谓集合Model就是给ItemsControl,如DataGrid,Combobox等绑定的集合数据模型。另外,通过定制ItemsControl的数据模板(DataTemplate),还可以控制集合对象中每一项的显示。实现很简单,但数据源集合对象必须继承IEnumerable接口,为了让目标属性与数据源集合的更新(不但包括元素的修改,还包括元素的增加和删除)保持同步,数据源集合还必须实现INotifyPropertyChanged接口和INotifyCollectionChanged接口。
在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接口,同样继承了
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接口
------------------------------------------------------------------------------------------------------------------------------------------------------------- [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