WPF MVVM实战系列教程(六、Prism区域导航)
⌨️ WPF MVVM实战系列教程
区域(Region)
在Prism中,引入了一个新的概念,叫Region(区域)。
Region 可以理解为 WPF 界面上的 “占位容器”,可以把不同的 View(视图)动态加载到这个容器中,无需在 XAML 中硬编码绑定,这是 Prism 实现模块化、松耦合 UI 的关键。

假设我们定义了两个Region,分别为Region1和Region2。
我们可以动态加载View(视图)到这两个Region里。
肯定有小伙伴会问,在WPF中,Frame控件也可以实现导航的功能,
是的,所以这里我们对比一下Frame和Region的区别
| Frame(WPF原生) | Region(Prism) | |
|---|---|---|
| 核心定位 | 页面(Page)导航控件 | 任意View的动态加载/切换容器 |
| 支持的视图类型 | 仅支持Page类型 |
支持任意UIElement(UserControl、Grid等) |
| 导航方式 | 基于XAML文件路径(如frame.Navigate(new Uri("Page1.xaml", UriKind.Relative))) |
基于View名称/类型(松耦合,无硬编码路径) |
| 模块化支持 | 弱,需手动管理页面与模块的关联 | 强,与Prism Module深度集成,天然支持模块化 |
| 生命周期 | 仅简单的导航事件(Navigated、Navigating) | 完整的导航生命周期(INavigationAware接口) |
| 多视图管理 | 仅支持单页面显示,无多视图激活/切换机制 | 支持多视图(TabControl/ItemsControl作为Region),可激活/停用指定View |
| 依赖注入 | 原生不支持,需手动实例化Page并传参 | 与Prism容器(Unity/DryIoc)深度集成,自动注入ViewModel/服务 |
| 参数传递 | 仅支持简单对象传参(Navigate的object参数) | 支持强类型参数(NavigationParameters),可在生命周期中获取 |
| 复用性 | 页面实例默认每次导航重建(可手动缓存) | 可通过IsNavigationTarget |
如何创建Region
1、引入Prism命名空间
1 xmlns:prism="http://prismlibrary.com/"
2、增加一个ContentControl
使用RegionManager.RegionName附加属性给区域命名
1 <ContentControl prism:RegionManager.RegionName="ContentRegion" />
完整代码如下所示
1 <Window x:Class="Regions.Views.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:prism="http://prismlibrary.com/" 5 Title="Shell" Height="350" Width="525"> 6 <Grid> 7 <ContentControl prism:RegionManager.RegionName="ContentRegion" /> 8 </Grid> 9 </Window>
如何实现区域导航
在前面介绍Bootstrapper启动器时,我们提到了Prism提供的一些服务,
其中就包括了IRegionManager:管理视图区域(Region),它能实现视图的动态加载 / 切换;
只需要通过构造函数注入IRegionManager,就可以使用IRegionManager接口进行区域导航
如下所示
1 public class MainWindowViewModel : BindableBase 2 { 3 private readonly IRegionManager regionManager; 4 5 public MainWindowViewModel(IRegionManager regionManager) 6 { 7 this.regionManager = regionManager; 8 } 9 }
IRegionManager提供了RequestNavigate函数用于导航 ,RequestNavigate有多种重载,较为完整的一种重载定义如下
1 /// <summary> 2 /// 此方法允许IRegionManager定位指定区域,并在其中导航至指定的目标URI,同时传递一个导航回调函数和一个NavigationParameters实例,该实例包含一组对象参数。 3 /// </summary> 4 /// <param name="regionName">区域名称</param> 5 /// <param name="target">一个表示区域将导航到的目标的URI。</param> 6 /// <param name="navigationCallback">导航完成后将执行的导航回调函数。</param> 7 /// <param name="navigationParameters">一个NavigationParameters实例,其中包含一组对象参数。</param> 8 void RequestNavigate(string regionName, Uri target, Action<NavigationResult> navigationCallback, INavigationParameters navigationParameters);
接下来我们演示一下
1、首先我们定义主界面,在界面顶部放置两个按钮,用于导航。然后在中间放置一个ContentControl,用于动态显示内容。
我们使用RegionManager.RegionName附加属性给区域命名
MainWindow.xaml
1 <Grid> 2 <Grid.RowDefinitions> 3 <RowDefinition Height="60"/> 4 <RowDefinition/> 5 </Grid.RowDefinitions> 6 7 <StackPanel Orientation="Horizontal"> 8 <Button Content="切换到ViewA" Width="88" Height="28" Margin="10" VerticalAlignment="Center" Command="{Binding NavigationToViewCommand}" CommandParameter="ViewA"></Button> 9 <Button Content="切换到ViewB" Width="88" Height="28" Margin="10" VerticalAlignment="Center" Command="{Binding NavigationToViewCommand}" CommandParameter="ViewB"></Button> 10 </StackPanel> 11 12 <ContentControl Grid.Row="1" prism:RegionManager.RegionName="NavigationArea"></ContentControl> 13 </Grid>

