简单的介绍下WPF中的MVVM框架
最近在研究学习Swift,苹果希望它迅速取代复杂的Objective-C开发,引发了一大堆热潮去学它,放眼望去各个培训机构都已打着Swift开发0基础快速上手的招牌了。不过我觉得,等同于无C++基础上手学习C#一样,即使将来OC被淘汰,那也是N年之后的事情,如果真的要做IOS开发,趁现在Swift才刚开始,花那么几个月去了解一下OC绝对是一件有帮助的事情。
扯远了,我前几天刚接触到一个叫做mvvm的框架,发现很有意思,带着学习的态度来写点东西,不足之处一起研究。还有一个很重要的原因,我发现不少同学学习WPF的时候的思维和Windows Form是完全一样的,也就是说为什么用WPF而不用WinForm对于他们来说仅仅是:WPF的UI好看!
下面是WPF的一个简介:
Windows Presentation Foundation(WPF)是美国微软公司推出.NET Framework 3.0及以后版本的组成部分之一,它是一套基于XML、.NET Framework、矢量绘图技术的展示层开发框架,微软视其为下一代用户界面技术。
WPF使用一种新的XAML(eXtensible Application Markup Language)语言来开发界面,这将把界面开发以及后台逻辑很好的分开,降低了耦合度,使用户界面设计师与程序开发者能更好的合作,降低维护和更新的成本。
注意上面的说明,XAML的使用就是为了降低耦合度。那么我们不得不说说WinForm和WPF的区别了。
1.WinForm更新UI的操作是通过后台操作UI名,即ID来完成的。WPF是通过数据Binding来实现UI更新的。
2.WinForm响应用户操作的方式是事件Event。WPF是通过命令(Command)Binding的方式。
所以说,从你写的第一个WPF的Hello World开始,你就要转变思路了!而不是很多人做的那种给按钮添加事件,点击触发然后抱怨和过去的Winform没啥区别一点都不好用。。。
上面又扯远了。
好了,开始写第一个MVVM框架。
定义一个NotificationBase类,这个类的作用是实现了INotifyPropertyChanged接口,目的是绑定数据属性。实现了它这个接口,数据属性发生变化后才会去通知UI。如果想在数据属性发生变化前知道,需要实现INotifyPropertyChanging接口。
1 using System.ComponentModel; 2 3 namespace mvvw框架示例 4 { 5 class NotificationBase : INotifyPropertyChanged 6 { 7 public event PropertyChangedEventHandler PropertyChanged; 8 9 public void RaisePropertyChanged(string propertyName) 10 { 11 if (this.PropertyChanged != null) 12 { 13 this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 14 } 15 } 16 } 17 }
OK,下一步。
同样实现了数据属性的可Binding,用来响应用户UI操作的命令也该有了。定义DelegateCommand类。目的是绑定命令属性。这个类的作用是实现了ICommand接口,WPF中实现了ICommand接口的类,才能作为命令绑定到UI。
using System.Windows.Input; namespace mvvw框架示例 { class DelegateCommand : ICommand { //A method prototype without return value. public Action<object> ExecuteCommand = null; //A method prototype return a bool type. public Func<object, bool> CanExecuteCommand = null; public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { if (CanExecuteCommand != null) { return this.CanExecuteCommand(parameter); } else { return true; } } public void Execute(object parameter) { if (this.ExecuteCommand != null) this.ExecuteCommand(parameter); } public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) { CanExecuteChanged(this, EventArgs.Empty); } } } }
开始定义Model类。一个属性成员"Now Time",它就是数据属性,继承自NotificationBase类,所以数据属性具有通知功能,它改变后,会知道通知UI更新。一个方法“PrintNowTime”,用来改变属性“Now Time”的值,它通过命令的方式相应UI事件。
namespace mvvw框架示例 { class Model : NotificationBase { private string _NowTime = "Now Time"; public string NowTime { get { return _NowTime; } set { _NowTime = value; this.RaisePropertyChanged("Now Time"); } } public void PrintNowTime(object obj) { this.NowTime += "Now Time :" + System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + System.Environment.NewLine; } } }
定义ViewModel类。定义了一个命令属性"PrintCmd",聚合了一个Model对象"model"。这里的关键是,给PrintCmd命令指定响应命令的方法是model对象的“PrintNowTime”方法。
using System; namespace mvvw框架示例 { class ViewModel { public DelegateCommand PrintCmd { get; set; } public Model model { get; set; } public ViewModel() { this.model = new Model(); this.PrintCmd = new DelegateCommand(); this.PrintCmd.ExecuteCommand = new Action<object>(this.model.PrintNowTime); } } }
定义View。Binding一个model中的NowTime属性到TextBlock的Text和PrintCmd命令到按钮的Command。
<Window x:Class="mvvw框架示例.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <StackPanel VerticalAlignment="Center" > <TextBlock Text="{Binding model.NowTime}" Height="208" TextWrapping="WrapWithOverflow"></TextBlock> <Button Command="{Binding PrintCmd}" Height="60" Width="100">Print Time</Button> </StackPanel> </Grid> </Window>
上面的{Binding Model.NowTime}也可以写成{Binding Path = NowTime},是一个意思,前者更直观。
最后需要在MainWindowx.xaml.cs中加上this.DataContext = new ViewModel();表明MainWindowx.xaml这个UI层的数据和命令指定为从ViewModel获得。
运行结果: