3

I knew how to implement 2 levels of TreeView by defining HierarchicalDataTemplate.DataType to 2 concreate class. I also found some topics about defining HierarchicalDataTemplate.DataType to interface such as: WPF HiercharchicalDataTemplate.DataType: How to react on interfaces? Any way to use interfaces with wpf HierarchicalDataTemplate

But I have two levels in TreeView: folders and files, it is contracted by two interfaces: IFolder and IFile. When I create a nested TreeViewItems, TargetInvocationException is thrown. But it is OK if just create only one level of TreeViewItem.

This is code: (Can download whole VS2010 solution here (rename extension name from png to zip after downloaded): 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;
    }
}
Community
  • 1
  • 1
user2606091
  • 203
  • 4
  • 9

2 Answers2

2

I found the problem.

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; }
    }
    ...
}

I just did not new a instance for them.


BTW: DataTemplateSelector is necessary for interface DataType.

user2606091
  • 203
  • 4
  • 9
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

Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Using interface classes as DataTemplate.DataType is different from concreate classes, this is key point of my question. For concreate classes case, it works well and there is no problem. – user2606091 Jul 24 '13 at 02:39
  • For interface classes, WPF doesn't work, that's why I introduced a DataTemplateSelector-based class ItemTemplateSelector to do DataTemplate selection according to concreate objects. Due to interface isolation principle between Server side and Client side, I have to use interface classes instead of concreate classes. It's true that the 2nd level is unnecessary to use HierarchicalDataTemplate, just DataTemplate is OK, but this is not the key point. – user2606091 Jul 24 '13 at 02:53
  • I tried many ways according to your hints, but did not find answer. If you have VS2010+, would you like to download my source code from the above link and try to address the problem? Thank you very much for your help. – user2606091 Jul 24 '13 at 02:54