从DataTemplateSelector显式返回该DataTemplate时,为什么该DataTemplate无法绑定到接口? [英] Why can't a DataTemplate bind to an interface when that DataTemplate was explicitly returned from a DataTemplateSelector?

查看:83
本文介绍了从DataTemplateSelector显式返回该DataTemplate时,为什么该DataTemplate无法绑定到接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个DataTemplateSelector,它使用一组已知接口进行了初始化.如果传递到选择器的项实现了这些接口之一,则返回关联的数据模板.

I've created a DataTemplateSelector which is initialized with a collection of known interfaces. If an item passed into the selector implements one of those interfaces, the associated data template is returned.

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

First, here's the ICategory interface in question...

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

    ICategoryCollection Subcategories { get; }
}

这里是DataTemplateSelector,它根据基类或接口而不只是特定的具体类进行匹配...

Here's the DataTemplateSelector which matches based on a base class or interface rather than just a specific concrete class...

[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 ...

Here's how it's set up in the resources along with the respective HierarchicalDataTemplate with 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 ...

And finally, here's a TreeView which uses it...

<TreeView x:Name="MainTreeView"
    ItemsSource="{Binding Categories}"
    ItemTemplateSelector="{StaticResource SubclassedTypeTemplateSelector}" />

我已对其进行调试,并且 可以确认正确的数据模板已返回 到TreeView中,正如预期的那样,逐步执行代码是因为 TreeView会按照HierarchicalDataTemplate上的ItemSource绑定 正确加载子类别.所有这些都按预期工作.

I've debugged it and can confirm the correct data template is being returned to the TreeView as expected both stepping through the code and because the TreeView is properly loading the subcategories as per the ItemSource binding on the HierarchicalDataTemplate. All of this works as expected.

不起作用的是模板本身的内容.如您所见,模板只是应该显示类别的名称,但是它只是将对象显示为原始对象,就像将其直接放置在ContentPresenter中而不使用任何模板一样.您在UI中看到的只是ToString的结果.模板的内容将被完全忽略.

What doesn't work is the contents of the template itself. As you can see, the template is simply supposed to show the name of the category but it's just presenting the object raw as if it were placed directly in a ContentPresenter without any template. All you see in the UI is the result of ToString. The template's contents are completely ignored.

我唯一能想到的是它不起作用,因为我为DataType使用了接口,但是同样,对子项的ItemsSource的绑定确实起作用了,所以在这里我有些困惑.

The only thing I can think of is its not working because I'm using an interface for the DataType, but again, the binding for the children's ItemsSource does work, so I'm kind of stumped here.

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

Of note: As a test, I created a second DataTemplate based on the concrete type (i.e. Category and not just ICategory) and when I did, it worked as expected. The problem is the concrete type is in an assembly that's not supposed to be referenced by the UI. That's the entire reason we're using interfaces in the first place.

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

*NOTE: I have also tried changing the way I look up the template by using a Key instead of setting the DataType property. In that case, just as before, the selector still finds the same resource, but it still doesn't work!

具有讽刺意味的是,如果我使用相同的键直接通过StaticResource绑定设置TreeView的ItemTemplate,则它起作用,这意味着仅当我从选择器,是否出现与是否设置了DataType有关.*

Ironically however, if I use that same key to set the ItemTemplate of the TreeView directly via a StaticResource binding, then it does work, meaning it only doesn't work when I return the template from the selector and does not appear related to whether DataType is set or not.*

推荐答案

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

What doesn't work is the contents of the template itself

这是因为由于DataType属性设置为接口类型,因此未应用您在XAML标记中定义的模板.正如@Manfred Radlwimmer所建议的那样,这是设计使然:

This is because the templates that you define in your XAML markup are not being applied since the DataType property is set to an interface type. As @Manfred Radlwimmer suggests this is by design: https://social.msdn.microsoft.com/Forums/vstudio/en-US/1e774a24-0deb-4acd-a719-32abd847041d/data-templates-and-interfaces?forum=wpf. Returning such a template from a DataTemplateSelector doesn't make it work as you have already discovered.

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

But if you use a DataTemplateSelector to select the appropriate data template you could remove the DataType attribute from the data templates and give each template a unique x:Key instead:

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

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

You should then be able to resolve the resource using this key, e.g.:

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

这篇关于从DataTemplateSelector显式返回该DataTemplate时,为什么该DataTemplate无法绑定到接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