Prism / MEF: How to RegisterViewWithRegion Without Hard-Coding the Region Name
We are building a WPF Prism application. We have different developers working on different module projects, and multiple modules are injected into the main Application Shell. The main application is also a separate project. We also want to be able to use the modules in different applications. We do not want to have to name the Regions with the same names in every application.
For instance, say we have a module to be used in two different applications. In one application, its developer may name the module's region "DetailsRegion," and in the other, its developer may name it "ResultsRegion."
Every example I can find registers the View with the Region by hard-coding the region name in the module's class definition:
myRegionManager.RegisterViewWithRegion("RegionNameHere", GetType(ModuleViewType))
What I want to do is put the Region name in the main application's app.config file, and pass this name to the module. Something like this:
In the main Shell Application's app.config:
<Modules>
<SearchModule>
<add key="RegionName" value="SearchRegion" />
</SearchModule>
</Modules>
And in the module's class file:
Dim settings As NameValueCollection = CType(ConfigurationManager.GetSection("Modules/SearchModule"), NameValueCollection)
Dim regionName as str开发者_如何学Cing = settings("RegionName")
myRegionManager.RegisterViewWithRegion(regionName, GetType(SearchModuleType)
In a way, this would be the last step to completely decouple the modules from the shell and from each other.
This works perfectly in the views of the module. But I cannot do it in the module's class definition file, as ConfigurationManager is not available at that level.
I can do this by putting the region name in the ApplicatonSettings section of the module's app.config. But this defeats the purpose of being able to store the module in one location to be loaded by multiple applications. It really needs to be in the main application's app.config.
Is there a way to register a module's View with a Region, without hard-coding the name of the Region in the code? We try so hard NOT to hard-code anything. Is it truly necessary here?
As Meleak already mentioned in his comment: Use a static class
namespace Infrastructure
{
public static class RegionNames
{
public const string MainRegion = "MainRegion";
}
}
In your xaml code you can use the region name as follows:
<UserControl
xmlns:Inf="clr-namespace:Infrastructure;assembly=Infrastructure"
xmlns:Regions="clr-namespace:Microsoft.Practices.Prism.Regions;assembly=Microsoft.Practices.Prism">
<ContentControl Regions:RegionManager.RegionName="{x:Static Inf:RegionNames.MainRegion}"/>
</UserControl>
I got it. Turns out I was wrong on one point, and I apologize for that. The parent application's .config settings ARE available at the Module class definition level. One must add the correct references and make the correct Imports (or using) entries. I must have been napping at the keyboard.
In the host application's app.config, add configSection definitions. Here I define sections for two modules:
<configSections>
<sectionGroup name="Modules">
<section name="SearchModule" type="System.Configuration.NameValueSectionHandler" />
<section name="HeaderModule" type="System.Configuration.NameValueSectionHandler"/>
</sectionGroup>
...
</configSections>
In the host application's app.config, add a Modules section, and a subsection for each module:
<Modules>
<SearchModule>
<add key="Region" value="SearchRegion"/>
</SearchModule>
<HeaderModule>
<add key="Region" value="HeaderRegion"/>
</HeaderModule>
</Modules>
In the Module project, add a reference to System.Configuration.dll. Add "Imports" (VB) or "using" (C#) for System.Collections.Specialized and System.Configuration:
VB:
Imports System.Collections.Specialized
Imports System.Configuration
C#:
using System.Collections.Specialized;
using System.Configuration;
In the Initialize method of the Module's Class definition file:
VB:
Public Sub Initialize() Implements Microsoft.Practices.Prism.Modularity.IModule.Initialize
Dim settings As NameValueCollection = CType(ConfigurationManager.GetSection("Modules/SearchModule"), NameValueCollection)
MyRegionManager.RegisterViewWithRegion(settings("Region"), GetType(SearchModuleView))
End Sub
C#:
public void Initialize() : Microsoft.Practices.Prism.Modularity.IModule.Initialize
{
(NameValueCollection)settings = (NameValueCollection)ConfigurationManager.GetSection("Modules/SearchModule");
MyRegionManager.RegisterViewWithRegion(settings["Region"], typeof(SearchModuleView));
}
This, then, registers the View with a Region from entries made in the Host application's app.config. This means one module can be built for multiple host applications, and it can be inserted in a Region of any name in the Host. No need to make changes in compiled code, or to make a separate RegionNames class for each application.
Our application is also built using MVVM architecture. We define the View-Models in the Host application, and expose them to the modules by names defined the app.config using RegionContext or EventAggregator. This now completely decouples the modules from the application, and makes the modules totally reusable in different applications without modification.
Thanks for the input, and I hope this helps someone else in the future.
精彩评论