Views and viewmodels grouped in subfolders

Jun 12, 2013 at 6:39 AM
Edited Jun 12, 2013 at 6:39 AM
Hi,

typically a business application has several views and viewmodels and I would like to group them in subfolders, for example:

Views\Login
Views\Home

If I put views in different subfolders it seems they are not registered in the container.

For the viewmodels the only way I found is to put viewmodels in different sufolders but with the same namespace; in this way I can define an easy rule to resolve viewmodels.
Maybe I can use a different rule to resolve viewmodels, for example replacing Views with ViewModels in the full type name. Could it work?
Jun 12, 2013 at 5:59 PM
The default looks for the views in the Views folder and view models in the ViewModels folder. If you want your own scheme, you need to add some additional code and store them where you wish. I use the default, but try looking for GetPageNameToTypeResolver Method or the AdventureWorks app as a demo.
Coordinator
Jun 13, 2013 at 6:36 AM
There is also documentation that describes how to change the conventions for where views and view models are located. You can find it Using Prism for the Windows Runtime.
Jun 13, 2013 at 6:52 AM
Hi,

I'll try overriding GetPageType.
Thank you very much.
Jun 27, 2013 at 1:46 AM
What I do is I moved them into different folder but i keep the namespace to Views

For example the view LoginView is in the folder appName\Views\Login
but the namespace is still appName.Views

it works like a charm :-D
Jun 27, 2013 at 4:39 PM
@Halloween, that is a really good idea. I never thought of that, but believe it will help in managing views as my app grows over time. Thanks!
Aug 15 at 3:56 PM
Edited Aug 15 at 3:57 PM
@OasisLiveForever:

Could you please share the codesnippet that you used for overrriding?
At this moment (august 2014) I get a warning message in desingtime, that there is no reference for GetTypeInfo() what is part of the code on page 35/36 of the documentation.

Thanks!

Regards,
Aug 15 at 5:07 PM
@PeterKlein:

In my solution I have an assembly that contains all ViewModels organized in folders and the main app assembly that contains Views in Views folder.
To resolve views and viewmodels I use Unity as dependency injection framework that I wrapped with this class:
public static class AppContainer
    {
        private static readonly IUnityContainer _container = new UnityContainer();
        private static readonly Dictionary<string, Type> _typeRegistrations = new Dictionary<string, Type>();

        public static void RegisterType(string name, Type type)
        {
            _typeRegistrations[name] = type;
        }

        public static Type GetType(string name)
        {
            if (_typeRegistrations.ContainsKey(name))
            {
                return _typeRegistrations[name];
            }
            else
            {
                return null;
            }
        }

        public static void Register<T>()
        {
            Register<T>(null, false);
        }

        public static void Register<T>(string name)
        {
            Register<T>(name, false);
        }

        public static void RegisterTransient<T>()
        {
            Register<T>(null, true);
        }

        public static void RegisterTransient<T>(string name)
        {
            Register<T>(name, true);
        }

        private static void Register<T>(string name, bool transient)
        {
            LifetimeManager lifetime;

            if (transient)
            {
                lifetime = new TransientLifetimeManager();
            }
            else
            {
                lifetime = new ContainerControlledLifetimeManager();
            }

            if (!string.IsNullOrWhiteSpace(name))
            {
                _container.RegisterType<T>(name, lifetime);
            }
            else
            {
                _container.RegisterType<T>(lifetime);
            }
        }

        public static void Register<T1, T2>() where T2 : T1
        {
            Register<T1, T2>(null, false);
        }

        public static void Register<T1, T2>(string name) where T2 : T1
        {
            Register<T1, T2>(name, false);
        }

        public static void RegisterTransient<T1, T2>() where T2 : T1
        {
            Register<T1, T2>(null, true);
        }

        public static void RegisterTransient<T1, T2>(string name) where T2 : T1
        {
            Register<T1, T2>(name, true);
        }

        private static void Register<T1, T2>(string name, bool transient) where T2 : T1
        {
            LifetimeManager lifetime;

            if (transient)
            {
                lifetime = new TransientLifetimeManager();
            }
            else
            {
                lifetime = new ContainerControlledLifetimeManager();
            }

            if (!string.IsNullOrWhiteSpace(name))
            {
                _container.RegisterType<T1, T2>(name, lifetime);
            }
            else
            {
                _container.RegisterType<T1, T2>(lifetime);
            }
        }

        public static void RegisterInstance<T>(T instance)
        {
            RegisterInstance(instance, null);
        }

        public static void RegisterInstance<T>(T instance, string name)
        {
            if (!string.IsNullOrWhiteSpace(name))
            {
                _container.RegisterInstance<T>(instance);
            }
            else
            {
                _container.RegisterInstance<T>(name, instance);
            }
        }

        public static object GetInstance(Type type)
        {
            return _container.Resolve(type);
        }

        public static object GetInstance(Type type, string name)
        {
            return _container.Resolve(type, name);
        }

        public static T GetInstance<T>()
        {
            return _container.Resolve<T>();
        }

        public static T GetInstance<T>(string name)
        {
            return _container.Resolve<T>(name);
        }
    }
