DataTemplate 注入






4.25/5 (4投票s)
将 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
示例解决方案是一个带有 TreeView
和 ContentPresenter
的应用程序,它在选定的 TreeView
节点上呈现数据。树中的数据使用反射从插件程序集中加载。
历史
- 2011年6月2日:初始版本