现在的位置: 首页 > 综合 > 正文

[Silverlight入门系列]Prism的INavigationAware导航接口和IRegionNavigationJournal接口

2011年07月16日 ⁄ 综合 ⁄ 共 5227字 ⁄ 字号 评论关闭

大家在使用Prism的时候需要用到IRegionManager接口来获得RegionManager,这个Region可以是Frame、ContentControl、TabControl或其他的Container容器。这个容器就是可以用来导航Navigation的目标Target,导航的时候可以提供一个地址(URI),这可以是一个view的类型名称;而且这个URI可以带参数,例如:MyViewType?mydata1=abc&key=121,这个URI就是带两个参数mydata1和key,这个两个参数的值分别是abc和121,都在URI里面了。这样导航的时候就很方便。另外,Prism的IRegionNavigationJournal服务已经实现了GoBack() GoForward()前进、后退等导航。

在XAML中定义一个Prism的Region:

prism:Region

xmlns:prism="http://www.codeplex.com/prism"

<ContentControl prism:RegionManager.RegionName="MyRegion"/>

这里用一个ContentControl作为Region容器,你也用任何其他的Container做容器,或者你的自定义控件,但需要实现IRegionAdapter接口,这篇文章讲了如何创建一个自定义的RegionAdapter。

在其它的模块中往这个Region放一个视图View,可以这样做:

首先,在相应类里用MEF依赖注入获得IRegionManager(这个类也必须用MEF方式创建,否则MEF注入IRegionManager会失败),你可以用属性方式注入,或者构造器方式注入:

属性方式注入

[Import]
public IRegionManager TheRegionManager { private get; set; }
构造器方式注入

private IRegionManager TheRegionManager;

[ImportingConstructor]
public MyViewModel(IRegionManager regionManager)
{
TheRegionManager
= regionManager;
}

然后用MEF注入你希望放入Region的视图View(注入View的同时会注入其ViewModel等,无需手动添加)

MEF注入View

[Import]
public MyView _MyView { private get; set; }

现在你可以在Region里显示视图了,

视图在Region显示

IRegion mainContentRegion = TheRegionManager.Regions["YourRegionName"];

mainContentRegion.Add(_MyView,
"MyKey");

然后你可以用Navigate导航的方式导航到这个视图,可以用param在URI传递参数:

Navigate导航

TheRegionManager.RequestNavigate("MyRegionName", new Uri("MyView?param1=abc&param2=123", UriKind.Relative));

INavigationAware接口

上面的代码中我们navigate导航到URI:MyView?param1=abc&param2=123,如何获得这些参数呢?这就要实现INavigationAware接口。

上面那个视图View叫MyView,假设它的ViewModel叫MyViewModel,那个这个ViewModel必须实现INavigationAware接口。

INavigationAware

public interface INavigationAware
{
bool IsNavigationTarget(NavigationContext navigationContext);
void OnNavigatedTo(NavigationContext navigationContext);
void OnNavigatedFrom(NavigationContext navigationContext);
}

这三个方法都很重要:

  • IsNavigationTarget方法:当前的视图模型是否可以处理请求的导航行为,通常用来指定当前的视图/模型是否可以重用。例如,一个显示客户明细的视图可以显示客户a,客户b....等的信息,他们重用同一个视图。
  • OnNavigatedTo方法:当前的页面被导航到以后发生,这个函数可以用来处理URI的参数。
  • OnNavigatedFrom方法:当前的页面导航到其他页面的时候发生。

这里给出一个实现了INavigationAware接口的ViewModel例子:

实现INavigationAware接口的ViewModel

1 [Export("MyViewModel ", typeof(MyViewModel ))]
2 [PartCreationPolicy(CreationPolicy.NonShared)]
3  public class MyViewModel : INavigationAware
4 {
5 private IRegionNavigationJournal navigationJournal;
6
7 bool INavigationAware.IsNavigationTarget(NavigationContext navigationContext)
8 {
9 return true;
10 }
11
12 void INavigationAware.OnNavigatedFrom(NavigationContext navigationContext)
13 {
14 // Intentionally not implemented.
15   }
16
17
18 void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
19 {
20 var pram1 = navigationContext.Parameters["param1"];
21
22 UpdateDataAsync(pram1 );
23
24 this.navigationJournal = navigationContext.NavigationService.Journal;
25 }
26
27
28
29 }

