Click here to Skip to main content
12,628,821 members (34,528 online)
Click here to Skip to main content
Add your own
alternative version

Stats

11.9K views
178 downloads
12 bookmarked
Posted

DataTemplate Injection

, 1 Jun 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Add XAML resources to an existing application

Introduction

Plug-ins are a mechanism for making an application more robust. By enabling a system to include libraries into it after its release, users can easily upgrade a system or simply add features which respond to their needs. When using Plug-in based development in WPF, a way for adding XAML resources into the application should be added as well.

Background

I was writing an application which analyzed data. The program was parsing some raw binary files and presenting the data found within it. The data was constructed by many different types. Each type had different properties and was presented in a unique way at separate parts of the application. The system was Plug-in based, which meant customers could write Plug-ins that improved the data parsing. Many times, requests to support new data types were received. I thought of enhancing the application so that our client could add the data types by themselves. This required enabling them to add DataTemplates to the application.

The System

Different locations on the application required different DataTemplates for each type. A class which holds meta data over the DataTemplate had to be created:

/// <summary>
/// This class holds all the required data over the data template.
/// </summary>
public class DataTemplateMetaData
{
    /// <summary>
    /// Describes where the data template should be used
    /// </summary>
    public string Case { get; set; }
    /// <summary>
    /// Describes which type the data handler handles
    /// </summary>
    public Type HandlerType { get; set; }

    /// <summary>
    /// The actual data template to use
    /// </summary>
    public DataTemplate DataTemplate { get; set; }

    public DataTemplateMetaData()
    {
    }
} 

These instances were to be loaded into the application by a class inside the Plug-in which implemented the following interface:

/// <summary>
/// interface which returns all the templates meta data
/// </summary>
public interface IDataTemplatesHandler
{
    IEnumerable<DataTemplateMetaData> GetDataTemplateDetails();
} 

The next stage is writing a loader which invokes these classes:

Assembly assembly = Assembly.LoadFile(file);
// Check if the assembly refernces this assembly. 
if (assembly.GetReferencedAssemblies().FirstOrDefault(
    c => c.Name == typeof(DataTemplateMetaDataLoader).Assembly.GetName().Name) != null)
    {
	foreach (Type dth in assembly.GetTypes())
	{
		// Check if the type implements the relevant interface
		if (
			dth.GetInterfaces().FirstOrDefault(
				w => w.Name == typeof(IDataTemplatesHandler).Name) 
						!= null)
		{
			IDataTemplatesHandler handler = 
			(IDataTemplatesHandler)Activator.CreateInstance(dth, true);
			_templatesList.AddRange(handler.GetDataTemplateDetails());
		}
	}
    } 	

Last but not least, a DataTemplateSelector which is able to handle these dynamically loaded types, had to be created:

/// <summary>
/// This class comes to handle the templates data templates selection
/// </summary>
public class DetailDataTemplateSelector : DataTemplateSelector
{
    /// <summary>
    /// This string states the type of Templates we're seeking
    /// </summary>
    public string Case { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        DataTemplate dataTemplate = null;
        if (item != null)
        {
            var details = DataTemplateMetaDataLoader.GetDetailsForCertainCase(Case).
                FirstOrDefault(
                    c => c.HandlerType == item.GetType());
            if (details != null)
            {
                dataTemplate = details.DataTemplate;
            }                
        }

        return dataTemplate;
    }

    private bool GetDataTemplateForSpecificType(Type type, out DataTemplate dataTemplate)
    {
        var details = DataTemplateMetaDataLoader.GetDetailsForCertainCase(Case).
            FirstOrDefault(
                c => c.HandlerType == type);
        if (details != null)
        {
            dataTemplate = details.DataTemplate;
        }
        else
        {
            dataTemplate = null;
        }
        return dataTemplate != null;
    }
}
<Window.Resources>
    <dti:DetailDataTemplateSelector x:Key="mainselector" Case="MainView" />
    <dti:DetailDataTemplateSelector x:Key="treeselector" Case="TreeView" />
</Window.Resources> 

Using the Code

The example solution is an application with a TreeView and a ContentPresenter which presents data over the selected TreeView node. The data within the tree is loaded using reflection from the Plug-in assemblies.

History

  • 2nd June, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Izhar Lotem
Software Developer
Israel Israel
Software Developer in a promising Clean-Tech company

You may also be interested in...

Comments and Discussions

 
QuestionCan't download source code Pin
ruben henares24-Feb-14 12:35
memberruben henares24-Feb-14 12:35 
GeneralSimilar approach Pin
zamzoid2-Jun-11 0:13
memberzamzoid2-Jun-11 0:13 
GeneralRe: Similar approach Pin
Izhar Lotem2-Jun-11 0:53
memberIzhar Lotem2-Jun-11 0:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161205.3 | Last Updated 2 Jun 2011
Article Copyright 2011 by Izhar Lotem
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid