在Prism之Region(1)中,介绍了一些Prism中页面组织以及切换的方式。这一篇将以一个很简单的示例程序来实践一下。
下面是效果图:
先说Log,Prism内置了ILogFacade接口,在Prism提供的QuickStart项目里的Modularity中,有一个CallbackLogger,这里我们直接拿过来使用。然后在PrismRegionShell中放一个TextBox,将log的内容显示在这个TextBox中。值得一提的是,为了让输出新log的直接显示出来,需要将TextBox的滚动条滚动到最下面。这里采用的是如下方法:
public void Log(string message, Category category, Priority priority) { this.LogContainer.Text += string.Format(CultureInfo.CurrentUICulture, "[{0}][{1}] {2}\r\n", category, priority, message); // 这段代码的作用是让文本框的滚动条滚动到最底部 LogContainer.Select(LogContainer.Text.Length, LogContainer.Text.Length); }
然后说一下左边的导航区。这里放置了一个ItemsControl,并将其设为Region。里面的两个按钮并不是直接写死到xaml里的,而是在两个Module初始化时动态添加进来的。也就是说,这个ItemsControl并不知道自己将要包含哪些项。这里我们使用IRegionManager.RegisterViewWithRegion(RegionNames.NavRegion, typeof(EmptyNavigationItem));这种方式来将视图注册到Region中。这样当该Region显示的时候两个视图才会被初始化。这里需要注意的是,一个Region里需要同时显示多个视图时,视图的顺序问题。比如ItemsControl,哪个先被注册就哪个显示在上面,但是由于Module的加载速度等原因,所以这时两个视图不一定谁在上面。现在我需要指定[导航示例]这个按钮在上,那么Prism为我们提供了ViewSortHintAttribute来解决这个问题。在需要进行排序的视图上添加上相应的attribute就可以了。
[ViewSortHint("01")] public partial class NavigationItem : UserControl [ViewSortHint("02")] public partial class EmptyNavigationItem : UserControl
在初始化导航实例的Module时,将导航示例的视图注册到内容区的Region,这时[上一个]按钮依然处于灰色状态,因为通过RegisterViewWithRegion方法显示的页面是不被记录的。当点击[ViewA][ViewB][ViewC]这三个按钮时,会采用RequestNavigate方法来进行页面的跳转,这时页面跳转的过程会被记录下来,此时就可以通过[上一个]和[下一个]按钮进行页面的前进和后退。
void ToSpecifiedView(string viewName) { UriQuery query = new UriQuery(); if (viewName == ViewNames.ViewA) { query.Add("Time", DateTime.Now.ToShortTimeString()); } Uri uri = new Uri(viewName + query.ToString(), UriKind.Relative); _regionManager.RequestNavigate(RegionNames.NavDemoShowRegion, uri); logger.Log("跳转到视图 [" + viewName + "]", Category.Info, Priority.Low); ResetNavigationButtonState(); }
注意这三个视图已经在初始化Module的时候使用IUnityContainer.RegisterType<object, ViewA>(ViewNames.ViewA)方法注册过了。
public void Initialize() { logger.Log("初始化Navigation模块", Category.Debug, Priority.Low); _regionManager.RegisterViewWithRegion(RegionNames.NavRegion, typeof(NavigationItem)); _regionManager.RegisterViewWithRegion(RegionNames.MainRegion, () => _container.Resolve<NavigationContainer>() ); _regionManager.RegisterViewWithRegion(RegionNames.NavDemoActionRegion, typeof(ActionController)); // 注意注册的类型的必须是object,因为Prism无法确定视图的类型,所以就用了object _container.RegisterType<object, ViewA>(ViewNames.ViewA); _container.RegisterType<object, ViewB>(ViewNames.ViewB); _container.RegisterType<object, ViewC>(ViewNames.ViewC); }
ViewA和ViewB都实现了INavigationAware接口,不同之处在于ViewA是在其对应的ViewModel ViewAViewModel类中实现的,而ViewB则直接在Code Behind中实现的。Prism对MVVM提供了良好的支持,因此既可以选择在视图中实现该接口也可以在对应的ViewModel中实现。
public bool IsNavigationTarget(NavigationContext navigationContext) { return false; }
在ViewB中,IsNavigationTarget方法返回了false,而ViewA中则返回了true。可以通过点击三个按钮进行页面跳转,观察log可以发现,ViewA只创建了一次,而ViewB则每次都要重新创建。还有就是在跳转到ViewA的时候传递了参数,可以在OnNavigatedTo方法中取出参数。
public void OnNavigatedTo(NavigationContext navigationContext) { UriQuery query = navigationContext.Parameters; string time = query["Time"]; logger.Log(string.Format("ViewA: 现在时间 {0}", time), Category.Info, Priority.Medium); }