MVVM
对于.NET平台的开发人员,托微软的福分我们拥有一种更为强大的模型---MVVM。这应该算是做WPF/Silverlight应用的人必懂的一种结构,WPF/silverlight天生支持数据绑定和命令绑定(不过sl在命令绑定上还比较弱),这就为我们使用MVVM创造了可能。
View是什么呢,纯的View只有xaml或者附带必要的只与View本身相关逻辑代码。ViewModel,你可以把它理解为View具体呈现内容所依赖数据的一个抽象,在MVVM中View与ViewModel总会有一种绑定关系,一旦ViewModel中被绑定的数据发生改变View上的数据就会跟着变,相反也有可能,比如你的账号密码框内容发生变化,关联的ViewModel中的数据就会被框架自动通知到。
在wpf/silverlight中,绑定是通过xaml语法来完成(虽然你可以选择用c#来写但不符合mvvm的宗旨),并且绑定双方的通知机制是有框架来完成,也就是说一个会xaml和blend的美工只需事先和coder商量下“咱们的xx和xx是在哪个ViewModel上叫XXX的属性的XXX属性……”问题之后就可以各干各的了。那么ViewModel怎么写,咋view中又怎么绑定到viewmodel呢?首先我们谈ViewModel。
说道ViewModel你需要知道依赖属性和依赖对象的概念,这是wpf/silverlight的基础所以不多说。有两种方式写ViewModel。第一种是自己去实现INotifyPropertyChanged接口,并在属性变化时去调用NotifyPropertyChanged事件。
为了方便我们定义一个ViewModelBase的抽象基类,然后让其他ViewModel继承这个基类。
public abstract class ViewModelBase : System.ComponentModel.INotifyPropertyChanged, IDisposable { public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { var arg = new System.ComponentModel.PropertyChangedEventArgs(propertyName); PropertyChanged(this, arg); } } public virtual void Dispose() { } }
public class DemoViewModel : ViewModelBase { #region fields private string _propertyA; #endregion #region presentation properties public string PropertyA { get { return _propertyA; } set { if (_propertyA != value) { _propertyA = value; base.OnPropertyChanged("PropertyA"); } }
} #endregion
}
第二种是利用DependencyObject和DependencyProperty。
using System; using System.Windows; using System.ComponentModel; namespace FCClient.AppCode { public class PeopleItemViewModel : DependencyObject, IPeopleItemViewModel { public PeopleItemViewModel() { } public static readonly DependencyProperty SimpleUserDataProperty
= DependencyProperty.Register("SimpleUserData", typeof(SimpleUserData), typeof(PeopleItemViewModel)); public static readonly DependencyProperty RelativeSimpleUserDataProperty
= DependencyProperty.Register("RelativeSimpleUserData", typeof(ObservableCollection<SimpleUserData>), typeof(PeopleItemViewModel)); public static readonly DependencyProperty AllSimpleUserDataProperty
= DependencyProperty.Register("AllSimpleUserData", typeof(ObservableCollection<SimpleUserData>), typeof(PeopleItemViewModel)); public SimpleUserData SimpleUserData { get { return (SimpleUserData)base.GetValue(SimpleUserDataProperty); } set { if (!base.CheckAccess()) { Dispatcher.Invoke(new Action( () => { SimpleUserData = value; })); } else base.SetValue(SimpleUserDataProperty, value); } } public ObservableCollection<SimpleUserData> RelativeSimpleUserData { get { return (ObservableCollection<SimpleUserData>)base.GetValue(RelativeSimpleUserDataProperty); } set { if (!base.CheckAccess()) { Dispatcher.Invoke(new Action( () => { RelativeSimpleUserData = value; })); } else { base.SetValue(RelativeSimpleUserDataProperty, value); var collectionView = CollectionViewSource.GetDefaultView(value); collectionView.SortDescriptions.Add(new SortDescription("Distance", ListSortDirection.Ascending)); } } } public ObservableCollection<SimpleUserData> AllSimpleUserData { get { return (ObservableCollection<SimpleUserData>)base.GetValue(AllSimpleUserDataProperty); } set { if (!base.CheckAccess()) { Dispatcher.Invoke(new Action( () => { AllSimpleUserData = value; })); } else { base.SetValue(AllSimpleUserDataProperty, value); var collectionView = CollectionViewSource.GetDefaultView(value); collectionView.SortDescriptions.Add(new SortDescription("Distance", ListSortDirection.Ascending)); } } } } }
在View中绑定ViewModel。
为了方便,我们可以在app.xaml中将需要的viewmode放到全局资源字典中。
然后再我们的vs视图设计器Properties(中文版显示的是“属性”)页上选择为绑定源设置绑定目标(包括source和path等)以及必要的值转换器等等即可。