Click here to Skip to main content
14,216,563 members
Click here to Skip to main content
Article
Posted 1 Jun 2011

Stats

20.2K views
178 downloads
12 bookmarked

DataTemplate Injection

,
Rate this:
4.25 (4 votes)
Please Sign up or sign in to vote.
4.25 (4 votes)
1 Jun 2011     CPOL    
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<code>, 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

Comments and Discussions

 
QuestionCan't download source code Pin
ruben henares24-Feb-14 11:35
memberruben henares24-Feb-14 11:35 
GeneralSimilar approach Pin
MarkRainey1-Jun-11 23:13
memberMarkRainey1-Jun-11 23:13 
GeneralRe: Similar approach Pin
Izhar Lotem1-Jun-11 23:53
memberIzhar Lotem1-Jun-11 23: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.