A WinRt Plug In System Approach based on custom IXamlMetadataProvider Implementation

Tags: Xaml WinRt

While working on a plug in system for WinRt and Metro style apps I realized a custom implementation of the IXamlMetadataProvider interface. The goal of my solution was to use no reflection for the reasons I mentioned in my previous blog post. My approach uses a view model base class which has some management methods for properties. With this methods I read and write the values of the properties. All view models have to be registered at a central view model registration. The custom IXamlMetadataProvider implementation is then using the central view model registration to look up view model types and is using the management methods to work with the view model.

Here is my solution in detail:

View Model Base Class

In my view model base class I have two dictionaries to store information about the properties:

// Dictionaries to manage the view model properties
public IDictionary<string, object> PropertyValues { get; set; }
public IDictionary<string, Type> PropertyTypes { get; set; }

To create a property I provide a method to initialize these dictionaries:

/// <summary>
/// Creates a property, registers type and 
/// default value to the view model
/// </summary>
protected void CreateProperty<T>(string propertyName)
{
    PropertyTypes[propertyName] = typeof(T);
    PropertyValues[propertyName] = default(T);
}

I save the type of the property and initialize it with the default value. To read and write the property data I implemented two helper methods:

/// <summary>
/// Gets the value of a property
/// </summary>
protected T GetPropertyValue<T>(string propertyName)
{
    return (T)PropertyValues[propertyName];
}
 
/// <summary>
/// Sets the value of a property
/// </summary>
protected void SetPropertyValue<T>(string propertyName, T value)
{
    if (PropertyValues[propertyName] == null
        || !PropertyValues[propertyName].Equals(value))
    {
        PropertyValues[propertyName] = value;
        OnPropertyChanged(propertyName);
    }
}

With these methods a view model implementation can read from and write to its properties

View Model Implementation

A view model implementation has to derive from the view model base class, register its properties and optionally provide a normal property getter and/or setter to the registered properties. Here is an example:

public class HelloWorldViewModel : ViewModelBase
{
    public HelloWorldViewModel()
    {
        // Register property
        CreateProperty<string>("HelloWorldText");
 
        // Initalize with default value
        HelloWorldText = "Hello World WinRt Plugin!";
    }
 
    /// <summary>
    /// Helper to access the property.
    /// </summary>
    public string HelloWorldText
    {
        get { return GetPropertyValue<string>("HelloWorldText"); }
        set { SetPropertyValue("HelloWorldText", value); }
    }
}

View Model Registration

The central information store about view model metadata is the XamlTypeProvider class. It is implemented as a singleton and has information about all registered view models in two dictionaries:

private IDictionary<Type, IXamlType> _xamlTypesByType;
private IDictionary<string, IXamlType> _xamlTypesByName;
 
/// <summary>
/// Registers a view model to the type provider
/// </summary>
public void RegisterViewModelType<T>(IXamlType baseType = null) 
where T : ViewModelBase, new() { IXamlType xamlType = new ViewModelXamlType<T>(baseType); AddType(typeof(T), xamlType); } public void AddType(Type underlyingType, IXamlType xamlType) { _xamlTypesByType[underlyingType] = xamlType; _xamlTypesByName[underlyingType.FullName] = xamlType; }

Here I create a ViewModelXamlType for each registered view model type. This class is my custom implementation of the WinRt IXamlType interface. In this class I create a dictionary containing each member of the view model type. To do this I create a dummy of the view model and read the property information from it:

_members = new Dictionary<string, ViewModelXamlMember<T>>();
 
// Create a dummy view model to read the property information
T dummy = Activator.CreateInstance<T>();
 
foreach (KeyValuePair<string, Type> property in dummy.PropertyTypes)
{
    IXamlType propertyType = 
XamlTypeProvider.Instance.GetType(property.Value); _members.Add(
property.Key,
new ViewModelXamlMember<T>(property.Key, this, propertyType)); }

The ViewModelXamlMember class is my custom implementation of the WinRt IXamlMember interface.

To register the view model you have to call the RegisterViewModel method once at application startup for each view model. To do this I created an interface which is called on application start:

public class PluginInitializer : IPluginInitializer
{
    public void Initialize()
    {
        XamlTypeProvider.Instance
.RegisterViewModelType<HelloWorldViewModel>(); } }

IXamlMetadataProvider implementation

The custom implementation of the WinRt IXamlMetadataProvider is very simple. I just use the view model registration to read the type information:

public class ViewModelXamlMetadataProvider 
: IXamlMetadataProvider { public IXamlType GetXamlType(string fullName) { return XamlTypeProvider.Instance.GetType(fullName); } public IXamlType GetXamlType(Type type) { return XamlTypeProvider.Instance.GetType(type); } public XmlnsDefinition[] GetXmlnsDefinitions() { return new XmlnsDefinition[0]; } }

Application Startup and Bootstrapping

At application startup I need to load all of my plug in assemblies and run the Initialize method of the IPluginInitializer interface. Then I dynamically load some views into my application. The naming of the view has to match the following convention <<RegionName_ViewName.xaml>>. So I can query all views for a specified region and load the views into this region.

As you can see in the following screenshot, the plug in and the application do not have a reference to each other. They use both the plug in system which does everything necessary at runtime.

 

To deploy the plug ins to the appx folder at development time, I added some simple xcopy commands in the post build event in the ExtensibleMetroApp project.

What is this solution good for?

One advantage of this solution is that you can add plug ins to your app without recompiling your app. Of course you still have the restrictions given by WinRt. The one which matters here is that you cannot add assemblies after deployment of the app. But for an enterprise solution you can provide your own packaging tool with your app using the packaging api. So the customer of your app can create its individual package with the plug ins needed in the app and deploy it in the enterprise network. What you can do at runtime is to add xaml views to your app. In these views you can then use the view models available in your already deployed assemblies.

Here you can download the sample implementation of my solution. Please be aware that you should see it as a proof of concept. Advanced use cased like indexers are not implemented in my example implementation.

It is just an approach, hope it helps!

No Comments

Add a Comment