如何加载模块并导航到模块?Prism

3

我有两个模块:ModuleLogging和ModuleItems。我Shell有声明:

<Grid>
   <DockPanel LastChildFill="True">
    <ContentControl  DockPanel.Dock="Top" prism:RegionManager.RegionName="{x:Static inf:RegionNames.TheUpperRegion}" Margin="5" />
    <ContentControl prism:RegionManager.RegionName="{x:Static inf:RegionNames.TheBottomRegion}" Margin="5"/>                     
   </DockPanel>

    <ContentControl prism:RegionManager.RegionName="{x:Static inf:RegionNames.TheWholeRegion}" Margin="5"  />
</Grid>

首先加载ModuleLogging,然后如果用户已经通过身份验证,我想加载ModuleItems

根据我的理解,我应该实例化:

我的引导程序类:

protected override IModuleCatalog CreateModuleCatalog()
{
   ModuleCatalog catalog = new ModuleCatalog();
   catalog.AddModule(typeof(ModuleLogging));
   //I've not added ModuleItems as I want to check whether the user is authorized
   return catalog;
}   

我尝试从ModuleLogging调用ModuleItems

Uri viewNav = new Uri("ModuleItems", UriKind.Relative);
regionManager.RequestNavigate(RegionNames.TheUpperRegion, viewNav);
regionManager.RequestNavigate(RegionNames.TheBottomRegion, viewNav); 

但我的ModuleLogging的LoggingView没有被ModuleItems的ToolBarView和DockingView所替代。相反,在ContentControls中,我只看到了System.Object和System.Object,而不是LoggingView所应有的控件,因为LoggingView是透明的。
是的,我知道内存中没有名为ModuleItems的对象。
如何按需加载模块并导航到视图?
2个回答

8

为使其正常工作,您可能需要对它们进行排序:

  • 将模块添加到目录中
  • 按需加载模块
  • 注册要在区域中显示的视图(或视图模型)
  • 请求导航至您的视图

让我们按顺序来看。

将模块添加到目录中

我不认为添加模块到目录中会有任何问题,但您可能希望在身份验证之后再初始化模块。为了使您的模块能够按需初始化,您可以使用标志OnDemand将其添加到目录中:

当请求时,该模块将被初始化,而不是在应用程序启动时自动初始化。

在启动引导程序中:

protected override void ConfigureModuleCatalog()
{
    Type ModuleItemsType = typeof(ModuleItems);
    ModuleCatalog.AddModule(new ModuleInfo()
    {
        ModuleName = ModuleItemsType.Name,
        ModuleType = ModuleItemsType.AssemblyQualifiedName,
        InitializationMode = InitializationMode.OnDemand
    });

按需加载模块

在身份验证成功后,您可以初始化您的模块:

private void OnAuthenticated(object sender, EventArgs args)
{
    this.moduleManager.LoadModule("ModuleItems");    
}

在这里,moduleManagerMicrosoft.Practices.Prism.Modularity.IModuleManager的一个实例(最好在类构造函数中注入)。

注册您的视图

您还需要在容器中注册您要导航到的视图(或如果您使用的是视图模型,则为视图模型)。最好的地方可能是您的模块 ModuleItems 的初始化,只需确保容器被注入到其构造函数中:

public ModuleItems(IUnityContainer container)
{
    _container = container;
}

public void Initialize()
{
    _container.RegisterType<object, ToolBarView>(typeof(ToolBarView).FullName);
    _container.RegisterType<object, DockingView>(typeof(DockingView).FullName);
    // rest of initialisation
}

请注意,为了使RequestNavigate起作用,您必须将视图注册为对象,并提供其完整名称作为名称参数。
此外,将模块用作导航目标(就像您在此处所做的那样:Uri viewNav = new Uri("ModuleItems", UriKind.Relative);)可能不是最好的选择。模块对象通常只是一个临时对象,在初始化模块后默认情况下会被丢弃。
请求导航
最后,一旦加载了您的模块,您可以请求导航。您可以使用上述moduleManager来挂钩到LoadModuleCompleted事件:
this.moduleManager.LoadModuleCompleted += (s, e)
    {
        if (e.ModuleInfo.ModuleName == "ModuleItems")
        {
            regionManager.RequestNavigate(
                RegionNames.TheUpperRegion,
                typeof(ToolBarView).FullName), 
                result => 
                { 
                   // you can check here if the navigation was successful
                });

            regionManager.RequestNavigate(
                RegionNames.TheBottomRegion,
                typeof(DockingView).FullName));
        }
    }

我应该在哪里订阅事件moduleManager.LoadModuleCompleted?是在方法OnAuthenticated(object sender,EventArgs args)中还是在ModuleItems的构造函数中? - StepUp
只要在调用LoadModule之前订阅它,实际上并不重要。因此,如果您在日志记录完成时调用OnAuthenticated并将订阅放在LoadModule之前,那么就应该没问题了。 - pangabiMC
顺便提一下,MSDN上有一篇关于这个主题的好文章,如果你使用MEF而不是Unity,它还附带了MEF示例:使用Prism Library 5.0为WPF进行模块化应用程序开发-按需加载模块 - pangabiMC
但是我不能在ModuleLogging中使用ToolBarView,因为它是紧密耦合的。我已经尝试在ModuleItems中订阅,但它没有起作用。 - StepUp
那是真的,但有几种方法可以解决它。从技术上讲,您不需要ToolBarView来导航到它,您只需要知道它的名称。但这对我来说仍然太耦合了。也许更好的方法是在ModuleItems.Initialise方法的结尾处直接调用RequestNavigate。 或者第三种,也许是最干净的选择,是在模块加载时通过EventAggregator触发事件,并由ModuleItems中的某个视图控制器单例对象订阅该事件。(请注意,IModule是一个临时对象,使用弱事件无法在那里订阅) - pangabiMC

3

最伟大的Magnus Montin帮助我解决了问题:

此代码位于ModuleLoggingLoggingViewmodel中:

private void DoLogin(object obj)
    {
        ShowAnotherView = false;
        if (UserName == "1" && UserPassword == "1")
        {                
            container.RegisterType<Object, TheUpperControl>("ModuleItems");
            Uri viewNav = new Uri("ModuleItems", UriKind.Relative);
            regionManager.RequestNavigate(RegionNames.TheUpperRegion, viewNav);
            regionManager.RequestNavigate(RegionNames.TheBottomRegion, viewNav);
            LoadTheOtherModule(); //load the other module
            var loginView = regionManager.Regions[RegionNames.TheWholeRegion].Views.ElementAt(0);
            regionManager.Regions[RegionNames.TheWholeRegion].Remove(loginView); //remove the login view
            //MessageBox.Show("");    
        }
        else
            ShowPromptingMessage = true;
    }

    private void LoadTheOtherModule()
    {
        Type moduleType = typeof(ModuleItems.ModuleItemsModule);
        ModuleInfo mi = new ModuleInfo()
        {
            ModuleName = moduleType.Name,
            ModuleType = moduleType.AssemblyQualifiedName,
        };
        IModuleCatalog catalog = container.Resolve<IModuleCatalog>();
        catalog.AddModule(mi);
        catalog.Initialize();

        IModuleManager moduleManager = container.Resolve<IModuleManager>();
        moduleManager.LoadModule(moduleType.Name); //run Initialize() of the other module
    }

它运行得像魔术一样!:)


实际上,这与我建议的几乎相同。嗯,没关系,我很高兴你的问题得到了解决。 - pangabiMC
1
@pangabiMC 非常感谢您的努力! :) 我已经为您的回复点赞。但是我需要更多解释性的答案,告诉我应该在哪里放置代码。 - StepUp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接