New Snippet New Snippet Recent Snippets Recent Snippets My Snippets My Snippets Web Code Search Snippets Search
Sign inor Register
Language: C#

Convention based ViewLocator (uses MEF but does not require it)

187 Views
Copy Code Show/Hide Line Numbers
namespace Nowcom.Quicksilver
{
    using System;
    using System.Reflection;
    using System.Windows;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
 
    [Export]
    public class ViewContainer 
    {
        [ImportMany(typeof(UIElement), AllowRecomposition = true)]
        public List<ExportFactory<UIElement>> Views { get; set; }
    }
 
    public class ViewLocator
    {  
        public static UIElement Locate(object viewModel)
        {
            if (viewModel == null)
                return null;
 
            //First check for a custom Attribue ViewTemplate to see if a View is associated with this view Model
            //This is so we can specify the same View for multiple ViewModels
            var type = viewModel.GetType();
            string contractName =type.AssemblyQualifiedName;
            UIElement view = null;
 
            var viewTemplate = type.GetCustomAttribute<ViewTemplateAttribute>();
            if (viewTemplate != null)
            {
                //If we have a fully qualified name lets use that. Specific namespace in a specific assembly
                if (!string.IsNullOrEmpty(viewTemplate.QualifiedName))
                    contractName = viewTemplate.QualifiedName;
                else
                {
                    contractName = contractName.Replace(type.Name, viewTemplate.Name);
                }
            
                //Lets try MEF first to see if we can find it via an Export using name in ViewTemplate.
                view = FindView(contractName);
                if (view != null)
                    return view;
            }
           
            //Use MEF and try and find with AssemblyQualified name first
            contractName = GetViewName(contractName);
            view = FindView(contractName);
            if (view != null)
                return view;
 
            //Use MEF to see if there is a view in the same namespace as the viewmodel. Doesn't matter if in different assemblies
            var fullName = GetViewName(string.Format("{0}.{1}", type.Namespace, type.Name));
            view = FindView(fullName);
            if (view != null)
                return view;
 
            //None of the previous steps found the view so lets get the type and create it.
            var viewType = Type.GetType(contractName);
            if (viewType == null)
                return null;
 
            return (UIElement)Activator.CreateInstance(viewType);
        }
     
        private static string GetViewName(string viewModelName)
        {
            return viewModelName.Replace("Model", string.Empty).Replace("Service", string.Empty);
        }
 
        public static UIElement FindView(string contractName)
        {
            var viewContainer = Container.GetExportedValueOrDefault<ViewContainer>(null);
            foreach (var viewExport in viewContainer.Views)
            {
                var view = viewExport.CreateExport();
                Type type = view.Value.GetType();
 
                if(type.AssemblyQualifiedName == contractName || type.FullName == contractName)
                    return view.Value as UIElement;
 
                view.Dispose();
            }
 
            return null;
        }
    }
}
 
namespace Nowcom.Quicksilver
{
    using System.ComponentModel.Composition;
    using System.Windows;
    using System;
 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class ViewAttribute: ExportAttribute
    {   
        public ViewAttribute():base(typeof(UIElement))
        {
        }   
    }
}
 
namespace Nowcom.Quicksilver
{
    using System;
 
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class ViewTemplateAttribute: Attribute
    {
        public string QualifiedName { get; set; }
        public string Name { get; set; }
 
        public ViewTemplateAttribute()
        {
        }
    }
}
by Robert Kozak
  April 07, 2010 @ 4:25pm
Tags:
Comment:
Sample showing a Convention Based ViewLocator using MEF. Convention: ViewModel = View or Service = View. Uses MEF to find a UIElement attributed with a [View] in any assembly with same Namespace. If not found in MEF it will CreateInstance on the type. I don't really like this implementation because I have to create each View to see if it matches. This could have been solved if ExportFactory<T> had a property of the Type it will create.

Add a comment


Report Abuse
brought to you by:
West Wind Techologies


If you find this site useful and use it frequently please consider making a donation to support this free service.
Donate