Windows phone 8 MVVM模式.docx
- 文档编号:17979463
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:27
- 大小:26.66KB
Windows phone 8 MVVM模式.docx
《Windows phone 8 MVVM模式.docx》由会员分享,可在线阅读,更多相关《Windows phone 8 MVVM模式.docx(27页珍藏版)》请在冰点文库上搜索。
Windowsphone8MVVM模式
MVVM
全称是ModelViewViewModel,MVVM可以很好的把逻辑和UI分离,有利于维护和自动化测试,传统的Model一般保存一些对象类,比如CCar这样的,保存几个简单的属性。
而View负责显示相关的内容,而ViewModel和Controller一样仍然主要负责逻辑和UI之间的联系,对于DotNetFramework引入的数据绑定概念对于MVVM是十分关键的。
1.Model
CCar类,包含了Color、Brand这样的属性,可以set和get。
2.View
一般为显示布局文件xxx.xaml
3.ViewModel
一般处理逻辑和UI的更新,比如逻辑类通过实现INotifyPropertyChanged接口来更新UI在WP7中。
如果你掌握了MFC的视图方式,精通MVC这样的模式,理解MVVM就十分轻松了,整体来看WP7的设计较Android来说多出的数据绑定可能对于一些新手会有些麻烦。
MVVM还比较复杂,Zune123今后会用更多的篇幅来讲解WP7中的这种全新移动应用开发方式。
一、View
View视图层
就是xaml文件主要就是界面的设计xaml.cs文件只有默认生成的代码,MVVM模式一般不用将页面的数据初始化,事件处理的代码写在xaml.cs上
数据初始化可以通过数据绑定来实现事件的处理也是通过绑定Command来实现
先添加资源这里是放在App.xaml上加载
--这里是将你写好的ViewModel放到了程序资源里面去,接下来的绑定将会用到的-->
GlobalViewModelLocatorxmlns: vm="clr-namespace: MyProject.ViewModels" x: Key="Locator"/> MyProject.ViewModels是命名空间GlobalViewModelLocator则是你写好的ViewModel类资源的key用Locator来表示 当然你也可以通过在具体的页面来实现上加载ViewModel作为资源 在xaml页面上就可以绑定ViewModel了 如 PhoneApplicationPage …… DataContext="{BindingMainViewModel,Source={StaticResourceLocator}}" …… "> 这样就绑定了之前定义的ViewModel资源啦 然后呢你就可以在页面的控件上绑定到ViewModel的定义好的数据和Command命令了(有些项目会将Command与ViewModel完完全全分开工作,个人觉得Command放在ViewModel进行初 始化和调用会更加好一些,也就是说所有的绑定无论是数据或者是Command命令都是放在ViewModel) 如 Text="{BindingItem.AverageRating}" Margin="0,0,8,0" FontSize="24" VerticalAlignment="Center"/> 这是绑定了ViewModel的数据 Interaction.Triggers> TapTrigger> EventToCommandCommand="{BindingTapCommand}"PassEventArgsToCommand="True"/> TapTrigger> Interaction.Triggers> 这是绑定了ViewModel的Command命令这个是使用了MVVMLighttoolkit框架的写法 二、ViewModel ViewModel是视图模型层这一层是负责了对View视图层展现以及各种事件的处理灰常灰常重要 实现ViewModel层的方法很多 可以通过继承INotifyPropertyChanged接口实现继承ViewModelBase基类来实现继承IEnumerable 继承INotifyPropertyChanged接口实现语法如下 publicclassPersonViewModel: INotifyPropertyChanged { privatestringfirstNameValue; publicstringFirstName{ get{returnfirstNameValue;} set { firstNameValue=value; //通知FirstName属性的改变 NotifyPropertyChanged("FirstName"); } } //定义PropertyChanged事件 publiceventPropertyChangedEventHandlerPropertyChanged; publicvoidNotifyPropertyChanged(stringpropertyName) { if(PropertyChanged! =null) { PropertyChanged(this,newPropertyChangedEventArgs(propertyName)); } } } 其实ViewModel类的原理都是大同小异的简单地说就是它的属性能够动态地变化,因为要跟View层交互 Command命令一般都需要定义成独立的类来实现,然后再ViewModel上实例化 Command命令类的实现的方法也几种方法 比如继承ICommand使用第三方组件的Command命令的类 继承ICommand的语法如下 publicclassMyCommand ICommand { Action Func publicShowMessageCommand(Action { this.Excuted=excuted; } publicboolCanExecute(objectparameter) { returntrue; } publiceventEventHandlerCanExecuteChanged; publicvoidExecute(objectparameter) { //你的需要执行的代码 } } View层要传递的参数等等都可以通过数据绑定来获取然后再在Command命令中处理,最后再通过数据绑定展现在View视图层上 三、Model Model层比较简单就是一个面向对象的实体类 比如 publicclassPerson { publicintage{get;set;} publicstringname{get;set;} } publicclassPersons { publicList publicList { person=newList { newPerson{name="Tom",age=21}, newPerson{name="Jack",age=22}, newPerson{name="Rose",age=23}, }; returnperson; } } Model层是主要的作用就是将信息用面向对象的方法封装起来,然后给ViewModel层使用。 MVVM模式导航问题 (1) 一般的采用View和后台cs中绑定Event的模式会导致view层和逻辑层耦合过紧,所以在开发Wp7客户端的时候我用了MVVM模式,一个VM对应了一个view,对于UI重构后后台逻辑基本不用变化就能够绑定Event。 但是原本后台逻辑中的页面跳转事件在松耦合的view和vm下就变的有点麻烦了。 原来我们可以直接在xaml.cs中重写下面两个方法来达到页面跳入跳出的逻辑的处理。 比如下面的代码,屎一般的代码啊。 。 。 1.publicpartialclassDetailPage: PhoneApplicationPage 2.{ 3.publicDetailPage() 4.{ 5.InitializeComponent(); 6.} 7. 8.Parametersparame=newParameters(); 9. 10.protectedoverridevoidOnNavigatedTo(NavigationEventArgse) 11.{ 12.stringid=null; 13.stringuid=null; 14.if(! NavigationContext.QueryString.TryGetValue("id",outid)||! NavigationContext.QueryString.TryGetValue("uid",outuid)){ 15.Deployment.Current.Dispatcher.BeginInvoke(()=>MessageBox.Show("参数错误! ")); 16.} 17. 18.this.parame.Add("id",id); 19.this.parame.Add("uid",uid); 20. 21.this.DetailPivot.DataContext=newDetailViewModel(this.parame); 22. 23.} 24. 25.protectedoverridevoidOnNavigatingFrom(NavigatingCancelEventArgse) 26.{ 27.base.OnNavigatingFrom(e); 28.this.parame.Clear(); 29.} 30. 31.} 这段代码是类似于从ListPage跳转到DetailPage的过程,这个过程中我们肯定要从上一个页面接收到ID来从后端获取信息,这样就出现了一段奇葩一样的代码 1.this.DetailPivot.DataContext=newDetailViewModel(this.parame); 我们的VM是在跳转后初始化的,而不是直接在View的Ctor中生成,这样的耦合是非常不自然的。 接下来我们要用MVVMLight中的Messenger消息通信机制来实现完全的view和vm的松耦合。 我们要用到ViewModelLocator这样vm反向定位器,并且在app.xml应用初始化的时候进行注册。 类似于这样: 1.publicclassViewModelLocator 2.{ 3.publicViewModelLocator() 4.{ 5.ServiceLocator.SetLocatorProvider(()=>SimpleIoc.Default); 6.SimpleIoc.Default.Register 7.} 8. 9. 10.//Singleton 11.[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance","CA1822: MarkMembersAsStatic",Justification="Thisnon-staticmemberisneededfordatabindingpurposes.")] 12.publicFeatureViewModelFeaturePage 13.{ 14.get 15.{ 16.returnServiceLocator.Current.GetInstance 17.} 18.} 19.} 由于消息通信时必须是每一个Vm对于与View是Singleton模式,我们用ServiceLocator自带的IOC容器注册这些vm,这里的ServiceLocator对象时取自Microsoft.Practices.ServiceLocation这个MS的Practices项目的DLL。 接下来我们把ViewModelLocator注册到app.xml中 1. ViewModelLocatorx: Key="Locator"d: IsDataSource="True"/> 在view中注入VM 1.DataContext="{BindingFeaturePage,Source={StaticResourceLocator}}" 接下来在注册一个导航的控制器并在初始化的时候生成: 1.publicclassNavigationController 2.{ 3.publicNavigationController() 4.{ 5.Messenger.Default.Register 6.} 7. 8.privatevoidNavigation(Uriuri) 9.{ 10.NavigationHelper.NavigationTo(uri); 11.} 12.} app.xml 1. NavigationControllerx: Key="NavCtr"/> 这里的控制器就是一个拦截消息并分发的作用。 每个与view绑定的VM需要实现INavigation这个接口: 1.publicinterfaceINavigation 2.{ 3.stringGetViewUrl(); 4.voidNavigated(Uriuri); 5.} 这个接口是注册执行方法的规范接口。 我们在Helper类中定义两个关键的方法: 1.publicstaticvoidNavigationMsgSend(stringpageUrl) 2.{ 3.Messenger.Default.Send(CreateUri(pageUrl),MessageToken.Navigation); 4.} 5. 6. 7.publicstaticvoidNavigatedMsgReg(objectrecipient) 8.{ 9.INavigationnavigation=recipientasINavigation; 10.if(navigation! =null) 11.{ 12.Messenger.Default.Register 13.} 14. 15.} 最后我们把上面的所有代码运用到vm中。 1.publicclassDetailViewModel: BaseViewModel,INavigation 2.{ 3.publicDetailViewModel() 4.{ 5.NavigationHelper.NavigatedMsgReg(this); 6.} 7. 8.publicstringGetViewUrl() 9.{ 10.return"/View/DetailPage.xaml"; 11.} 12. 13.publicvoidNavigated(Uriuri) 14.{ 15.this.userId=NavigationHelper.GetQueryString(uri,"uid"); 16.this.postId=NavigationHelper.GetQueryString(uri,"id"); 17.this.Cursor=0L; 18.this.LoadDetail(); 19.} 20.} 我们在List页面中导航是这样的: Java代码复制代码收藏代码 1.voidTapStoryItemAction(objectsender) 2.{ 3.LongListSelectorselector=senderasLongListSelector; 4.PostViewModelo=selector.SelectedItemasPostViewModel; 5.stringid=o.PostItem.Id; 6.stringuid=o.PostItem.Publisher.Id; 7. 8.NavigationHelper.NavigationMsgSend(String.Format("/View/DetailPage.xaml? id={0}&uid={1}",id,uid)); 9. 10.selector.SelectedItem=null; 11.} 好,大体的过程就是vm在初始化时NavigationHelper.NavigatedMsgReg(this)注册在了消息列表中,当在listpage发起导航时进行Messenger.Default.Send动作,第一件事是触发了控制器中的导航实际动作,这个动作是真正的导航到detail页面,但这个时候和vm是毫无关系的,第二步由于在相应的vm中用自己对应的uri来注册了消息通知,所以这个消息传递到了vm中,同时执行了vm中的Navigated方法,所以我们可以直接在vm中获得到上一个页面传进来的queue字段进操作,这样就完成了整套导航的动作,并且保持view和vm松耦合。 MVVM模式导航问题 (2) 上一篇是讲messager通信机制下的导航,但这种导航方式有诸多问题: 首先需要注册在IOC容器中的VM必须是Singleton模式,因为假如不是单例则每一次生成vm,每一次都会去消息列表中注册一个观察者,这样会导致当出现导航动作的时候,这个VM下的Navigated方法多次执行,你并不知道这个vm应该什么时间点去注销消息列表。 第二点是由于是单例模式下的vm,所以每次的跳出都要执行cleanup操作来重置当前view的数据,会带来一些不必要的麻烦,就好像C#的垃圾回收要你自己做一样。 第三点是有一些情况会出现这个view自跳转本身的view,就像一个ProfileView(个人主页)中会有好友列表,而这个好友列表又要调转到另一个ID的ProfileView,因为前一个view跳出时已经清空了数据,在新view中重新生成数据,这就导致了返回的时候又要重新的去请求后台并渲染UI,除非你自己维护一个cache来储存这些数据,但是cache的维护导致应用的复杂度提高而且cache何时被合适的销毁又是一个问题。 所以我们对于有些像detailview的view就要采用非单例的模式,每一次的跳转都会生成一个新的vm,而垃圾回收让C#的runtime自己去执行。 好,先从ViewModelLocator开始入手。 每次注入vm都采用全新的vm对象。 1.[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance","CA1822: MarkMembersAsStatic",Justification="Thisnon-staticmemberisneededfordatabindingpurposes.")] 2.publicDetailViewModelDetailPage 3.{ 4.get 5.{ 6.returnnewDetailViewModel(); 7.} 8.} 属性每一次不会去IOC容器中取单例,而是new一个新的对象,(IOC的完整代码可以看前一篇博文 好,这下每次注入的都是新的对象,但是问题来了,要是像上面说的这样每个vm每次都去注册一个消息观察者,一会导致这些vm得不到垃圾回收,二是导致方法重复执行。 我们要看来要放弃这种以消息通信机制为基础的导航。 重新写一个接口取名为NavigationViewModel 1.publicinterfaceINavigationBase 2.{ 3. 4.} 1.publicinterfaceINavigatedViewModel: INavigationBase 2.{ 3.voidOnNavigatedTo(NavigationEventArgse); 4.voidOnNavigatedFrom(NavigationEventArgse); 5.} 这个接口是要有导航动作的vm实现的。 现在我们改一下控制器: 1.publicclassNavigationController 2.{ 3.privateINavigationBasecurrentVM; 4. 5.privatestaticPhoneApplicationFrameframe; 6. 7.publicNavigationController() 8.{} 9. 10.publicNavigationController(PhoneApplicationFrameframe) 11.{ 12.frame.Navigated+=onRootFrameNavigated; 13.frame.Navigating+=onRootFrameNavigating; 14. 15.} 16. 17.privatevoidonRootFrameNavigated(objectsender,NavigationEventArgse) 18.{ 19.if(currentVM! =null) 20.{ 21.if(currentVMisINavigatedViewModel) 22.{ 23.(currentVMasINavigatedViewModel).OnNavigatedFrom(e); 24.} 25.currentVM=null; 26.} 27. 28.varpage=e.ContentasPhoneApplicationPa
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Windows phone MVVM模式 MVVM 模式