Trying to implement some View Model factory

Oct 4, 2010 at 8:19 AM
Edited Oct 4, 2010 at 8:22 AM

Hi,

In my code I successfully implement a class which compose objects and find view model in very few simple steps. However I use a custom made code to find the view and, because of that, I hide the definition of the catalogs, which bother me. I wonder if I can use MEF's catalog to find my views!

Code is below, but here is how it basically Works.

I have a Composition class.
It has a Register(Assembly)  method which create the 1]. MEF catalog and also 2]. use reflection to find all type tagged with "DataViewAttribute" in the arg assembly.
It has a Compose(object) method which compose data with MEF. 
It has a GetView(object) method which find a View (a class tagged with DataViewAttribute) for this data and compose it.

Now what I'd like to do is, in GetView() to use a MEF catalog
(instead of having to centralize the catalog creation and reflection in the Register() method).

Any tip on how  achieve that?

 

 

DataViewAttribute (that tags View)

 

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class DataViewAttribute : Attribute
{
	public DataViewAttribute()
	{
	}
	public DataViewAttribute(Type t)
	{
		DataType = t;
	}
	public Type DataType { get; set; }

	// utility to find DataView(s)
	public static IEnumerable<Type> GetDataTypes(Type t)
	{
		if (t == null)
			yield break;
		var attrs = Attribute.GetCustomAttributes(t, typeof(DataViewAttribute));
		if (attrs == null)
			yield break;

		foreach (DataViewAttribute a in attrs)
			yield return a.DataType;
	}
}

 Composition class (which merge MEF code and DataView finding)

public static class Composition
{
	static object locker = new object();

	/// <summary>
	/// Call upon MEF to fill all <see cref="ImportAttribute"/> of an object.
	/// </summary>
	/// <param name="o"></param>
	public static void Compose(object o)
	{
		if (o == null)
			return;
		var batch = new CompositionBatch();
		batch.AddPart(o);
		Container.Compose(batch);
	}

	/// <summary>
	/// Ask MEF for a given exported object.
	/// </summary>
	public static T GetInstance<T>()
	{
		return Container.GetExportedValue<T>();
	}

	/// <summary>
	/// Get the view for an object. A UI element would be returned verbatim.
	/// For other, the API try to find the appropriate view tagged with the type 
	/// of the data as its <see cref="DataViewAttribute.DataType"/>.
	/// </summary>
	/// <remarks>It will se the data as the <see cref="FrameworkElement.DataContext"/>
	/// and <see cref="Conpose(object)"/> the view</remarks>.
	public static object GetView(object data)
	{
		if (data == null)
			return null;

		if (data is UIElement)
			return (UIElement)data;

		var tt = data.GetType();
		foreach (var dataType in dataViews.Keys)
			if (dataType.IsAssignableFrom(tt))
			{
				var result = Activator.CreateInstance(dataViews[dataType]);
				var ui = result as FrameworkElement;
				if (ui != null)
				{
					ui.DataContext = data;
					Compose(ui);
					return ui;
				}
			}
		return data;
	}

	/// <summary>
	/// Register an assembly to be in the MEF catalog as well as one to look for
	/// view for model (<seealso cref="DataViewAttribute"/>)
	/// </summary>
	public static void Register(Assembly a)
	{
		if (a == null)
			return;
		lock (locker)
		{
			if (assemblies.ContainsKey(a))
				return;
			assemblies[a] = true;
			Catalog.Catalogs.Add(new AssemblyCatalog(a));
			LoadDataView(a);
		}
	}

	#region internals

	static Dictionary<Assembly, bool> assemblies = new Dictionary<Assembly, bool>();
	static Dictionary<Type, Type> dataViews = new Dictionary<Type, Type>();

	static void LoadDataView(Assembly a)
	{
		foreach (var t in a.GetExportedTypes())
			if (typeof(FrameworkElement).IsAssignableFrom(t))
				foreach (var dataType in DataViewAttribute.GetDataTypes(t))
					dataViews[dataType] = t;
	}

	static AggregateCatalog Catalog
	{
		get
		{
			if (catalog == null)
				lock (locker)
					if (catalog == null)
					{
						catalog = new AggregateCatalog();
						//catalog.Catalogs.Add(new AssemblyCatalog(GetType().Assembly));
						//catalog.Catalogs.Add(new DirectoryCatalog("AddIns"));
					}
			return catalog;
		}
	}
	static AggregateCatalog catalog;

	static CompositionContainer Container
	{
		get
		{
			if (container == null)
				lock (locker)
					if (container == null)
						container = new CompositionContainer(Catalog);
			return container;
		}
	}
	static CompositionContainer container;

	#endregion
}