In main app I have defined two functions to registers views and viewmodels (NavigationAwarePageViewModel is my custom base class defined in ViewModels assembly that inherits from Prism ViewModel):
public static void RegisterViewModels()
        {
            // Register all types available in ViewModels assembly.
            Assembly viewModelsAssembly = typeof(NavigationAwarePageViewModel).GetTypeInfo().Assembly;

            foreach (TypeInfo viewModelType in viewModelsAssembly.DefinedTypes.Where(type => type.IsClass &&
                                                                                             !string.IsNullOrWhiteSpace(type.Namespace)))
            {
                AppContainer.RegisterType(viewModelType.Name, viewModelType.AsType());
            }

            // Set rule to resolve viewmodel from view type name.
            ViewModelLocator.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
            {
                return AppContainer.GetType(string.Concat(viewType.Name, "ViewModel"));
            });
        }

public static void RegisterViews()
        {
            // Register all types available in current assembly that are defined in Views namespace.
            Assembly viewsAssembly = typeof(App).GetTypeInfo().Assembly;
            string viewsNamespace = string.Concat(viewsAssembly.GetName().Name, ".Views");

            foreach (TypeInfo viewType in viewsAssembly.DefinedTypes.Where(type => type.IsClass &&
                                                                                   !string.IsNullOrWhiteSpace(type.Namespace) &&
                                                                                   type.Namespace.StartsWith(viewsNamespace)))
            {
                AppContainer.RegisterType(viewType.Name, viewType.AsType());
            }
        }
Then I overridden Prism app base class methods to initialize and resolve views and viewmodels:
protected override Type GetPageType(string pageId)
        {
            return AppContainer.GetType(pageId);
        }

        protected override object Resolve(Type type)
        {
            return AppContainer.GetInstance(type);        
        }

        protected override void OnInitialize(IActivatedEventArgs args)
        {
            base.OnInitialize(args);

            // Other initializations
            
            RegisterViewModels();
            RegisterViews();
        }
Aug 18 at 8:59 AM
@OasisLiveForever:

Thanks for this extensive explanation and the example code, I have not yet studied it completely, but it looks promising.

Peter
Aug 18 at 2:15 PM
@OasisLiveForever

I Have had some time to dive a bit deeper into your code snippets, but that uses the very same construct that rendered an error fot me earlier (see my initial reply). I cannot use .GetTypeInfo() like you do in the following snippet:

OasisLiveForever wrote:
public static void RegisterViews()
   {
      // Register all types available in current assembly that are defined in Views namespace.
      Assembly viewsAssembly = typeof(App).GetTypeInfo().Assembly;
       string viewsNamespace = string.Concat(viewsAssembly.GetName().Name, ".Views");

At this moment (august 2014) I get a warning message in desingtime, that there is no reference for GetTypeInfo() what is part of the code on page 35/36 of the documentation as it is is your codesnipppet.
Aug 18 at 2:41 PM
@PeterKlein:
Maybe an example poject helps I've posted a few month ago:
Examle
Pages are grouped like in ASP.NET MVC in different 'modules'.
Have a look also athe the App.xaml.cs file where I've definded the methods to look up in different Folders for instantiation
       protected override Type GetPageType(string pageToken)
       {
            Assembly assemblyQualifiedAppType = GetType().GetTypeInfo().Assembly;
            IEnumerable<TypeInfo> namespaces = assemblyQualifiedAppType.DefinedTypes
                .Where(t => OnPredicate(t, String.Format(".Modules.{0}.Views", pageToken)));
            string endsWithName = string.Format(CultureInfo.InvariantCulture, "{0}Page", pageToken);
            TypeInfo typeInfo = namespaces.FirstOrDefault(p => p.FullName.EndsWith(endsWithName));
            if (typeInfo != null)
                return typeInfo.AsType();
            return null;
       }

        private Type GetViewModelType(string pageToken, string fullName)
        {
            string pageTokenWithoutPage = fullName.Replace(".Views.", ".ViewModels.");
            int positionDot = pageTokenWithoutPage.LastIndexOf('.');
            pageTokenWithoutPage = pageTokenWithoutPage.Substring(0, positionDot);

            Assembly assemblyQualifiedAppType = GetType().GetTypeInfo().Assembly;
            IEnumerable<TypeInfo> namespaces = assemblyQualifiedAppType.DefinedTypes
                .Where(t => OnPredicateViewModel(t, pageTokenWithoutPage));
            string endsWithName = string.Format(CultureInfo.InvariantCulture, "{0}ViewModel", pageToken);
            TypeInfo typeInfo = namespaces.FirstOrDefault(p => p.FullName.EndsWith(endsWithName));
            if (typeInfo != null)
                return typeInfo.AsType();
            return null;
        }

        private bool OnPredicate(TypeInfo t, string viewType)
        {
            string nameSpaceStart = GetType().Namespace + viewType;
            return t.Namespace != null && (t.IsClass && t.Namespace.StartsWith(nameSpaceStart));
        }

        private bool OnPredicateViewModel(TypeInfo t, string viewTypeFullName)
        {
            //string nameSpaceStart = GetType().Namespace;// + viewType;
            return t.Namespace != null && (t.IsClass && t.Namespace.StartsWith(viewTypeFullName));
        }
The example is generated by a MDE tool which generates Prism for Windows Runtime solutions.
I' m working for months on that tools but still have not a lot of time to go on.
Aug 18 at 2:53 PM
Edited Aug 18 at 2:54 PM
@PeterKlein
I'm using Prism for Windows 8.1, not the Converged release since it doesn't have validation support.
Are you creating an universal app?
Tue at 10:09 PM
@Oasisliveforever: The version I use is exactly the same version that you use. The project I have created is a Windows 8.1 Store HubApp, not a universal app.

@ judgy: Thanks for your reply, but your code uses exactly the same command that won't work in my VB.Net environment, namely the GetType().GetTypeInfo().Assembly command. So it does not give a useful solution.