2、然后我们定义两个用户控件,命名为ViewA和ViewB,并分别创建对应的ViewModel
ViewA.xaml
1 <UserControl x:Class="_14_Prism_Region.Views.ViewA" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:_14_Prism_Region.Views" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <Grid> 10 <TextBlock Text="{Binding ViewName}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30"></TextBlock> 11 </Grid> 12 </UserControl>
ViewAViewModel.cs
1 public class ViewAViewModel : BindableBase 2 { 3 private string viewName = "ViewA"; 4 5 public string ViewName 6 { 7 get => this.viewName; 8 set => SetProperty(ref this.viewName, value); 9 } 10 }
ViewB.xaml
1 <UserControl x:Class="_14_Prism_Region.Views.ViewB" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:_14_Prism_Region.Views" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <Grid> 10 <TextBlock Text="{Binding ViewName}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30"></TextBlock> 11 </Grid> 12 </UserControl>
ViewBViewModel.cs
1 public class ViewBViewModel : BindableBase 2 { 3 private string viewName = "ViewB"; 4 5 public string ViewName 6 { 7 get => this.viewName; 8 set => SetProperty(ref this.viewName, value); 9 } 10 }
3、然后我们在Bootstrapper中注册需要进行导航的View,这一步很关键
这里可以只注册View,然后通过ViewModelLocator.AutoWireViewModel自动绑定ViewModel
1 containerRegistry.RegisterForNavigation<ViewA>(); 2 containerRegistry.RegisterForNavigation<ViewB>();
也可以在注册View的时候,同步绑定ViewModel(推荐)
1 containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>(); 2 containerRegistry.RegisterForNavigation<ViewB, ViewBViewModel>();
4、此时我们就可以在MainWindowViewModel中进行动态导航
MainWindowViewModel.cs
1 public class MainWindowViewModel : BindableBase 2 { 3 private IRegionManager regionManager; 4 5 public DelegateCommand<string> NavigationToViewCommand { get; private set; } 6 7 8 public MainWindowViewModel(IRegionManager regionManager) 9 { 10 //构造函数注入IRegionManager 11 this.regionManager = regionManager; 12 13 NavigationToViewCommand = new DelegateCommand<string>(NavigationToView); 14 } 15 16 private void NavigationToView(string viewName) 17 { 18 //导航 19 this.regionManager.RequestNavigate("NavigationArea", viewName); 20 } 21 }
运行效果

如何设置默认视图
如果我们想给某个区域设置默认设置,可以在Bootstrapper里重写OnInitialized函数,然后从容器中解析IRegionManager,再手动导航到指定的视图即可
1 public class Bootstrapper : PrismBootstrapper 2 { 3 protected override DependencyObject CreateShell() 4 { 5 return Container.Resolve<MainWindow>(); 6 } 7 8 protected override void RegisterTypes(IContainerRegistry containerRegistry) 9 { 10 containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>(); 11 containerRegistry.RegisterForNavigation<ViewB, ViewBViewModel>(); 12 } 13 14 protected override void OnInitialized() 15 { 16 base.OnInitialized(); 17 18 //设置默认视图 19 var regionManager = this.Container.Resolve<IRegionManager>(); 20 regionManager.RequestNavigate("NavigationArea", nameof(ViewA)); 21 } 22 }
也可以使用IRegionManager提供的 RegisterViewWithRegion函数,初始化时,给区域绑定一个默认视图。
1 regionManager.RegisterViewWithRegion("NavigationArea", typeof(ViewA));
导航通知
如果我们想在导航完成后,或者导航离开时,做出一些响应,应该如何处理呢?
Prism提供了INavigationAware接口,它为参与导航的对象提供了一种获取导航活动通知的方式。
定义如下:
1 // 2 // 摘要: 3 // 提供了一种方式,使参与导航的对象能够接收到导航的通知 4 // activities. 5 public interface INavigationAware 6 { 7 // 8 // 摘要: 9 // 被导航到时调用。 10 // 11 // 参数: 12 // navigationContext: 13 // The navigation context. 14 void OnNavigatedTo(NavigationContext navigationContext); 15 16 // 17 // 摘要: 18 // 被调用以确定此实例是否能够处理导航请求。(是否创建新实例) 19 // 20 // 参数: 21 // navigationContext: 22 // The navigation context. 23 // 24 // 返回结果: 25 // true if this instance accepts the navigation request; otherwise, false. 26 bool IsNavigationTarget(NavigationContext navigationContext); 27 28 // 29 // 摘要: 30 // 被导航离开时调用。 31 // 32 // 参数: 33 // navigationContext: 34 // The navigation context. 35 void OnNavigatedFrom(NavigationContext navigationContext); 36 }
我们只需要将需要处理导航事件View绑定的ViewModel继承自INavigationAware接口,然后做出相应的实现即可
1 public class ViewAViewModel : BindableBase, INavigationAware 2 { 3 ... 4 5 public void OnNavigatedTo(NavigationContext navigationContext) 6 { 7 //导航进入 8 } 9 10 public bool IsNavigationTarget(NavigationContext navigationContext) 11 { 12 //仅当目标区域中已存在该视图的实例,且再次导航到该视图类型时触发。 13 //返回 true:复用已存在的视图实例(不创建新实例,仅调用 OnNavigatedTo 更新数据)。 14 //返回 false:销毁已存在的视图实例,创建新的视图实例并导航到它。 15 return true; 16 } 17 18 public void OnNavigatedFrom(NavigationContext navigationContext) 19 { 20 //导航离开 21 } 22 23 ... 24 }
如何在区域导航时传递参数
这里主要用到IRegionManager.RequestNavigate的重载,如下所示
1 void RequestNavigate(string regionName, string target, NavigationParameters navigationParameters);
其中NavigationParameters是IEnumerable<keyvaluepair<string, object>>类型,也就是一个键值对列表
NavigationParameters使用方法如下所示:
1 NavigationParameters keyValuePairs = new NavigationParameters(); 2 keyValuePairs.Add("key", value);
下面介绍一下如何实现区域导航时传递参数
1、为了方便演示,我们定义一个数据模型ImageItem(方便演示,没有定义成可通知类型)
1 public class ImageItem 2 { 3 public string Name { get; set; } 4 5 public string Image { get; set; } 6 }
2、然后在ViewList中增加一个ListBox,
当ListBox选中项时,再点切换到ViewDetail,就可以在ViewDetail显示ListBox选中的项。
ViewList.xaml
1 <Grid> 2 <ListBox ItemsSource="{Binding ImageList}"> 3 <i:Interaction.Triggers> 4 <i:EventTrigger EventName="SelectionChanged"> 5 <i:InvokeCommandAction Command="{Binding SelectImageItemCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBox},Path=SelectedItem}"></i:InvokeCommandAction> 6 </i:EventTrigger> 7 </i:Interaction.Triggers> 8 <ListBox.ItemTemplate> 9 <DataTemplate> 10 <Grid Height="300" Width="400" Margin="10"> 11 <Grid.RowDefinitions> 12 <RowDefinition/> 13 <RowDefinition Height="30"/> 14 </Grid.RowDefinitions> 15 16 <Image Source="{Binding Image}"></Image> 17 <Label Content="{Binding Name}" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold"/> 18 </Grid> 19 </DataTemplate> 20 </ListBox.ItemTemplate> 21 </ListBox> 22 23 </Grid>
ViewListViewModel.cs
1 public class ViewListViewModel : BindableBase 2 { 3 private IRegionManager regionManager; 4 private ObservableCollection<ImageItem> imageList; 5 6 public DelegateCommand<ImageItem> SelectImageItemCommand { get; private set; } 7 8 public ObservableCollection<ImageItem> ImageList 9 { 10 get => this.imageList; 11 set => SetProperty(ref this.imageList, value); 12 } 13 14 public ViewListViewModel(IRegionManager regionManager) 15 { 16 SelectImageItemCommand = new DelegateCommand<ImageItem>(SelectImageItem); 17 18 //创建示例数据 19 CreateDemoData(); 20 21 this.regionManager = regionManager; 22 } 23 24 private void SelectImageItem(ImageItem item) 25 { 26 //构造NavigationParameters 27 NavigationParameters keyValuePairs = new NavigationParameters(); 28 keyValuePairs.Add("selected", item); 29 30 //传递参数 31 this.regionManager.RequestNavigate("NavigationArea", nameof(ViewDetail), keyValuePairs); 32 33 //不传递参数 34 //this.regionManager.RequestNavigate("NavigationArea", "ViewDetail"); 35 } 36 37 private void CreateDemoData() 38 { 39 ImageList = 40 [ 41 new ImageItem() {Name = "此情可待成追忆,只是当时已惘然",Image = "../imgs/1.jpg" }, 42 new ImageItem() { Name = "纵使相逢应不识,尘满面,鬓如霜。", Image = "../imgs/2.jpg" }, 43 ]; 44 } 45 }
3、在ViewDetail中点击返回,返回到ViewList
ViewDetail.xaml
1 <Grid> 2 <Grid.RowDefinitions> 3 <RowDefinition Height="40"/> 4 <RowDefinition/> 5 <RowDefinition Height="35"/> 6 </Grid.RowDefinitions> 7 8 <Button Content="返回" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0" Width="88" Height="28" Command="{Binding ReturnCommand}"></Button> 9 <Image Source="{Binding SelectedImageItem.Image}" Grid.Row="1"></Image> 10 <Label Content="{Binding SelectedImageItem.Name}" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold"></Label> 11 </Grid>
ViewDetailViewModel.cs
1 public class ViewDetailViewModel : BindableBase, INavigationAware 2 { 3 private IRegionManager regionManager; 4 private ImageItem selectedImageItem; 5 6 public ICommand ReturnCommand { get; private set; } 7 8 public ImageItem SelectedImageItem 9 { 10 get => selectedImageItem; 11 set => SetProperty(ref selectedImageItem,value); 12 } 13 14 public ViewDetailViewModel(IRegionManager regionManager) 15 { 16 this.regionManager = regionManager; 17 18 ReturnCommand = new DelegateCommand(Return); 19 } 20 21 private void Return() 22 { 23 //返回到列表界面 24 this.regionManager.RequestNavigate("NavigationArea", nameof(ViewList)); 25 } 26 27 public void OnNavigatedTo(NavigationContext navigationContext) 28 { 29 //导航进入 30 System.Windows.MessageBox.Show("导航进入ViewDetail"); 31 32 //获取传递过来的参数 33 this.SelectedImageItem = navigationContext.Parameters["selected"] as ImageItem; 34 } 35 36 public bool IsNavigationTarget(NavigationContext navigationContext) 37 { 38 //仅当目标区域中已存在该视图的实例,且再次导航到该视图类型时触发,返回 bool 值: 39 //返回 true:复用已存在的视图实例(不创建新实例,仅调用 OnNavigatedTo 更新数据)。 40 //返回 false:销毁已存在的视图实例,创建新的视图实例并导航到它。 41 return true; 42 } 43 44 public void OnNavigatedFrom(NavigationContext navigationContext) 45 { 46 //导航离开 47 System.Windows.MessageBox.Show("导航离开ViewDetail"); 48 } 49 }
运行效果

示例代码
https://github.com/zhaotianff/WPF-MVVM-Beginner/tree/main/14_Prism_Region

浙公网安备 33010602011771号