Language: C#
Convention based ViewLocator (uses MEF but does not require it)
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
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.
Report Abuse
Subscribe
Discuss
What's new
What is it
New Snippet
Recent Snippets
My Snippets
Web Code
Search

