一、使用场景
在普遍的业务场景当中,必不可少的是页面切换,而Prism就可以使用Navigation功能来进行页面导航,在不同的场景当中会有各种用法,例如在切换页面验证、传递参数、返回上一页、返回下一页等功能。
二、导航的使用
2.1 注册显示区域(region)
这个在前面章节已做详细介绍不再赘述。
2.2 注册导航页面(View)
之前的介绍中我们一般是将一个View指定到一个region中。而现在我们需要将多个View注册到导航中。例如我们现在有两个子模块(ViewAModule和ViewBModule),我们分别注册导航界面:
public class ViewAModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation(typeof(ViewA.Views.ViewA), "ViewA");
}
}
public class ViewBModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation(typeof(ViewB.Views.ViewB), "ViewB");
}
}
注意这一段代码:
containerRegistry.RegisterForNavigation(typeof(ViewB.Views.ViewB), "ViewB");
这就代码就是注册导航页面的,其中第一个参数是View的类型,第二个参数是ViewName(可省略,则默认为View类型的类名)。当然,这句代码也可以在App类中的重写函数中实现。
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation(typeof(ViewA.Views.ViewA), "ViewA");
containerRegistry.RegisterForNavigation(typeof(ViewB.Views.ViewB), "ViewB");
}
然后我们在主界面上添加两个按钮用于导航:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel
Grid.Row="0"
Margin="10"
Orientation="Horizontal">
<Button
Width="40"
Height="30"
Margin="40,0,40,0"
Command="{Binding NavigationCmd}"
CommandParameter="ViewA"
Content="ViewA" />
<Button
Width="40"
Height="30"
Margin="40,0,40,0"
Command="{Binding NavigationCmd}"
CommandParameter="ViewB"
Content="ViewB" />
</StackPanel>
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>
在MainViewModel中我们进行了一些数据的绑定:
public class MainWindowViewModel : BindableBase
{
public DelegateCommand<string> NavigationCmd { get; private set; }
private IRegionManager region;
public MainWindowViewModel(IRegionManager regionManager)
{
this.region = regionManager;
NavigationCmd = new DelegateCommand<string>(Navigation);
}
private void Navigation(string page)
{
region.RequestNavigate("ContentRegion", page);
}
}
注意这一段代码:
region.RequestNavigate("ContentRegion", page);
第一个参数是RegionName,第二个参数是我们前面注册的ViewName。其实它还有很多重载函数,还有另外两个参数Action navigationCallback和NavigationParameters navigationParameters。
2.3 导航完成后的回调
private void Navigation(string page)
{
region.RequestNavigate("ContentRegion", page,NavigationCallback);
}
private void NavigationCallback(NavigationResult result)
{
Console.WriteLine(result.Context.Uri);
Console.WriteLine(result.Error.Message);
Console.WriteLine(result.Result.Value);
}
请求导航完成的回调函数。
2.4 请求导航时的参数
private void Navigation(string page)
{
NavigationParameters parameters= new NavigationParameters("param1");
region.RequestNavigate("ContentRegion", page,NavigationCallback, parameters);
}
这个参数有什么用,怎么用,我们在后面详说。
2.5 INavigationAware
我们导航切换界面到底是一个什么过程呢?这样我们需要了解INavigationAware接口。该接口有三个方法需要实现。
public void OnNavigatedTo(NavigationContext navigationContext)
{
throw new NotImplementedException();
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
throw new NotImplementedException();
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
throw new NotImplementedException();
}
- OnNavigatedTo: 导航到当前页面前, 此处可以传递过来的参数以及是否允许导航等动作的控制
- IsNavigationTarget: 是否创建新示例。为true的时候表示不创建新示例,页面还是之前的;如果为false,则创建新的页面。
- OnNavigatedFrom: 导航离开当前页面前。
传递参数示例:
var parameters = new NavigationParameters();
parameters.Add("person", person);
public void OnNavigatedTo(NavigationContext navigationContext)
{
var person = navigationContext.Parameters["person"] as Person;
if (person != null)
SelectedPerson = person;
}
注意,这个接口应该由ViewModel来继承。
所以一个完整的导航请求的过程如下:
RequestNavigate->OnNavigatedFrom->IsNavigationTarget->ResolveView->OnNavigatedTo->NavigateComplete
2.6 IConfirmNavigationRequest
该接口继承自INavigationAware,所以,它多了一个功能:允许用户针对导航请求进行拦截。同时也需要多实现一个方法:
public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
throw new NotImplementedException();
}
public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
bool result = true;
if (MessageBox.Show("Do you to navigate?", "Navigate?", MessageBoxButton.YesNo) == MessageBoxResult.No)
result = false;
//true则同意 false则拒绝
continuationCallback(result);
}
所以这个接口的执行流程为:
RequestNavigate->ConfirmNavigationRequest->OnNavigatedFrom->ContinueNavigationProcess
2.7 IRegionNavigationJournal
导航日志,其实就是对导航系统的一个管理功能,理论上来说,我们应该知道我们上一步导航的位置、以及下一步导航的位置,包括我们导航的历史记录。以便于我们使用导航对应用程序可以灵活的控制。
IRegionNavigationJournal接口有如下功能:
- GoBack() : 返回上一页
- CanGoBack : 是否可以返回上一页
- GoForward(): 返回后一页
- CanGoForward : 是否可以返回后一页
IRegionNavigationJournal _journal;
public void OnNavigatedTo(NavigationContext navigationContext)
{
_journal = navigationContext.NavigationService.Journal;
}
我们可以在OnNavigatedTo函数中得到IRegionNavigationJournal的实现,然后就可以使用相应的方法。
三、InvokeCommandAction
直接由控件的事件触发,然后转化为Command。
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<ListBox x:Name="_listOfPeople" ItemsSource="{Binding People}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction Command="{Binding PersonSelectedCommand}" CommandParameter="{Binding SelectedItem, ElementName=_listOfPeople}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
上面的一种方式是传送一个其他的参数给命令。是否可以直接将事件参数传递给命令呢?
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<ListBox Grid.Row="1" Margin="5" ItemsSource="{Binding Items}" SelectionMode="Single">
<i:Interaction.Triggers>
<!-- This event trigger will execute the action when the corresponding event is raised by the ListBox. -->
<i:EventTrigger EventName="SelectionChanged">
<!-- This action will invoke the selected command in the view model and pass the parameters of the event to it. -->
<prism:InvokeCommandAction Command="{Binding SelectedCommand}" TriggerParameterPath="AddedItems" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
public IList<string> Items { get; private set; }
public DelegateCommand<object[]> SelectedCommand { get; private set; }
public MainWindowViewModel()
{
Items = new List<string>();
Items.Add("Item1");
Items.Add("Item2");
Items.Add("Item3");
Items.Add("Item4");
Items.Add("Item5");
// This command will be executed when the selection of the ListBox in the view changes.
SelectedCommand = new DelegateCommand<object[]>(OnItemSelected);
}
private void OnItemSelected(object[] selectedItems)
{
if (selectedItems != null && selectedItems.Count() > 0)
{
SelectedItemText = selectedItems.FirstOrDefault().ToString();
}
}