3

我知道如何通过将 HierarchicalDataTemplate.DataType 定义为 2 个 concreate 类来实现 2 个级别的 TreeView。我还发现了一些关于将 HierarchicalDataTemplate.DataType 定义为接口的主题,例如: WPF HiercharchicalDataTemplate.DataType: How to react on interfaces? 使用 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;
    }
}
4

2 回答 2

2

我发现了问题。

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

应该:

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 所必需的。

于 2013-07-24T05:02:03.777 回答
0

I believe that you may be looking at this in the wrong way. Use the HierarchicalDataTemplate to declare the type of data in the parent layer and most importantly, the property that contains the children items. Think of it as setting the data structure.

Then you can simply use ordinary additional DataTemplates to style the different data types that will be used. If you declare the DataTemplates in the Resources section without specifying a key for them, then they will automatically affect all objects of that type.

I wrote a file synchronisation application and styled it to look like Windows Explorer and I believe you are trying to do something similar. However, I did not need to use any interfaces for my file and folder objects. They each had a Type property which I could bind to an Image.Source property using a FileTypeConverter that I wrote to display the various type of file icons.

I designed one DataTemplate to render the files and another for the folders and one HierarchicalDataTemplate to define the structure and the container for each item using the HierarchicalDataTemplate.ItemContainerStyle property. Unfortunately, I don't have access to that project at the moment, so I can't give you more code examples.

Alternatively, here are some related articles:

Set TreeView HierarchicalDataTemplate with 2 levels in WPF

WPF TreeView HierarchicalDataTemplate - binding to object with multiple child collections

于 2013-07-23T11:21:49.040 回答