Search code examples
wpfmvvmprismmef

WPF MEF + Prism initial Region loading


I have written an MVVM app in WPF using MEF and Prism with three different regions. Code is across two modules, being discovered in the App.Config.

I have all the navigation commands and structure working perfectly fine, but the one thing I am confused on is how to set the initial Views loaded into each region at app startup as there seems to be nowhere I can do this. Furthermore if I add something to the end of the MainViewModel constructor to explicitly navigate to screen set A, something else seems to be overriding it and loading a different set of views to start with.

It also seems dependent on what order I load the modules in on the app.config which seems undesirable. If I load the Admin module last, it loads a set of screens from the admin module, if I load the search module last, it loads a set of views from the search module and in this case, it is not even finding a View for the main region.

What is the method for specifying what Views are loaded into each region at app startup when using MEF and config discovery?

using System;
using System.ComponentModel.Composition;
using Microsoft.Practices.Prism.Regions;

namespace CRM.GUI.WPF.Shared.Infrastructure.Behaviour
{
    [Export(typeof(AutoPopulateExportedViewsBehavior))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class AutoPopulateExportedViewsBehavior : RegionBehavior, IPartImportsSatisfiedNotification
    {
        protected override void OnAttach()
        {
            AddRegisteredViews();
        }

        public void OnImportsSatisfied()
        {
            AddRegisteredViews();
        }

        private void AddRegisteredViews()
        {
            if (Region != null)
            {
                foreach (var viewEntry in RegisteredViews)
                {
                    if (viewEntry.Metadata.RegionName == Region.Name)
                    {
                        var view = viewEntry.Value;

                        if (!Region.Views.Contains(view))
                        {
                            Region.Add(view);
                        }
                    }
                }
            }
        }

        [ImportMany(AllowRecomposition = true)]
        public Lazy<object, IViewRegionRegistration>[] RegisteredViews { get; set; }
    }
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    [MetadataAttribute]
    public class ViewExportAttribute : ExportAttribute, IViewRegionRegistration
    {
        public ViewExportAttribute()
            : base(typeof(object))
        { }

        public ViewExportAttribute(string viewName)
            : base(viewName, typeof(object))
        { }

        public string RegionName { get; set; }
    }

Used

[ViewExport(RegionName = RegionNames.MainRegion)]
public partial class ReportView

Solution

  • Based on my understanding, Prism loads and show by default the first View that gets registered on each Region (Only the first View would be shown if the region is set on a ContentControl item).

    Therefore, you could deactivate the undesired Views on each RegionBehavior you don't want to show at startUp. This would make that when the desired StartUp View is added, it would get activated as there is no other active View yet.

    Another alternative would be to register each View on the corresponding Module initialize() method, instead of using RegionBehaviours. So finally, after adding each View to the corresponding Region, you would decide to deactivate the View whether it is the StartUp View or not.

    UPDATE:

    The following implementation shows a possible alternative for deactivating non-startup Views on each RegionBehavior. In order to get a more elegant solution, you could create a dictionary or a simple static class that would return the StartUpView name for the corresponding Region, and then call it as shown below:

    private void AddRegisteredViews()
    {
       ...
           var view = viewEntry.Value;
    
           if (!Region.Views.Contains(view))
           {
                Region.Add(view);
                if (view.GetType().Name != StartUpViewNames.getViewNameFromRegion(Region))
                {
                     Region.deactivate(view);
                }
           }
      ...
    }
    

    Notice that I after the StartUpView is found and it is kept active, it continues deactivating the following added views, but you could leave them active. As I mentioned, the View that would be shown, would be the first one which gets Active in the Region.

    I hope this helps, Regards.