Prism导航

发布时间 2023-12-07 17:13:00作者: ZHIZRL

注册导航页面

注册区域

使用p:RegionManager.RegionName注册页面区域

<Window x:Class="Zhaoxi.PrismRegion.Navigation.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Zhaoxi.PrismRegion.Navigation"
        xmlns:p="http://prismlibrary.com/"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <DockPanel>
        <Grid Width="220" DockPanel.Dock="Left">
            <StackPanel>
                <Button Content="View A" Margin="0,3"
                        Command="{Binding OpenViewCommand}"
                        CommandParameter="ViewA"/>
                <Button Content="View B" Margin="0,3"
                        Command="{Binding OpenViewCommand}"
                        CommandParameter="ViewB"/>
                <Button Content="View C" Margin="0,3"/>
            </StackPanel>
        </Grid>
        <Grid>
            <!--<ContentControl p:RegionManager.RegionName="ViewRegion"/>-->
            <TabControl p:RegionManager.RegionName="ViewRegion">
                <TabControl.ItemContainerStyle>
                    <Style TargetType="TabItem">
                        <!--TabItem的绑定数据源是页面对象-->
                        <!--TabItem的DataContext=View对象-->
                        <!--View对象的DataContext=对应的ViewModel-->
                        <Setter Property="Header" Value="{Binding DataContext.Title}"/>
                    </Style>
                </TabControl.ItemContainerStyle>
            </TabControl>
        </Grid>
    </DockPanel>
</Window>

注册界面

注册两个UserControl用于测试

<UserControl x:Class="Zhaoxi.PrismRegion.Navigation.Views.Pages.ViewA"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Zhaoxi.PrismRegion.Navigation.Views.Pages"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <StackPanel>
        <TextBox Text="View A" Foreground="Orange" FontSize="20"/>
        <TextBlock Text="{Binding Title}" FontSize="20" Foreground="Red"/>
        <Button Content="Close" Command="{Binding CloseTabCommand}"/>
    </StackPanel>
</UserControl>
<UserControl x:Class="Zhaoxi.PrismRegion.Navigation.Views.Pages.ViewB"
             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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:local="clr-namespace:Zhaoxi.PrismRegion.Navigation.Views.Pages"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBlock Text="View B" Foreground="Green" FontSize="20"/>

    </Grid>
</UserControl>

RegisterTypes方法中注册界面

    public class Startup : PrismBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            // 注册需要导航的子页面,只有注册了才能处理
            containerRegistry.RegisterForNavigation<ViewA>();
            containerRegistry.RegisterForNavigation<ViewB>();
        }
    }

 界面关联区域

使用IRegionManager提供的RegisterViewWithRegion方法将界面名称关联到"ViewRegion"区域

    public class MainWindowViewModel
    {
        public ICommand OpenViewCommand { get; set; }

        // 区域管理,需要拿到RegionManager
        IRegionManager _regionManager;
        public MainWindowViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;

            OpenViewCommand = new DelegateCommand<string>(DoOpenView);
        }

        private void DoOpenView(string viewName)
        {
            _regionManager.RegisterViewWithRegion("ViewRegion", viewName);
        }
    }

 运行效果如下

导航页面传参——INavigationAware接口

 页面对应的ViewAViewModel继承INavigationAware接口并实现方法

public class ViewAViewModel : INavigationAware
    {
        public string Title { get; set; } = "View A";
        public ICommand CloseTabCommand { get; set; }

        IRegionManager _regionManager;
        public ViewAViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            CloseTabCommand = new DelegateCommand(DoCloseTab);
        }
        private void DoCloseTab()
        {

        }
        #region INavigationAware接口方法
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            // 是否允许重复导航进来
            // 主-》A-》B-》A (显示前对象)  返回True
            // 主-》A-》B-》A(新对象)     返回false
            return true;

            // 编辑页面,通过主页的子项点击,打开这个页面,
            // 子项有很多,可能同时打开多个子项进行编辑
        }
        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
            // 从当前View导航出去的时候触发
            // 从某个页面跳转到另一个页面的时候,可以把这个信息带过去
            navigationContext.Parameters.Add("abcd", "Hello ViewA");
        }
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            // 打开当前View的时候触发
            string arg = navigationContext.Parameters.GetValue<string>("abcd");
        }
        #endregion
    }

IsNavigationTarget

当IsNavigationTarget方法返回false时每次导航都会新建

 

OnNavigatedTo

在MainWindowViewModel中创建NavigationParameters对象,使用RequestNavigate在关联区域的同时传入NavigationParameters对象

