【WP开发】应用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松耦合。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值