使用场景
在普遍的业务场景当中, 必不可少的是页面切换, 而Prism就可以使用Navigation功能来进行页面导航, 在不同的场景当中会有各种用法, 例如在切换页面验证、传递参数、返回上一页、返回下一页等功能。
导航的基本条件
- 1.注册显示区域
- 2.注册导航页面
1.注册导航
- 注册视图类型或添加别名, 如果为指定别名,名称默认为当中类型的名称
public class ModuleAModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
//添加别名 "CustomName"
containerRegistry.RegisterForNavigation<ViewA>("CustomName");
//默认名称 "ViewB"
containerRegistry.RegisterForNavigation<ViewB>();
}
}
- 注册时,指定ViewModel或添加别名。
public class ModuleAModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
//指定ViewModel
containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
//指定ViewModel并且添加别名
containerRegistry.RegisterForNavigation<ViewB, ViewBViewModel>("CustomName");
}
}
2.使用导航
正如前面的章节所讲到, Region的注册以及管理、导航等, 我们可以使用IRegionManager接口,所以,我们现在便可以使用该接口实现导航功能, 如下所示:
IRegionManager regionManager = …;
regionManager.RequestNavigate("RegionName", "ViewName");
可以注意点, 我们调用了IRegionManager接口的RequestNavigate方法, 并且传递了两个参数:
- RegionName: 该参数为注册的区域名称
- ViewName: 该参数实际为我们上面注册过的导航页, 字符串类型, 对应的是我们注册页面的nameof
3.带参数导航
正如,我们想要在导航页前, 传递一些参数, 则可以使用NavigationParameters, 如下所示:
var param = new NavigationParameters();
param.Add("Parameter", param);
_regionManger.RequestNavigate("RegionName", "ViewName", param);
//类似URL地址传递参数
_regionManger.RequestNavigate("RegionName", "ViewName?id=1&Name=xiaoming");
疑问?
当我们使用导航为指定区域注册视图时, 我们怎么控制打开的过程? 所以, 下面我们将讲解一下
INavigationAware接口。
INavigationAware
该接口包含3个方法, 每个方法中都包含当前导航的上下文, 如下所示:
public void OnNavigatedTo(NavigationContext navigationContext)
{
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
- OnNavigatedTo: 导航完成前, 此处可以传递过来的参数以及是否允许导航等动作的控制。
- IsNavigationTarget: 调用以确定此实例是否可以处理导航请求。否则新建实例
- OnNavigatedFrom: 当导航离开当前页时, 类似打开A, 再打开B时, 该方法被触发。
INavigationAware 执行流程
获取导航请求参数
正如上面所描述, 导航中允许我们传递参数, 用于在我们完成导航之前, 进行做对应的逻辑业务处理。这时候, 我们便可以在OnNavigatedTo方法中通过导航上下文中获取到传递的所有参数。如下:
public void OnNavigatedTo(NavigationContext navigationContext)
{
var id = navigationContext.Parameters.GetValue<int>("id");
var name = navigationContext.Parameters["Name"].ToString();
}
IConfirmNavigationRequest
该接口继承于INavigationAware, 所以, 它多了一个功能: 允许用户针对导航请求进行拦截。
//多了一个回调函数, 该值觉得是否拦截该导航请求
void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback);
IConfirmNavigationRequest 执行流程
拦截导航请求
当打开新的导航时, 或许有些情况下你需要经过用户进行确认, 这个时候, IConfirmNavigationRequest接口可以满足需求, 如下:
public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
bool result = true;
if (MessageBox.Show("确认导航?", "温馨提示", MessageBoxButton.YesNo) == MessageBoxResult.No)
result = false;
//通过回调当前返回的确认结果,决定是否启动该导航
continuationCallback(result);
}
Navigation Journal
导航日志, 其实就是对导航系统的一个管理功能, 理论上来说, 我们应该知道我们上一步导航的位置、以及下一步导航的位置, 包括我们导航的历史记录。以便于我们使用导航对应用程序可以灵活的控制。
IRegionNavigationJournal
该接口包含以下功能:
- GoBack() : 返回上一页
- CanGoBack : 是否可以返回上一页
- GoForward(): 返回后一页
- CanGoForward : 是否可以返回后一页
1.示例(返回上一页)
当导航日志当中,包含上一页的历史记录, 如下所示, D的前面有ABC的记录,所以对于D而言, 它可以返回上一页, 所以CanGoBack 为True。
2.示例(返回下一页)
当导航日志当中,包含下一个的历史记录,如下所示,D的后面有E的记录,所以对于D而言,它可以返回下一页,所以CanGoForward 为True。
示例
MainWindow代码
<Window
x:Class="PrismApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PrismApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
Title="{Binding Title}"
Width="800"
Height="450"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="9*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button
MinWidth="80"
Margin="3"
Command="{Binding OpenCommand}"
CommandParameter="ViewA"
Content="打开模块A"
FontSize="12" />
<Button
MinWidth="80"
Margin="3"
Command="{Binding OpenCommand}"
CommandParameter="ViewB"
Content="打开模块B"
FontSize="12" />
<Button
MinWidth="80"
Margin="3"
Command="{Binding OpenCommand}"
CommandParameter="ViewC"
Content="打开模块C"
FontSize="12" />
<Button
MinWidth="80"
Margin="3"
Background="Yellow"
Command="{Binding BackCommand}"
Content="上页"
FontSize="12" />
<Button
MinWidth="80"
Margin="3"
Background="Yellow"
Command="{Binding ForwardCommand}"
Content="下页"
FontSize="12" />
</StackPanel>
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>
</Window>
绑定两个导航命令
<Button
MinWidth="80"
Margin="3"
Background="Yellow"
Command="{Binding BackCommand}"
Content="上页"
FontSize="12" />
<Button
MinWidth="80"
Margin="3"
Background="Yellow"
Command="{Binding ForwardCommand}"
Content="下页"
FontSize="12" />
MainWindouViewModle代码
namespace PrismApp.ViewModels
{
internal class MainWindowViewModel : BindableBase
{
private string _title = "Prism App"; //标题
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private readonly IRegionManager regionManager;
private IRegionNavigationJournal journal; //导航日志
public DelegateCommand<string> OpenCommand { get; private set; }
public DelegateCommand BackCommand { get; private set; }
public DelegateCommand ForwardCommand { get; private set; }
public MainWindowViewModel(IRegionManager regionManager)
{
BackCommand = new DelegateCommand(Back);
ForwardCommand = new DelegateCommand(Forward);
OpenCommand = new DelegateCommand<string>(Open);
this.regionManager = regionManager;
}
private void Forward()
{
if (this.journal != null && journal.CanGoForward)
journal.GoForward();
}
private void Back()
{
if (this.journal != null && journal.CanGoBack)
journal.GoBack();
}
private void Open(string obj)//页面切换方法
{
NavigationParameters keys = new NavigationParameters();//传递参数
keys.Add("Title", "Hello!");
keys.Add("Name", "Tom!");
//不带参数
// regionManager.Regions["ContentRegion"].RequestNavigate(obj,keys);
//带参数
regionManager.Regions["ContentRegion"].RequestNavigate(obj, callBack =>
{
if ((bool)callBack.Result)
{
journal = callBack.Context.NavigationService.Journal;
}
}, keys);
}
}
}
ModuleA代码
ModuleA前端代码,主要绑定一个标题属性用于显示接收的参数
<UserControl x:Class="ModuleA.Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ModuleA.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<Grid Background="AliceBlue">
<TextBlock Text="{Binding Title}" FontSize="60" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</UserControl>
ViewAViewModel代码
namespace ModuleA.ViewModels
{
public class ViewAViewModel : BindableBase, INavigationAware
{
public ViewAViewModel()
{
}
private string title;
public string Title
{
get { return title; }
set { title = value; RaisePropertyChanged(); }
}
/// <summary>m
/// 调用以确定此实例是否可以处理导航请求。否则新建实例
/// </summary>
/// <param name="navigationContext"></param>
/// <returns></returns>
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return false;
}
/// <summary>
/// 当导航离开当前页时, 类似打开A, 再打开B时, 该方法被触发。
/// </summary>
/// <param name="navigationContext"></param>
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
/// <summary>
/// 导航完成前, 此处可以传递过来的参数以及是否允许导航等动作的控制。
/// </summary>
/// <param name="navigationContext"></param>
public void OnNavigatedTo(NavigationContext navigationContext)
{
if (navigationContext.Parameters.ContainsKey("Title"))
{
Title = navigationContext.Parameters.GetValue<string>("Title");
}
}
///// <summary>
///// IConfirmNavigationRequest该接口继承于INavigationAware, 所以, 它多了一个功能: 允许用户针对导航请求进行拦截。
///// </summary>
///// <param name="navigationContext"></param>
///// <param name="continuationCallback"></param>
//public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
//{
// bool result = true;
// if (MessageBox.Show("确认导航", "温馨提示", MessageBoxButton.YesNo) == MessageBoxResult.No)
// result = false;
// //通过回调当前返回的确认结果,决定是否启动该导航
// continuationCallback(result);
//}
}
}