【发布时间】:2017-01-18 08:52:42
【问题描述】:
我创建了一个 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<BaseTypeMapping> BaseTypeMappings { get; } = new Collection<BaseTypeMapping>();
}
public class BaseTypeMapping
{
public Type BaseType { get; set; }
}
以下是它在资源中的设置方式以及 DataType = ICategory 的相应 HierarchicalDataTemplate...
<HierarchicalDataTemplate DataType="{x:Type model:ICategory}"
ItemsSource="{Binding Subcategories}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<is:SubclassedTypeTemplateSelector x:Key="SubclassedTypeTemplateSelector">
<!--<is:BaseTypeMapping BaseType="{x:Type model:ICategory}" />-->
</is:SubclassedTypeTemplateSelector>
最后,这是一个使用它的 TreeView...
<TreeView x:Name="MainTreeView"
ItemsSource="{Binding Categories}"
ItemTemplateSelector="{StaticResource SubclassedTypeTemplateSelector}" />
我已经对其进行了调试,并且可以确认正确的数据模板正在返回到 TreeView,正如预期的那样,通过代码和因为 TreeView 正在根据 HierarchicalDataTemplate 上的 ItemSource 绑定正确加载子类别。所有这些都按预期工作。
不起作用的是模板本身的内容。如您所见,模板只是应该显示类别的名称,但它只是呈现对象原始,就好像它直接放置在没有任何模板的 ContentPresenter 中一样。您在 UI 中看到的只是 ToString 的结果。模板的内容被完全忽略。
我唯一能想到的是它不起作用,因为我正在使用 DataType 的接口,但同样,儿童 ItemsSource 的绑定确实有效,所以我在这里有点难过。
注意:作为测试,我根据具体类型(即 Category 而不仅仅是 ICategory)创建了第二个 DataTemplate,当我这样做时,它按预期工作。问题是具体类型位于 UI 不应引用的程序集中。这就是我们首先使用接口的全部原因。
*注意:我还尝试通过使用 Key 而不是设置 DataType 属性来更改查找模板的方式。在那种情况下,和以前一样,选择器仍然会找到相同的资源,但它仍然不起作用!
然而,具有讽刺意味的是,如果我使用相同的键直接通过 StaticResource 绑定设置 TreeView 的 ItemTemplate,那么它确实 工作,这意味着它只有在我返回模板时不起作用选择器和 not 是否出现与 DataType 是否设置有关。*
【问题讨论】:
-
简短回答:因为它是这样设计的。可能是因为您可以为单个课程获得多个匹配项。 Official answer on MSDN
-
如果你查看你的 MSDN 链接,你会看到他们特别说你可以使用 DataTemplateSelector,我在上面做的。你的论点,以及他们说他们决定反对的是他们试图自己匹配接口,这不是我在这里想要实现的。
-
您最初的问题是“为什么 DataTemplate 不能绑定到接口?”。我以为你对它为什么会这样感兴趣。
标签: c# wpf datatemplate hierarchicaldatatemplate datatemplateselector