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

时间:2017-01-18 08:52:42

标签: c# wpf datatemplate hierarchicaldatatemplate datatemplateselector

我创建了一个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; }
}

以下是如何在资源中设置它以及各自的HierarchicalDataTemplate与DataType = ICategory ...

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

我已经对其进行了调试,并且 可以确认正确的数据模板正在返回 ,并按预期方式逐步执行代码,因为 < em> TreeView正在按照HierarchicalDataTemplate上的ItemSource绑定 正确加载子类别。所有这些都按预期工作。

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

我唯一能想到的是它没有用,因为我使用了DataType的接口,但同样,对于孩子们的ItemsSource的绑定确实有效,所以我很友好在这里难倒。

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

  

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

     

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

2 个答案:

答案 0 :(得分:1)

  

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

这是因为您在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:

<HierarchicalDataTemplate x:Key="ICategory" ItemsSource="{Binding Subcategories}">
    <TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>

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

var baseTypeName = "ICategory";
var dataTemplate = (DataTemplate)tryFindResource("baseTypeName");

答案 1 :(得分:-1)

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