在上面的例子中,OnNavigatedTo方法获取了URI的参数,当参数发生变化自动重新获取数据刷新ViewModel。这个场景可以用在:例如,一个显示客户明细的视图可以显示客户a,客户b....等的信息,他们重用同一个视图。当我们navigate导航到URI:MyView?param1=abc&param2=123的时候,导航到同一个视图但客户ID变化,放在URI的参数中传递,OnNavigatedTo方法获取了URI的参数,重用当前视图,同时刷新数据。整个过程很清晰。

IConfirmNavigationRequest接口

有些时候当我们导航到其他页面的时候,需要弹出一个框提示用户“是否放弃修改?保存、放弃、取消”,这就需要实现IConfirmNavigationRequest接口。还是上面那个例子,那个ViewModel还要实现IConfirmNavigationRequest接口。这样这个ViewModel既实现了INavigationAware接口又实现了IConfirmNavigationRequest接口,触发的顺序如下:

  • 导航发生的时候,如果目标也实现了IConfirmNavigationRequest接口,那么先自动调用ConfirmNavigationRequest.
  • ViewModel触发interaction来打开一个“是否放弃修改?保存、放弃、取消” 确认的UI。
  • 当用户选择了"保存、放弃、取消”以后interaction的callback自动触发
  • 根据用户的选择决定是否继续导航

来一个例子:

ViewModel实现了IConfirmNavigationRequest接口

1 public class ComposeEmailViewModel : NotificationObject, IConfirmNavigationRequest
2 {
3 private readonly InteractionRequest<Confirmation>
4 confirmExitInteractionRequest;
5 public ComposeEmailViewModel(IEmailService emailService)
6 {
7 this.confirmExitInteractionRequest = new
8 InteractionRequest<Confirmation>();
9 }
10 public IInteractionRequest ConfirmExitInteractionRequest
11 {
12 get { return this.confirmExitInteractionRequest; }
13 }
14 }

XAML定义Interaction

<UserControl.Resources>
<DataTemplate x:Name="ConfirmExitDialogTemplate">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
Text
="{Binding}"/>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<ei:Interaction.Triggers>
<prism:InteractionRequestTrigger
SourceObject="{Binding ConfirmExitInteractionRequest}">
<prism:PopupChildWindowAction
ContentTemplate="{StaticResource ConfirmExitDialogTemplate}"/>
</prism:InteractionRequestTrigger>
</ei:Interaction.Triggers>
...

用户确认

void IConfirmNavigationRequest.ConfirmNavigationRequest(
NavigationContext navigationContext, Action
<bool> continuationCallback)
{
this.confirmExitInteractionRequest.Raise(
new Confirmation {Content = "...", Title = "..."},
c
=> {continuationCallback(c.Confirmed);});
}

导航的前进和后退(GoBack, GoForward)

还是上面的ViewModel例子,我们以后获得了IRegionNavigationJournal,就可以定义Command来很容易的实现前进和后退(GoBack, GoForward)

用INavigationJournal实现GoBack

1 public class MyViewModel : INavigationAware
2 {
3 public DelegateCommand<object> GoBackCommand { get; private set; }
4
5 private void GoBack(object commandArg)
6 {
7 if (navigationJournal.CanGoBack)
8 navigationJournal.GoBack();
9 }
10
11 public DelegateCommand<object> GoForwardCommand { get; private set; }
12
13 private void GoForward(object commandArg)
14 {
15 if (navigationJournal.CanGoForward)
16 navigationJournal.GoForward();
17 }

总结

Prism中的Navigation导航比较复杂,上面说的内容需要琢磨透,否则navigate过程很容易出问题。对了,失败了会触发NavigationFailed事件。

此外,除了机遇视图(view)的导航,还有基于状态的导航,例如同一个界面,同样的数据,viewByIcon和viewByList是不同的视图状态,这个导航下回再说。

抱歉!评论已关闭.