public class MainWindowViewModel
    {
        public ICommand OpenViewCommand { get; set; }

        // 区域管理,需要拿到RegionManager
        IRegionManager _regionManager;
        public MainWindowViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;

            OpenViewCommand = new DelegateCommand<string>(DoOpenView);
        }

        private void DoOpenView(string viewName)
        {
            //_regionManager.RegisterViewWithRegion("ViewRegion", viewName);
            // 打开/显示某个View的时候,希望给个参数它?
            // 上面的语句无法传参
            // 需要请求的View,先注册到容器中
            //_regionManager.RequestNavigate("ViewRegion", viewName);

            // 向某个View中传递特定参数,参数对接到View的ViewModel里
            if (viewName == "ViewA")
            {
                NavigationParameters parameters = new NavigationParameters();
                parameters.Add("abcd", "Hello");
                _regionManager.RequestNavigate("ViewRegion", viewName, parameters);
            }
            else if (viewName == "ViewB")
                _regionManager.RequestNavigate("ViewRegion", viewName);
        }
    }

 

OnNavigatedFrom

从当前View导航出去的时候触发,跳转到另一个页面的时候,可以把这个信息传递到下一个页面

第一次打开ViewB时无参数传进

打开ViewA,再次点击ViewB时参数从ViewA的OnNavigatedFrom传进ViewB的OnNavigatedTo

自动销毁——IRegionMemberLifetime接口

页面的ViewModel继承IRegionMemberLifetime接口

接口中的KeepAlive参数默认为true:非激活状态,在Region中保留

public class ViewAViewModel : INavigationAware,IRegionMemberLifetime
    {
        public string Title { get; set; } = "View A";
        public ICommand CloseTabCommand { get; set; }

        IRegionManager _regionManager;
        public ViewAViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            CloseTabCommand = new DelegateCommand(DoCloseTab);
        }
        private void DoCloseTab()
        {

        }
        
        #region IRegionMemberLifetime接口方法
        // 用来控制当前页面非激活状态,是否在Region中保留
        public bool KeepAlive => true;

        #endregion

        #region INavigationAware接口方法
        
        #region INavigationAware接口方法
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            // 是否允许重复导航进来
            // 主-》A-》B-》A (显示前对象)  返回True
            // 主-》A-》B-》A(新对象)     返回false
            return true;

            // 编辑页面,通过主页的子项点击,打开这个页面,
            // 子项有很多,可能同时打开多个子项进行编辑
        }
        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
            // 从当前View导航出去的时候触发
            // 从某个页面跳转到另一个页面的时候,可以把这个信息带过去
            navigationContext.Parameters.Add("abcd", "Hello ViewA");
        }
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            // 打开当前View的时候触发
            string arg = navigationContext.Parameters.GetValue<string>("abcd");
        }
        #endregion
    }

将KeepAlive参数改为false

public bool KeepAlive => false;

运行后点击ViewB,再点击ViewA

再次点击ViewB后,ViewA被自动销毁

页面离开前的事件——IConfirmNavigationRequest接口

页面ViewModel实现IConfirmNavigationRequest接口

public class ViewAViewModel : INavigationAware,IRegionMemberLifetime,IConfirmNavigationRequest
    {
        public string Title { get; set; } = "View A";
        public ICommand CloseTabCommand { get; set; }

        IRegionManager _regionManager;
        public ViewAViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            CloseTabCommand = new DelegateCommand(DoCloseTab);
        }
        private void DoCloseTab()
        {

        }
        
        #region IRegionMemberLifetime接口方法
        // 用来控制当前页面非激活状态,是否在Region中保留
        public bool KeepAlive => true;

        #endregion

        #region INavigationAware接口方法
        
        #region INavigationAware接口方法
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            // 是否允许重复导航进来
            // 主-》A-》B-》A (显示前对象)  返回True
            // 主-》A-》B-》A(新对象)     返回false
            return true;

            // 编辑页面,通过主页的子项点击,打开这个页面,
            // 子项有很多,可能同时打开多个子项进行编辑
        }
        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
            // 从当前View导航出去的时候触发
            // 从某个页面跳转到另一个页面的时候,可以把这个信息带过去
            navigationContext.Parameters.Add("abcd", "Hello ViewA");
        }
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            // 打开当前View的时候触发
            string arg = navigationContext.Parameters.GetValue<string>("abcd");
        }
        #endregion
        
        #region IConfirmNavigationRequest接口方法
        // 当从当前页面跳转到另一个页面时触发
        // OnNavigatedFrom调用前执行
        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
        {
            // 打开某个页面,
            if (MessageBox.Show("是否离开当前页面?", "导航提示", MessageBoxButton.YesNo) ==
                MessageBoxResult.Yes)
            {
                /// 继续打开
                /// 
                continuationCallback?.Invoke(true);
            }
            else
                // 不被导航
                continuationCallback?.Invoke(false);
        }
        #endregion
    }

运行后点击ViewA,再点击ViewB的时候会弹窗提醒