How are views entered into the user interface using PRISM and MEF?

I was already looking for some lessons and even looked at the plural approach. Introduction to PRISM. However, most examples are based on the use of unity containers and some lack of information on how to implement this function with the Mef container. My simple helloworld module is based on a web tutorial . My code is the same, except that I was stuck only on HelloModule and used Mef and not Unity, as the tutorial shows:

The main problem with initializing my view with my view model. The only working way I found through experiment is to initialize the view model in the view constructor:

HelloView.xaml.cs namespace Hello.View { [Export] public partial class HelloView : UserControl, IHelloView { public HelloView() { InitializeComponent(); Model = new HelloViewModel(this); } public IHelloViewModel Model { //get { return DataContext as IHelloViewModel; } get { return (IHelloViewModel)DataContext; } set { DataContext = value; } } } } 

And the standard module initialization code:

 [ModuleExport(typeof(HelloModule), InitializationMode=InitializationMode.WhenAvailable)] public class HelloModule : IModule { IRegionManager _regionManager; [ImportingConstructor] public HelloModule(IRegionManager regionManager) { _regionManager = regionManager; } public void Initialize() { _regionManager.Regions[RegionNames.ContentRegion].Add(ServiceLocator.Current.GetInstance<HelloView>()); } } 

However, can someone tell me the correct way how this happens, I have to do this in the module initialization section.

+6
source share
2 answers

MatthiasG shows a way to define modules in MEF. Please note: the view itself does not implement IModule. However, the interesting part of using MEF with PRISM is how to import modules into the user interface at startup.

I can explain the system in principle here, but it may point you in the right direction. There are always many approaches to everything, but this is what I understood as the best practice and that I made a very good experience:

Bootstrap

As with Prism and Unity, it all starts with Bootstrapper, which is obtained from MefBootstrapper at Microsoft.Practices.Prism.MefExtensions . The bootloader installs the MEF container and thus imports all types, including services, views, ViewModels and models.

Export of views (modules)

This is the part referenced by MatthiasG. My practice is the following structure for GUI modules:

  • The model exports itself as a specific type (it can also be an interface, see MatthiasG) using the [Export(typeof(MyModel)] attribute. Check with [PartCreationPolicy(CreationPolicy.Shared)] to indicate that only one instance is created (single user behavior).

  • ViewModel exports itself as its specific type in the same way as a model, and imports the model through constructor injection:

    [ImportingConstructor] public class MyViewModel (model MyModel) {_model = model; }

  • View imports ViewModel using the constructor insert, just as ViewModel imports Model

  • And now this is important: The View exports itself with a specific attribute, which is obtained from the attribute 'standard' [Export] . Here is an example:

 [ViewExport(RegionName = RegionNames.DataStorageRegion)] public partial class DataStorageView { [ImportingConstructor] public DataStorageView(DataStorageViewModel viewModel) { InitializeComponent(); DataContext = viewModel; } } 

[ViewExport] attribute

The [ViewExport] attribute has two functions: since it is derived from the [Export] attribute, it tells the MEF container to import the view. Like what? This is hidden in it by defintion: the constructor signature looks like this:

 public ViewExportAttribute() : base(typeof(UserControl)) {} 

By calling the [Export] constructor of type UserControl , each view is registered as a UserControl in the MEF container.

Secondly, it defines the RegionName property, which will later be used to determine in which area of โ€‹โ€‹the shell UI the view should be connected. The RegionName property is the only member of the IViewRegionRegistration interface. Attribute Class:

 /// <summary> /// Marks a UserControl for exporting it to a region with a specified name /// </summary> [Export(typeof(IViewRegionRegistration))] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] [MetadataAttribute] public sealed class ViewExportAttribute : ExportAttribute, IViewRegionRegistration { public ViewExportAttribute() : base(typeof(UserControl)) {} /// <summary> /// Name of the region to export the View to /// </summary> public string RegionName { get; set; } } 

Import Views

Now, the last important part of the system is the behavior that you attach to areas of your shell: AutoPopulateExportedViews . This imports your entire module from the MEF container with this line:

 [ImportMany] private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews; 

This imports all types registered as a UserControl from the container if they have a metadata attribute that implements IViewRegionRegistration . Since your [ViewExport] attribute does this, it means that you import every type marked with the [ViewExport(...)] sign.

The last step is to connect the views to the areas that bahvior does in it. OnAttach() property:

 /// <summary> /// A behavior to add Views to specified regions, if the View has been exported (MEF) and provides metadata /// of the type IViewRegionRegistration. /// </summary> [Export(typeof(AutoPopulateExportedViewsBehavior))] [PartCreationPolicy(CreationPolicy.NonShared)] public class AutoPopulateExportedViewsBehavior : RegionBehavior, IPartImportsSatisfiedNotification { protected override void OnAttach() { AddRegisteredViews(); } public void OnImportsSatisfied() { AddRegisteredViews(); } /// <summary> /// Add View to region if requirements are met /// </summary> private void AddRegisteredViews() { if (Region == null) return; foreach (var view in _registeredViews .Where(v => v.Metadata.RegionName == Region.Name) .Select(v => v.Value) .Where(v => !Region.Views.Contains(v))) Region.Add(view); } [ImportMany()] private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews; } 

Pay attention to .Where(v => v.Metadata.RegionName == Region.Name) . This uses the RegionName property of the attribute to get only those views that are exported for a specific region to which you are binding behavior.

The behavior binds to areas of your shell in the bootloader:

 protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors() { ViewModelInjectionBehavior.RegionsToAttachTo.Add(RegionNames.ElementViewRegion); var behaviorFactory = base.ConfigureDefaultRegionBehaviors(); behaviorFactory.AddIfMissing("AutoPopulateExportedViewsBehavior", typeof(AutoPopulateExportedViewsBehavior)); } 

Let's start with the full circle. I hope you get an idea of โ€‹โ€‹how everything happens with MEF and PRISM.

And, if you're still not bored: that's fine:

Mike Tautey screencast

+11
source

The implementation method of HelloView means that View must know the exact implementation of IHelloViewModel , which in some scenarios is beautiful, but means that you do not need this interface .

For the examples that I provide, I use property injection , but constructor injection will also be fine.

If you want to use interface , you can implement it as follows:

 [Export(typeof(IHelloView)] public partial class HelloView : UserControl, IHelloView { public HelloView() { InitializeComponent(); } [Import] public IHelloViewModel Model { get { return DataContext as IHelloViewModel; } set { DataContext = value; } } } [Export(typeof(IHelloViewModel))] public class HelloViewModel : IHelloViewModel { } 

Otherwise, it will look like this:

 [Export(typeof(IHelloView)] public partial class HelloView : UserControl, IHelloView { public HelloView() { InitializeComponent(); } [Import] public HelloViewModel Model { get { return DataContext as HelloViewModel; } set { DataContext = value; } } } [Export] public class HelloViewModel { } 

One more thing: if you do not want to modify your Views or provide several implementations of them, you do not need an interface for them.

+1
source

All Articles