WPF指定接口的层次数据模板

4
我在WPF中发现了一个非常奇怪的问题。 如果我为接口指定 DataTemplate,则如果在ItemsControl.ItemTemplate内定义,则可以正常工作,但是如果在ItemsControl.Resrouces内定义,则不起作用。

具体例子:

我有一个树形结构要表示。 树中的所有项目都实现了IHardware,但它们不一定具有共同的基础类型。 如果我在TreeView.ItemTemplate内为IHardware定义一个HierarchicalDataTemplate,那么一切都可以正常工作。 如果我将模板定义在 TreeView.Resources 内,它将永远不会被使用/应用。 下面显示相同的数据,在左侧列(即第一列)按预期工作,而在右侧列(即第二列)则无法正常工作。

<Window x:Class="WPFInterfaceBinding.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:self ="clr-namespace:WPFInterfaceBinding"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="MainWindow" Height="350" Width="525">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <!-- Works -->
        <Border
            Grid.Column="0"
            Background="Gray">
            <TreeView
                ItemsSource="{Binding Hardware}">

                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate
                        DataType="{x:Type self:IHardware}"
                        ItemsSource="{Binding SubHardware}">
                        <TextBlock Text="{Binding Path=Name}" />
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>
        </Border>

        <!-- Doesn't work -->
        <Border
            Grid.Column="1"
            Background="Gray">
            <TreeView
                ItemsSource="{Binding Hardware}">

                <TreeView.Resources>
                    <HierarchicalDataTemplate
                        DataType="{x:Type self:IHardware}"
                        ItemsSource="{Binding SubHardware}">
                        <TextBlock Text="{Binding Path=Name}" />
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>
        </Border>

    </Grid>
</Window>

请注意第二列中除了 TreeView.ItemTemplate -> TreeView.Resources 之外,没有发生任何变化。为什么会这样?当在 Resources 中时,如何使模板正常工作?我想我可以使用 DataTemplateSelector 来解决这个问题,但首先我很好奇是否有一种方法可以让它按预期工作。

完整的代码

using System.Windows;

namespace WPFInterfaceBinding
{
    public partial class MainWindow : Window
    {
        public IHardware[] Hardware { get; private set; }

        public MainWindow ()
        {
            Hardware = InitializeHardware();

            InitializeComponent();
        }

        private IHardware[] InitializeHardware ()
        {
            return  new Hardware[] {
                new Hardware("Component 1", new Hardware[] {
                    new Hardware("Sub Component 1"),
                    new Hardware("Sub Component 2")
                }),
                new Hardware("Component 2", new Hardware[] {
                    new Hardware("Sub Component 3"),
                    new Hardware("Sub Component 4")
                })
            };
        }
    }

    public class Hardware : IHardware
    {
        public string      Name        { get; set; }
        public IHardware[] SubHardware { get; set; }

        public Hardware ( string name, Hardware[] subHardware = null )
        {
            Name = name;
            SubHardware = subHardware ?? new Hardware[0];
        }
    }

    public interface IHardware
    {
        string      Name        { get; set; }
        IHardware[] SubHardware { get; set; }
    }
}

附加信息:

  • 我不能简单地使用 ItemTemplate,因为在我的实际使用场景中,会有非 IHardware 项目混合在一起,使用 CompositeCollection,因此我需要多个模板。
  • 我不能将集合的类型从 IHardware 更改为具体的类型,因为我正在显示来自我无法控制的代码的数据。
  • 这只是示例代码,不代表任何实际使用的设计模式。
  • 如果将类型从 IHardware 更改为 Hardware,则在 TreeView.Resources 中定义模板完全有效。
1个回答

4
原来,WPF不喜欢绑定接口。我找到的唯一解决方法是使用DataTemplateSelector。
public class OHMTreeTemplateSelector : DataTemplateSelector
{
    public HierarchicalDataTemplate HardwareTemplate { get; set; }
    public             DataTemplate   SensorTemplate { get; set; }

    public override DataTemplate SelectTemplate ( object item, DependencyObject container )
    {
             if ( item is IHardware ) return HardwareTemplate;
        else if ( item is ISensor   ) return   SensorTemplate;

        return base.SelectTemplate(item, container);
    }
}

尽管如此,出于其他原因,我最终创建了一个单独的ViewModel来暴露经过具体类型处理后的数据,从而规避了这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接