层次化数据模板的DataType是接口,如何实现两级TreeView?

3
我知道如何通过将HierarchicalDataTemplate.DataType定义为两个具体类来实现两级树形视图。我还找到了一些关于将HierarchicalDataTemplate.DataType定义为接口的主题,例如: WPF HiercharchicalDataTemplate.DataType:如何对接口作出反应? 使用WPF HierarchicalDataTemplate的任何方式 但是我在TreeView中有两个级别:文件夹和文件,它由两个接口IFolder和IFile约束。当我创建嵌套的TreeViewItems时,会抛出TargetInvocationException异常。但是,如果只创建一个级别的TreeViewItem,则没有问题。
这是代码: (可以在此处下载整个VS2010解决方案(下载后将扩展名从png重命名为zip): http://img.bbs.csdn.net/upload/201307/23/1374565982_74852.png )
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <local:ItemTemplateSelector x:Key="ItemTemplateSelector">
        <local:ItemTemplateSelector.FolderTemplate>
            <HierarchicalDataTemplate DataType="{x:Type local:IFolder}" ItemsSource="{Binding Items}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Name}" />
                </StackPanel>
            </HierarchicalDataTemplate>
        </local:ItemTemplateSelector.FolderTemplate>
        <local:ItemTemplateSelector.FileTemplate>
            <HierarchicalDataTemplate DataType="{x:Type local:IFile}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Name}" />
                </StackPanel>
            </HierarchicalDataTemplate>
        </local:ItemTemplateSelector.FileTemplate>
    </local:ItemTemplateSelector>
</Window.Resources>
<Grid>
    <TreeView Name="tvwFiles" ItemTemplateSelector="{DynamicResource ItemTemplateSelector}" />
</Grid>

class ItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate FolderTemplate { get; set; }
    public DataTemplate FileTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        IFolder folder = item as IFolder;
        if (folder != null)
        {
            return FolderTemplate;
        }

        IFile file = item as IFile;
        if (file != null)
        {
            return FileTemplate;
        }

        return null;
    }
}


public interface IFolder
{
    string Name { get; set; }
}


public interface IFile
{
    string Name { get; set; }
}


public class Folder : IFolder
{
    public string Name { get; set; }

    public ICollection<object> Items
    {
        get
        {
            ICollection<object> items = new List<object>();

            if (SubFolders != null)
            {
                foreach (var folder in SubFolders)
                    items.Add(folder);
            }

            if (Files != null)
            {
                foreach (var file in Files)
                    items.Add(file);
            }

            return items;
        }
    }

    public ICollection<IFolder> SubFolders { get; set; }
    public ICollection<IFile> Files { get; set; }

    public Folder(string name)
    {
        Name = name;
    }
}


public class File : IFile
{
    public string Name { get; set; }

    public File(string name)
    {
        Name = name;
    }
}


public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        ObservableCollection<object> dirs = new ObservableCollection<object>();
        Folder folder = new Folder("Root");
        dirs.Add(folder);
        for (int i = 0; i < 3; ++i)
        {
            Folder subfolder = new Folder("Folder" + i);
            for (int j = 0; j < 3; ++j)
            {
                File File = new File("File" + j);
                subfolder.Files.Add(File);
            }
            folder.SubFolders.Add(subfolder);
        }

        tvwFiles.ItemsSource = dirs;
    }
}
2个回答

2
我找到了问题。
public Folder : IFolder
{
    ...
    public ICollection<IFolder> SubFolders { get; set; }
    public ICollection<IFile> Files { get; set; }
}

should be:

public Folder : IFolder
{
    ...
    private ICollection<IFolder> _subFolders = new ObservableCollection<IFolder>();
    public ICollection<IFolder> SubFolders
    {
        get { return _subFolders; }
        set { _subFolders = value; }
    }

    private ICollection<IFile> _files = new ObservableCollection<IFile>();
    public ICollection<IFile> Files
    {
        get { return _files; }
        set { _files = value; }
    }
    ...
}

我只是没有为它们创建一个实例。


顺便说一下: DataTemplateSelector对于接口DataType是必需的。


0

我认为你可能从错误的角度来看待这个问题。使用HierarchicalDataTemplate声明父层中数据类型和最重要的是包含子项的属性。将其视为设置数据结构。

然后,您可以简单地使用普通的附加DataTemplate来为将要使用的不同数据类型设置样式。如果在Resources部分中声明DataTemplate没有为它们指定键,则它们将自动影响该类型的所有对象。

我编写了一个文件同步应用程序,并将其样式设置为类似于Windows资源管理器。我相信你也在尝试做类似的事情。但是,我不需要为我的文件和文件夹对象使用任何接口。它们各自都有一个Type属性,我可以使用我编写的FileTypeConverter将其绑定到Image.Source属性上,以显示各种类型的文件图标。

我设计了一个DataTemplate来渲染文件,另一个用于文件夹,还有一个HierarchicalDataTemplate来定义结构和每个项目的容器,使用HierarchicalDataTemplate.ItemContainerStyle属性。不幸的是,我目前无法访问该项目,因此无法提供更多的代码示例。

或者,这里有一些相关文章:

在WPF中使用两个级别的TreeView HierarchicalDataTemplate

WPF TreeView HierarchicalDataTemplate - 绑定具有多个子集合的对象


使用接口类作为DataTemplate.DataType与具体类不同,这是我的问题的关键点。对于具体类情况,它可以正常工作,没有任何问题。 - user2606091
对于接口类,WPF 不起作用,因此我引入了一个基于 DataTemplateSelector 的类 ItemTemplateSelector,根据具体对象进行 DataTemplate 选择。由于服务器端和客户端之间的接口隔离原则,我必须使用接口类而不是具体类。虽然第二级不需要使用 HierarchicalDataTemplate,只需要 DataTemplate 就可以了,但这不是关键点。 - user2606091
我根据你的提示尝试了很多方法,但没有找到答案。 如果你有VS2010+,你愿意从上面的链接下载我的源代码并尝试解决问题吗?非常感谢你的帮助。 - user2606091

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