当从DataTemplateSelector显式返回DataTemplate时,为什么DataTemplate不能绑定到接口?

我创建了一个DataTemplateSelector,它使用已知接口的集合进行初始化。 如果传入选择器的项目实现其中一个接口,则返回关联的数据模板。

首先,这是有问题的ICategory接口……

public interface ICategory { ICategory ParentCategory { get; set; } string Name { get; set; } ICategoryCollection Subcategories { get; } } 

这是基于基类或接口而不仅仅是特定具体类匹配的DataTemplateSelector …

 [ContentProperty("BaseTypeMappings")] public class SubclassedTypeTemplateSelector : DataTemplateSelector { private delegate object TryFindResourceDelegate(object key); public override DataTemplate SelectTemplate(object item, DependencyObject container) { var frameworkElement = container as FrameworkElement; foreach(var baseTypeMapping in BaseTypeMappings) { // Check if the item is an instance of, a subclass of, // or implements the interface specified in BaseType if(baseTypeMapping.BaseType.IsInstanceOfType(item)) { // Create a key based on the BaseType, (not item.DataType as usual) var resourceKey = new DataTemplateKey(baseTypeMapping.BaseType); // Get TryFindResource method from either the FrameworkElement, // or from the application var tryFindResource = (frameworkElement != null) ? (TryFindResourceDelegate)frameworkElement.TryFindResource : Application.Current.TryFindResource; // Use the TryFindResource delegate from above to try finding // the resource based on the resource key var dataTemplate = (DataTemplate)tryFindResource(resourceKey); dataTemplate.DataType = item.GetType(); if(dataTemplate != null) return dataTemplate; } } var defaultTemplate = DefaultDataTemplate ?? base.SelectTemplate(item, container); return defaultTemplate; } public DataTemplate DefaultDataTemplate { get; set; } public Collection BaseTypeMappings { get; } = new Collection(); } public class BaseTypeMapping { public Type BaseType { get; set; } } 

以下是它在资源中的设置方式以及各自的HierarchicalDataTemplate和DataType = ICategory ……

      <!---->  

最后,这是一个使用它的TreeView ……

  

我已经调试了它,并且可以确认正确的数据模板正在按预期返回到TreeView,并且因为TreeView正在按照HierarchicalDataTemplate上的ItemSource绑定正确加载子类别 。 所有这些都按预期工作。

什么不起作用的是模板本身的内容。 正如您所看到的,模板应该只显示类别的名称,但它只是将对象原始呈现,就好像它直接放在没有任何模板的ContentPresenter中一样。 你在UI中看到的只是ToString的结果。 模板的内容完全被忽略。

我唯一能想到的是它没有工作,因为我正在使用DataType的接口,但同样,子项的ItemsSource的绑定确实有效,所以我有点难过。

值得注意的是:作为测试,我根据具体类型(即Category而不仅仅是ICategory)创建了第二个DataTemplate,当我这样做时,它按预期工作。 问题是具体类型是在一个不应该由UI引用的程序集中。 这就是我们首先使用接口的全部原因。

*注意:我还尝试使用Key而不是设置DataType属性来更改我查找模板的方式。 在这种情况下,就像以前一样,选择器仍然找到相同的资源,但它仍然不起作用!

然而具有讽刺意味的是,如果我使用相同的键直接通过StaticResource绑定设置TreeView的ItemTemplate,那么它确实有效,这意味着当我从选择器返回模板时它只是不起作用并且看起来与DataType是否相关设定与否。*

什么不起作用的是模板本身的内容

这是因为您在XAML标记中定义的模板未应用,因为DataType属性设置为接口类型。 正如@Manfred Radlwimmer建议的那样是设计: https ://social.msdn.microsoft.com/Forums/vstudio/en-US/1e774a24-0deb-4acd-a719-32abd847041d/data-templates-and-interfaces?forum = wpf 。 从DataTemplateSelector返回这样的模板不会使它像您已经发现的那样工作。

但是,如果使用DataTemplateSelector选择适当的数据模板,则可以从数据模板中删除DataType属性,并为每个模板指定唯一的x:Key:

    

然后,您应该能够使用此密钥解析资源,例如:

 var baseTypeName = "ICategory"; var dataTemplate = (DataTemplate)tryFindResource("baseTypeName"); 
  1. DataTemplates不能以接口为目标 – 所以你需要一个选择器或一个解决方法 – >完成。
  2. 你可以使用{x:Type}而不是实现BaseTypeMapping
  3. 使用BaseType.IsAssignableFrom(item.GetType())来检查您是否匹配
  4. 您必须从模板定义中删除该类型,如果您的类型错误则无法分配 – 因此您添加了一个键。
  5. 在#4之后,你的模板有一个键,并且只是通过分配类型而不起作用,因此你必须在分配类型后删除它 – > dataTemplate.Key = null
  6. 我这里没有IDE,但是找到ressources会给你一个实例,所以你可以通过引用来改变。 这是你想要的吗?