MVVM Master-Detail导航

最近在看Laurent Bugnion的Deep Dive MVVM,大神传授了很多技巧,本文只关注一点,如何从ViewModel发起页面导航。

本文的Demo也很简单,MasterPage使用ListBox加载一些信息,单击其中任意项,页面导航到DetailsPage并显示选择项的详情,如下图。

                                       MasterPage                        DetailsPage 

MasterPage绑定至MasterViewModel,DetailsPage绑定至DetailsViewModel,代码在此MvvmNavigationDemo.zip,以下将只介绍关键技术点。

 

一、声明INavigationService接口,并实现一个继承NavigationService类.

public interface INavigationService
   {
       event NavigatingCancelEventHandler Navigating;
       void NavigateTo(Uri uri);
       void GoBack();
   }

public class NavigationService : INavigationService
   {
       private PhoneApplicationFrame _mainFrame;

       public event System.Windows.Navigation.NavigatingCancelEventHandler Navigating;

       public void NavigateTo(Uri uri)
       {
           if (EnsureMainFrame())
           {
               _mainFrame.Navigate(uri);
           }
       }

       public void GoBack()
       {
           if (EnsureMainFrame() && _mainFrame.CanGoBack)
           {
               _mainFrame.GoBack();
           }
       }

       private bool EnsureMainFrame()
       {
           if(_mainFrame!=null)
           {
               return true;
           }
         
           _mainFrame = Application.Current.RootVisual as PhoneApplicationFrame;
           if (_mainFrame != null)
           {
               _mainFrame.Navigating += (s, e) =>
                                            {
                                                if (Navigating != null)
                                                {
                                                    Navigating(s, e);
                                                }
                                            };
           }
           return false;
       }
   }

 

二、 在MasterViewModel类中声明INavigationService字段及SelectedItem属性.

public class MasterViewModel : ViewModelBase
   {
       public INavigationService NavigationService { get; set; }

 

       public ObservableCollection<FriendViewModel> Friends
       {
           get;
           private set;
       }

 

       public const string SelectedFriendPropertyName = "SelectedItem";
       private FriendViewModel _selectedItem = null;
       public FriendViewModel SelectedItem
       {
           get { return _selectedItem; }
           set
           {
               if (_selectedItem == value)
                   return;

               var oldValue = _selectedItem;
               _selectedItem = value;
               RaisePropertyChanged(SelectedFriendPropertyName, oldValue, _selectedItem, true);
          
              if(NavigationService !=null)
               {

                   // 调用NavigationService实例方法,进行页面导航
                   NavigationService.NavigateTo(ViewModelLocator.DetailsPageUri);
               }
           }
       }

   }
注意,本处变更通知,使用的是ViewModelBase的重载方法,发起PropertyChanged事件并广播一个PropertyChangedMessage消息。本处另一重要技巧便是使用INavigationService接口调用导航方法。

image

 

三、DetailsViewModel中加入SelectedItem属性,在默认构造中注册接受PropertyChangedMessage消息,并在收到消息时,执行传入的Action。

public partial class DetailsViewModel : ViewModelBase
    {
        public const string SelectedFriendPropertyName = "SelectedItem";

        private FriendViewModel _selectedItem = null;
        public FriendViewModel SelectedItem
        {
            get
            {
                return _selectedItem;
            }

            set
            {
                if (_selectedItem == value)
                {
                    return;
                }

                _selectedItem = value;
                RaisePropertyChanged(SelectedFriendPropertyName);
            }
        }

        public DetailsViewModel(IFriendsService friendsService)
        {
            ……

            Messenger.Default.Register<PropertyChangedMessage<FriendViewModel>>(
                this,
                message =>
                    {
                        SelectedItem = null;
                        SelectedItem = message.NewValue;
                    });

            ……
        }

image

 

四、ViewModelLocator中分别向SimpleIoc注册MasterViewModel/DetailsViewModel,并加入Master/Detail属性。同时将Master的NavigationService字段赋值为NavigationService的实例。

public class ViewModelLocator
   {
       public static readonly Uri DetailsPageUri = new Uri("/DetailsPage.xaml", UriKind.Relative);

       static ViewModelLocator()
       {
           ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

           if (ViewModelBase.IsInDesignModeStatic)
           {
               SimpleIoc.Default.Register<IFriendsService, Design.DesignFriendsService>();
           }
           else
           {
               SimpleIoc.Default.Register<IFriendsService, FriendsService>();
           }

           SimpleIoc.Default.Register<MasterViewModel>();
           // Ensure VM
           var master = SimpleIoc.Default.GetInstance<MasterViewModel>();
         
          SimpleIoc.Default.Register<DetailsViewModel>();
           // Ensure VM
           SimpleIoc.Default.GetInstance<DetailsViewModel>();

           master.NavigationService = new NavigationService();
       }

       /// <summary>
       /// Gets the Main property.
       /// </summary>
       [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
           "CA1822:MarkMembersAsStatic",
           Justification = "This non-static member is needed for data binding purposes.")]
       public MasterViewModel Master
       {
           get
           {
               return ServiceLocator.Current.GetInstance<MasterViewModel>();
           }
       }

       public DetailsViewModel Details
       {
           get
           {
               return SimpleIoc.Default.GetInstance<DetailsViewModel>();
           }
       }

}

 

Demo源代码:MvvmNavigationDemo.zip

posted @ 2012-04-24 10:18  逝 者 如 斯  阅读(1688)  评论(2编辑  收藏  举报