在Prism之Region(1)中,介绍了一些Prism中页面组织以及切换的方式。这一篇将以一个很简单的示例程序来实践一下。
下面是效果图:
先说Log,Prism内置了ILogFacade接口,在Prism提供的QuickStart项目里的Modularity中,有一个CallbackLogger,这里我们直接拿过来使用。然后在PrismRegionShell中放一个TextBox,将log的内容显示在这个TextBox中。值得一提的是,为了让输出新log的直接显示出来,需要将TextBox的滚动条滚动到最下面。这里采用的是如下方法:
1
2
3
4
5
6
|
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就可以了。
1
2
3
4
5
|
[ViewSortHint(
"01"
)]
public
partial
class
NavigationItem : UserControl
[ViewSortHint(
"02"
)]
public
partial
class
EmptyNavigationItem : UserControl
|
在初始化导航实例的Module时,将导航示例的视图注册到内容区的Region,这时[上一个]按钮依然处于灰色状态,因为通过RegisterViewWithRegion方法显示的页面是不被记录的。当点击[ViewA][ViewB][ViewC]这三个按钮时,会采用RequestNavigate方法来进行页面的跳转,这时页面跳转的过程会被记录下来,此时就可以通过[上一个]和[下一个]按钮进行页面的前进和后退。
1
2
3
4
5
6
7
8
9
10
11
12
|
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)方法注册过了。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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中实现。
1
2
3
4
|
public
bool
IsNavigationTarget(NavigationContext navigationContext)
{
return
false
;
}
|
在ViewB中,IsNavigationTarget方法返回了false,而ViewA中则返回了true。可以通过点击三个按钮进行页面跳转,观察log可以发现,ViewA只创建了一次,而ViewB则每次都要重新创建。还有就是在跳转到ViewA的时候传递了参数,可以在OnNavigatedTo方法中取出参数。
1
2
3
4
5
6
|
public
void
OnNavigatedTo(NavigationContext navigationContext)
{
UriQuery query = navigationContext.Parameters;
string
time = query[
"Time"
];
logger.Log(
string
.Format(
"ViewA: 现在时间 {0}"
, time), Category.Info, Priority.Medium);
}
|