Type.GetType implementation with help of XamlReader

The Type.GetType method is different in Silverlight than in the standard .NET runtime. In Silverlight we must provide the fully qualified assembly name to get a type from an assembly. Only built in controls such as Button, Grid, ListBox, etc. or types in the executing assembly are excluded from this rule. Fully qualified assembly name means you must provide the version, culture and public key token. That means that the following GetType usage works well, until the target assembly version changes from 1.0 to 1.1.

Type.GetType("MyComponent.MyType, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4bec85d7bec6698f")
 

And do we really want to have such Xaml or code behind files?

<local:MyCustomElement Grid.Row="2" Grid.Column="1"
   ItemType="MyComponent.MyType, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4bec85d7bec6698f" >
</local:MyCustomElement>

 

Iterate over Assembly Parts

One option to load a type just by its type-name and assembly-name is to iterate over all loaded assemblies. The method AppDomain.CurrentDomain.GetAssemblies() does not exist in Silverlight, but we can iterate over all assembly parts that are included in the deployment, e.g. all assemblies that are in the XAP file. I’ve found a blog post from Malcolm Jack in which he describes how to iterate over the parts to find a type by its name.

An this is how it works in brief: loop through each assembly part, getting the assembly, then use assembly.GetType to load the type.

This is the code this short explanation:

public static Type GetAssemblyType(string assemblyName, string className)
{
    StreamResourceInfo info = Application.GetResourceStream(new Uri(assemblyName, UriKind.Relative));
    Assembly assembly = new AssemblyPart().Load(info.Stream);
    Type type = assembly.GetType(className);
    return type;
}

public static Type GetAssemblyType(string className)
{
    Type type = null;
    foreach (AssemblyPart part in Deployment.Current.Parts)
    {
        type = GetAssemblyType(part.Source, className);
        if (type != null)
            break;
    }
    return type;
}

 

Unfortunately this solution has two drawbacks:

1. Dynamic loaded assemblies downloaded with help of a web client instance are not part of the deployment, e.g. the code cannot find types in those assemblies. You can add an additional part to the collection after each downloaded dll, but you do not have a guarantee this will be done.

AssemblyPart part = new AssemblyPart();
Assembly assm = part.Load(e.Result);
Deployment.Current.Parts.Add(part);

 

2. The second issue is when using application library caching to reduce the XAP size.

clip_image001

If this option is enabled, it will change the application manifest and the XAP no longer includes those assemblies which do support assembly caching. See Tim Heuer’s posts to get more detail how it works.

The cached assemblies are now declared as external parts as you can see in the manifest file (AppManifest.xml):

<Deployment ... >
  <Deployment.Parts>
     ...
  </Deployment.Parts>
  <Deployment.ExternalParts>
    <ExtensionPart Source="System.Windows.Controls.Input.Toolkit.zip" />
    <ExtensionPart Source="System.Xml.Linq.zip" />
    <ExtensionPart Source="System.Windows.Controls.zip" />
    <ExtensionPart Source="System.Windows.Controls.Toolkit.zip" />
  </Deployment.ExternalParts>
</Deployment>

 

In the end when the application starts those external assemblies will still be downloaded before the main form is added to the visual tree.

clip_image002

Iterating over all external parts is not that easy as iterating over all assembly parts, because it needs to download the source again and this is an asynchron operation.

 

The Solution: Using the XamlReader

I’ve found a solution by using the XamlReader to resolve types during parsing a xaml string. Unfortunately there is no StringToTypeConverter in Silverlight and the Xaml parser uses some hard coded logic to resolve System.Type properties only for certain type like Style and ControlTemplate. However, we can use a Style or ControlTemplate class and use the TargetType property to retrieve the type information. And these lines of code show how it works:

// create xaml with a simply Style element and set the TargetType property with the provided type name
string xaml = "<Style xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' ";

// set the xaml namesapce if provided
if (!string.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 
Style style = XamlReader.Load(xaml) as Style;

if (style != null)
{
    Type targetType = style.TargetType;
    return targetType;
}

 

Xaml parsing is not the fastest way to get a type. Therefore I encapsulated the functionality into a small class which maintains a cache for types which are already parsed.

The class provides four different ways to get a type:

// for types in the executing assembly or types in the core-control 
// assembly such as Button, Grid, etc. Just provide the type name.
TypeLoader.GetType("Grid"); 

// The type name with its xaml namespace
TypeLoader.GetType("Grid", "clr-namespace:System.Windows.Controls;assembly=System.Windows");

// Type name, namespace and assembly name as separate parameters
TypeLoader.GetType("Grid", "System.Windows.Controls", "System.Windows");

// Or with the assembly qualified type name, without version, culture and public key token
TypeLoader.GetType("System.Windows.Controls.Grid, System.Windows");

 

Here are some other examples:

// *** toolkit controls ***
// returns null, because NumericUpDown is in toolkit and not in core control assembly
//TypeLoader.GetType("NumericUpDown"); 
TypeLoader.GetType("NumericUpDown", 
               "clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit");
TypeLoader.GetType("NumericUpDown", 
               "System.Windows.Controls", "System.Windows.Controls.Input.Toolkit");
TypeLoader.GetType("System.Windows.Controls.NumericUpDown, System.Windows.Controls.Input.Toolkit");

// *** main application ***
// returns null, because MainPage is not in core-control assembly
//Type mainPage1 = TypeLoader.GetType("MainPage"); 
TypeLoader.GetType("MainPage", "clr-namespace:TypeLoaderDemo;assembly=TypeLoaderDemo");
TypeLoader.GetType("MainPage", "TypeLoaderDemo", "TypeLoaderDemo");
TypeLoader.GetType("TypeLoaderDemo.MainPage, TypeLoaderDemo");

// *** 3rd party libs ***
TypeLoader.GetType("MyButton", "clr-namespace:TypeLoaderControlLib;assembly=TypeLoaderControlLib");
TypeLoader.GetType("MyButton", "TypeLoaderControlLib", "TypeLoaderControlLib");
TypeLoader.GetType("TypeLoaderControlLib.MyButton,TypeLoaderControlLib");
 

Demo Project and Source Code

 

Here you can find the TypeLoader implementation or a full demo project including TypeLoader source, examples, assembly parts iteration and dynamic assembly loading.

 
Advertisements
Tagged

5 thoughts on “Type.GetType implementation with help of XamlReader

  1. Type.GetType implementation with help of XamlReader « Kiener’s Blog…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. johnzered says:

    Thank you! I’ve been struggling with this myself for some time. It would be great if you could fix the download links though, it just gives me a “Endpoint not found.”

  3. beatkiener says:

    Thank you for your feedback. I had problems with my webhoster, now the link should work.

  4. johnzered says:

    Yes, now it works. This turned out to be very usefull to me, thank you once again!

  5. Amir says:

    This only works for DependencyObject types (try s:Int32 and it won’t work) – damn.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Advertisements
Advertisements
%d bloggers like this: