首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > 移动开发 >

WP7开发中应用MVVM模式下的导航有关问题(一)

2012-06-27 
WP7开发中应用MVVM模式下的导航问题(一)一般的采用View和后台cs中绑定Event的模式会导致view层和逻辑层耦

WP7开发中应用MVVM模式下的导航问题(一)

一般的采用View和后台cs中绑定Event的模式会导致view层和逻辑层耦合过紧,所以在开发Wp7客户端的时候我用了MVVM模式,一个VM对应了一个view,对于UI重构后后台逻辑基本不用变化就能够绑定Event。

但是原本后台逻辑中的页面跳转事件在松耦合的view和vm下就变的有点麻烦了。

原来我们可以直接在xaml.cs中重写下面两个方法来达到页面跳入跳出的逻辑的处理。

比如下面的代码,屎一般的代码啊。。。

?

public partial class DetailPage : PhoneApplicationPage    {        public DetailPage()        {            InitializeComponent();        }        Parameters parame = new Parameters();        protected override void OnNavigatedTo(NavigationEventArgs e)        {            string id = null;             string uid = null;             if (!NavigationContext.QueryString.TryGetValue("id" ,out id) || !NavigationContext.QueryString.TryGetValue("uid" ,out uid)){                Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show("参数错误!"));            }            this.parame.Add("id", id);            this.parame.Add("uid", uid);            this.DetailPivot.DataContext = new DetailViewModel(this.parame);        }        protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)        {            base.OnNavigatingFrom(e);            this.parame.Clear();        }    }
?

这段代码是类似于从ListPage跳转到DetailPage的过程,这个过程中我们肯定要从上一个页面接收到ID来从后端获取信息,这样就出现了一段奇葩一样的代码

?

this.DetailPivot.DataContext = new DetailViewModel(this.parame);

?

?我们的VM是在跳转后初始化的,而不是直接在View的Ctor中生成,这样的耦合是非常不自然的。

?

接下来我们要用MVVMLight中的Messenger消息通信机制来实现完全的view和vm的松耦合。

?

我们要用到ViewModelLocator这样vm反向定位器,并且在app.xml应用初始化的时候进行注册。

类似于这样:

?

public class ViewModelLocator{        public ViewModelLocator()        {            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);            SimpleIoc.Default.Register<FeatureViewModel>();        }        //Singleton        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This non-static member is needed for data binding purposes.")]        public FeatureViewModel FeaturePage        {            get            {                return ServiceLocator.Current.GetInstance<FeatureViewModel>();            }        }}
?

?

由于消息通信时必须是每一个Vm对于与View是Singleton模式,我们用ServiceLocator自带的IOC容器注册这些vm,这里的ServiceLocator对象时取自Microsoft.Practices.ServiceLocation这个MS的Practices项目的DLL。

接下来我们把ViewModelLocator注册到app.xml中

?

?

<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />

?

在view中注入VM

?

DataContext="{Binding FeaturePage, Source={StaticResource Locator}}"

?

接下来在注册一个导航的控制器并在初始化的时候生成:

?

public class NavigationController    {        public NavigationController()        {            Messenger.Default.Register<Uri>(this, MsgToken.Navigation, Navigation);        }        private void Navigation(Uri uri)        {            NavigationHelper.NavigationTo(uri);        }    }

?app.xml

?

?

<nav:NavigationController  x:Key="NavCtr"/>

?

这里的控制器就是一个拦截消息并分发的作用。

?

每个与view绑定的VM需要实现INavigation这个接口:

?

?

public interface INavigation{        string GetViewUrl();        void Navigated(Uri uri);}

?

?

这个接口是注册执行方法的规范接口。

?

我们在Helper类中定义两个关键的方法:

?

        public static void NavigationMsgSend(string pageUrl)        {            Messenger.Default.Send(CreateUri(pageUrl), MessageToken.Navigation);        }        public static void NavigatedMsgReg(object recipient)        {            INavigation navigation = recipient as INavigation;            if (navigation != null)            {                Messenger.Default.Register<Uri>(recipient, navigation.GetViewUrl(), navigation.Navigated);            }        }

?

?最后我们把上面的所有代码运用到vm中。

?

?

public class DetailViewModel : BaseViewModel, INavigation{        public DetailViewModel()        {            NavigationHelper.NavigatedMsgReg(this);        }        public string GetViewUrl()        {            return "/View/DetailPage.xaml";        }        public void Navigated(Uri uri)        {            this.userId = NavigationHelper.GetQueryString(uri, "uid");            this.postId = NavigationHelper.GetQueryString(uri, "id");            this.Cursor = 0L;            this.LoadDetail();        }}

?

我们在List页面中导航是这样的:

?

?

void TapStoryItemAction(object sender)        {            LongListSelector selector = sender as LongListSelector;            PostViewModel o = selector.SelectedItem as PostViewModel;            string id = o.PostItem.Id;            string uid = o.PostItem.Publisher.Id;            NavigationHelper.NavigationMsgSend(String.Format("/View/DetailPage.xaml?id={0}&uid={1}", id, uid));            selector.SelectedItem = null;        }
?

?

?

好,大体的过程就是vm在初始化时NavigationHelper.NavigatedMsgReg(this)注册在了消息列表中,当在listpage发起导航时进行Messenger.Default.Send动作,第一件事是触发了控制器中的导航实际动作,这个动作是真正的导航到detail页面,但这个时候和vm是毫无关系的,第二步由于在相应的vm中用自己对应的uri来注册了消息通知,所以这个消息传递到了vm中,同时执行了vm中的Navigated方法,所以我们可以直接在vm中获得到上一个页面传进来的queue字段进操作,这样就完成了整套导航的动作,并且保持view和vm松耦合。

?

热点排行