Prism区域管理实现导航

50 篇文章 0 订阅
5 篇文章 0 订阅
本文详细介绍了使用Prism和DryIoc在WPF应用程序中实现模块化导航,包括TabControl、ContentControl的高级用法,区域管理,以及如何注册、导航到不同模块的用户控件,涉及接口如INavigationAware和IConfirmNavigationRequest的扩展,以及导航日志的使用。
摘要由CSDN通过智能技术生成

之前有学过几种导航的实现方案,简单点的就是用TabControl;复杂就是给ContentControl绑定上一个Object类型的页面用户控件;再进阶一点就是使用容器装页面,ViewModel也一块注册;如果是使用了Prism,使用区域管理就变得非常简单了。

1、创建好的DryIoc空白项目中的MainWindow会有这样的一行

<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />

        --这个时候就可以添加按钮,添加好Command,使用RequestNavigate()管理这里显示的内容

2、ViewModel中

public DelegateCommand<string> OpenViewCommand { get; private set; }
// 区域管理字段
private readonly IRegionManager _regionManager;

public MainWindowViewModel(IRegionManager regionManager)
{
    OpenViewCommand = new DelegateCommand<string>(OpenView);
    this._regionManager = regionManager;    // 构造方法注入
}

private void OpenView(string obj)
{
    _regionManager.Regions["ContentRegion"].RequestNavigate(obj);
}

        -- obj就是注册的窗口名称

3、这些用户控件页面的调用是要注册的,在App.xaml.cs中注册

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterForNavigation<ViewA>();
    containerRegistry.RegisterForNavigation<ViewB>();
    containerRegistry.RegisterForNavigation<ViewC>();
}

-- 提示这个RegisterForNavigation是Prism.Wpf的拓展方法,记得要装好这个NuGet

跨模块的实现

解释:这里的跨模块指的是View位于不同的类库中(图中的ModuleA和ModuleB就是WPF的类库,Prism127是Prism.DryIoc的空项目),然后Prism127项目的写法跟上面没跨模块访问的差不多,都是实现了导航。

Prism127项目跟没跨模块的区别就是:App.xaml.cs文件中的RegisterTypes注册的用户控件页面变为重写方法ConfigureModuleCatalog中添加模块(AddModule)

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{


}
// 跨模块注册(手动重写),添加模块
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
    moduleCatalog.AddModule<ModuleAConfig>();   // 引入模块A
    moduleCatalog.AddModule<ModuleBConfig>();   // 引入模块B
    base.ConfigureModuleCatalog(moduleCatalog);
}

        -- ModuleAConfig和MOduleBConfig是模块A和模块B的IModule类。Prism127项目中要添加两个WPF类库的引用

然后ModuleA和ModuleB两个类库也是需要添加上NuGet包Prism.DryIoc,View就是用户控件页面,ModuleAConfig就要实现IModule接口,在方法RegisterTypes中注册导航(这个方法跟没有跨模块导航时候的注册用户控件页面是一样的)

public class ModuleAConfig : IModule
{
    public void OnInitialized(IContainerProvider containerProvider)
    {

    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterForNavigation<ViewA>();   // 引入视图
    }
}

也可以在外边把导航给注册了

public class MainModule : IModule
{
    public void OnInitialized(IContainerProvider containerProvider)
    {
        // 初始化的时候,添加一个组件到对应的区域
        // 比如 左侧菜单栏
        // 需要一个RegionManager

        var regionManager = containerProvider.Resolve<IRegionManager>();    // 通过ioc获取regionManager对象
        regionManager.RegisterViewWithRegion("LeftMenuTreeRegion", typeof(TreeMenuView));   // 向区域LeftMenuTreeRegion放入TreeMenuView

    }

    // 注册要放到Region的控件
    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.Register<TreeMenuView>();
    }
}

-- 同样是跨模块,App.xaml.cs的AddModule也是一样的。它自己在模块这边就用户控件放到区域中了

前端的使用可能是这样的

<!--  放置菜单的区域  -->
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="LeftMenuTreeRegion" />

对跨模块的实现的再次改进

改进的就是由原本App.xaml.cs中的ConfigureModuleCatalog添加模块变为CreateModuleCatalog实现

protected override IModuleCatalog CreateModuleCatalog()
{
    return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
}

通过这种方法就不需要ModuleA和ModuleB类库的引用的,变为使用他们两个的dll,这里写的是bin/debug/net6.0-windows,这样就可以将AB两个类库装到Modules文件夹下了,".\"则表示运行的当前文件

自动绑定ViewModel

首先View和ViewModel的文件夹要放在正确的位置,然后在View中添加。用户控件和窗体都支持

xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"

--自动匹配是比较少用的,还是在注册的时候连带ViewModel一起注册的

View和ViewModel一起注册(这个是跨模块的注册):

public void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
}

导航的消息传递

比如点击按钮就显示某一个区域,就像文章开头时那样。怎么传递消息呢?

private void Open(string obj)
{

    NavigationParameters keys = new NavigationParameters();
    keys.Add("Title", "Hello!");

    regionManager.Regions["ContentRegion"].RequestNavigate(obj, keys);
}

        -- 创建了一个导航参数,请求区域导航的时候记得将这个参数传过去。

接收参数的窗口要接上INavigationAware接口

接口的实现方法OnNavigatedTo中获取参数:

public void OnNavigatedTo(NavigationContext navigationContext)
{
    if (navigationContext.Parameters.ContainsKey("Title"))
    {
        Title = navigationContext.Parameters.GetValue<string>("Title");
    }
}

        -- 这个Title就是string类型,可以给View绑定。Title对应的消息就是Hello。

        -- 同时INavigationAware接口中也有一个方法叫做IsNavigationTarget,如果返回Ture就是对这个页面重用,下次调用这个页面的时候就还是那一个,而不是重新创建一个。

        -- 这个接口还有个方法就是OnNavigatedFrom方法,这个方法时切换请求其它区域的时候就会调用。

对接口IsNavigationTarget的再拓展:

有一个接口叫IConfirmNavigationRequest也是继承了接口IsNavigationTarget的,但是多了一个接口同名的方法,用来对是否导航进行验证。

public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
    bool result = true;

    // 点击否就不导航
    if (MessageBox.Show("确认导航?", "温馨提示", MessageBoxButton.YesNo) == MessageBoxResult.No)
    {
        result = false;
    }

    // 这个就是判断给不给导航的
    continuationCallback(result);
}

导航日志

导航日志可以记录导航的页面,方便返回

在ViewModel中定义区域导航

private IRegionNavigationJournal journal;   // 导航日志

让一个按钮Command联系上导航返回

private void Back()
{
    if (journal.CanGoBack)
        journal.GoBack();
}

这个导航要实现是建立在每次导航请求都记录日志的基础上的,所以导航请求就要记录日志

private void Open(string obj)
{

    NavigationParameters keys = new NavigationParameters();
    keys.Add("Title", "Hello!");

    regionManager.Regions["ContentRegion"].RequestNavigate(obj, callBack =>
    {
        if ((bool)callBack.Result)
        {
            journal = callBack.Context.NavigationService.Journal;
        }
    }, keys);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值