65.9K
CodeProject 正在变化。 阅读更多。
Home

DataTemplate 注入

2011 年 6 月 2 日

CPOL

1分钟阅读

viewsIcon

23758

downloadIcon

210

将 XAML 资源添加到现有应用程序

引言

插件是一种使应用程序更健壮的机制。通过允许系统在其发布后包含库,用户可以轻松升级系统或简单地添加响应其需求的特性。在使用基于插件的开发时,也应添加一种将 XAML 资源添加到应用程序的方法。

背景

我正在编写一个分析数据的应用程序。该程序解析一些原始二进制文件并呈现其中找到的数据。数据由许多不同的类型构成。每种类型都有不同的属性,并在应用程序的不同部分以独特的方式呈现。该系统是基于插件的,这意味着客户可以编写插件来改进数据解析。经常收到支持新数据类型的请求。我想增强应用程序,以便我们的客户可以自行添加数据类型。这需要他们能够将 DataTemplates 添加到应用程序。

系统

应用程序的不同位置需要每种类型不同的 DataTemplates。必须创建一个包含关于 DataTemplate 元数据的类。

/// <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()
    {
    }
} 

这些实例将通过插件内部的一个类加载到应用程序中,该类实现了以下 接口

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

下一步是编写一个调用这些类的加载器

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());
		}
	}
    } 	

最后但并非最不重要的一点,必须创建一个能够处理这些动态加载类型的 DataTemplateSelector

/// <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

示例解决方案是一个带有 TreeViewContentPresenter 的应用程序,它在选定的 TreeView 节点上呈现数据。树中的数据使用反射从插件程序集中加载。

历史

  • 2011年6月2日:初始版本
© . All rights reserved.