// this class is documented here:
// http://blog.thekieners.com/2010/09/06/type-gettype-implementation-with-help-of-xamlreader/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;
namespace Nova.Core.Common
{
///
/// Provides functionality to load any type with its class name, namespace and assembly-name within the Silverlight environment.
///
///
/// The Type.GetType method is different in Silverlight than in the standard .NET runtime. In Silverlight we have to provide the
/// fully qualified assembly name to get a type in a custom assembly. Only build in controls or types in the same assembly are
/// excluded from this rule. Full qualified assembly name means a syntax like the following:
/// MyComponent.MyType, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4bec85d7bec6698f.
/// This class uses the XamlReader capability to resolve type during parsing a xaml-string. While this is a little time consuming
/// the TypeLoader maintains a cache to get types faster.
///
public static class TypeLoader
{
// cache for resolved type
private static readonly Dictionary _cache = new Dictionary();
///
/// Gets the System.Type with the specified name, name space and assembly name.
///
/// The class name without namespace.
/// The name space
/// The name of the assembly containing the type.
/// The type matching the provided parameters or null if not found.
//[DebuggerStepThrough()]
public static Type GetType(string className, string nameSpace, string assemblyName)
{
// check
if (StringHelper.IsNullOrWhiteSpace(nameSpace))
return null;
string xamlNamespace = string.Format("clr-namespace:{0}", nameSpace);
// assembly name is optional
if (!StringHelper.IsNullOrWhiteSpace(assemblyName))
xamlNamespace += string.Format(";assembly={0}", assemblyName);
return GetType(className, xamlNamespace);
}
///
/// Gets the System.Type with the specified name.
/// This method overload can be used for:
/// 1. core controls such as Button, Grid, ListBox, etc. without specifying the namespace or assembly name.
/// 2. with the qualified assembly name of the type without version and public key token like this: "MyNamespace.MyType, MyAssembly".
///
/// Pure class name of Core Controls such as Button, Grid, ListBox, etc.
/// The type matching the provided parameters or null if not found.
//[DebuggerStepThrough()]
public static Type GetType(string className)
{
if (className != null && className.Contains(","))
{
string[] qualifiedNameParts = className.Split(',');
if (qualifiedNameParts.Length == 2)
{
string[] fullClassNameParts = qualifiedNameParts[0].Split('.');
if (fullClassNameParts.Length > 0)
{
// classname
string newClassName = fullClassNameParts.Last().Trim();
// namespace
string nameSpace = "";
for (int i = 0; i < fullClassNameParts.Length - 1; i++)
{
nameSpace += fullClassNameParts[i] + ".";
}
nameSpace = nameSpace.TrimEnd('.');
string assemblyName = qualifiedNameParts[1].Trim();
return GetType(newClassName, nameSpace, assemblyName);
}
}
}
return GetType(className, "");
}
///
/// Gets the System.Type with the specified name. The xaml namespace specifies the namespace and assembly name in the same syntax as in xaml.
///
/// The class name without namespace.
///
/// The xaml namespace. This is the same syntax as used in XAML syntax.
/// Example: "clr-namespace:MyComponent.SubNamespace;assembly=MyAssemblyName
///
/// The type matching the provided parameters or null if not found.
//[DebuggerStepThrough()]
public static Type GetType(string className, string xamlNamespace)
{
// check input
if (StringHelper.IsNullOrWhiteSpace(className))
return null;
if (className.Contains("."))
throw new ArgumentException(
"className must not include the namespace. Please provide namespace with separate parameter.");
// check if type is already in cache
string key = xamlNamespace + "&" + className;
if (_cache.ContainsKey(key))
return _cache[key];
lock (_cache)
{
try
{
// check again because another thread might be faster and has already created the cache-entry
if (_cache.ContainsKey(key))
return _cache[key];
// create xaml with a simply Style element and set the TargetType property with the provided type name
string xaml = " // set the xaml namesapce if provided
if (!StringHelper.IsNullOrWhiteSpace(xamlNamespace))
{
xaml += string.Format("xmlns:tmp='{0}' TargetType='tmp:{1}' />", xamlNamespace, className);
}
else
{
// Core controls such as Button, Grid, ListBox, etc do not need a namespace
xaml += string.Format("TargetType='{0}' />", className);
}
// let the XamlParser load the type via the TargetType property
var style = XamlReader.Load(xaml) as Style;
if (style != null)
{
Type targetType = style.TargetType;
AddToCache(key, targetType);
return targetType;
}
}
catch (Exception ex)
{
// Try to load type in executing assembly
if (!StringHelper.IsNullOrWhiteSpace(xamlNamespace))
{
// note: Type.GetType uses needs assembly-qualified name of the type to get. If the type is
// in the currently executing assembly or in Mscorlib.dll, it is sufficient to supply
// the type name qualified by its namespace.
Type type =
Type.GetType(string.Format("{0}.{1}",
xamlNamespace.Replace("clr-namespace:", "").TrimEnd(';'),
className));
if (type != null)
{
// add to cache
AddToCache(key, type);
return type;
}
}
//****** DONT SET VALUE TO NULL, BECAUSE OF CASES WHEN AN ASSEMBLY IS *****
//****** LOADED DYNAMICALLY INTO THE APPLICATION DOMAIN *****
// don't let the exception repeat. Set null as cache value
AddToCache(key, null);
//**************************************************************************/
}
}
return null;
}
private static void AddToCache(string key, Type type)
{
_cache.Add(key, type);
CompositionTarget.Rendering -= CompositionTarget_Rendering;
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private static void CompositionTarget_Rendering(object sender, EventArgs e)
{
CompositionTarget.Rendering -= CompositionTarget_Rendering;
_cache.Clear();
}
#region Nested type: StringHelper
private static class StringHelper
{
// helper because .NET 3.5 does not support IsNullOrWhiteSpace
public static bool IsNullOrWhiteSpace(string str)
{
if (str == null)
return true;
if (str.Trim() == string.Empty)
return true;
return false;
}
}
#endregion
}
}