WPF Heirachical DataTemplate in TreeView

醉酒当歌 提交于 2021-02-16 15:18:30

问题


I am trying to get my head around Heirarchical DataTemplates and TreeViews in WPF and am having some trouble.

I have created an app with only a TreeView on the form as below and defined HierarchicalDataTemplate's for both a Directory object and a File object I then Bind the TreeView to the Directories property (ObservableCollection) of my model.

<Grid>
        <Grid.Resources>

            <HierarchicalDataTemplate DataType="{x:Type local:Directory}" ItemsSource ="{Binding Directories}">
                <TextBlock Text="{Binding Path=Name}"/>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type local:File}" ItemsSource ="{Binding Files}">
                <TextBlock Text="{Binding Path=FileName}"/>
            </HierarchicalDataTemplate>
        </Grid.Resources>
        <TreeView Margin="12,12,0,12" Name="treeView1" HorizontalAlignment="Left" Width="204" >
            <TreeViewItem ItemsSource="{Binding Directories}" Header="Folder Structure" />
        </TreeView>
    </Grid>

This works in that in the TreeView I see my directories and it recursively displays all sub directories, but what I want to see is Directories and Files! I've checked the model and it definately has files in some of the sub directories but I can't see them in the tree.

I'm not sure if it is my template that is the problem or my model so I have included them all! :-)

Thanks

OneShot

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();

    }


    private MainWindowViewModel _vm;

    public MainWindowViewModel VM
    {
        set
        {
            _vm = value;
            this.DataContext = _vm;
        }
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        var d = new Directory() { Name = "temp" };
        recurseDir("c:\\temp", ref d);

        VM = new MainWindowViewModel( new List<Directory>() { d } );            
    }

    private void recurseDir(string path, ref Directory dir)
    {
        var files = System.IO.Directory.GetFiles(path);
        var dirs = System.IO.Directory.GetDirectories(path);

        dir.Name = path.Substring(path.LastIndexOf("\\")+1);

        for (int i = 0; i < files.Length; i++)
        {
            var fi = new FileInfo(files[i]);
            dir.Files.Add(new File() { 
                FileName = System.IO.Path.GetFileName(files[i]), 
                DirectoryPath = System.IO.Path.GetDirectoryName(files[i]), 
                Size = fi.Length, 
                Extension= System.IO.Path.GetExtension(files[i]) 
            });

        }

        for (int i = 0; i < dirs.Length; i++)
        {
            var d = new Directory() { Name = dirs[i].Substring(dirs[i].LastIndexOf("\\")+1) };
            recurseDir(dirs[i], ref d);
            dir.Directories.Add(d);

        }

    }
}

-

 public class MainWindowViewModel
        : DependencyObject
    {


        public MainWindowViewModel(List<Directory> Dirs)
        {
            this.Directories = new ObservableCollection<Directory>( Dirs);
        }

        public ObservableCollection<Directory> Directories
        {
            get { return (ObservableCollection<Directory>)GetValue(DirectoriesProperty); }
            set { SetValue(DirectoriesProperty, value); }
        }

        public static readonly DependencyProperty DirectoriesProperty =
            DependencyProperty.Register("Directories", typeof(ObservableCollection<Directory>), typeof(MainWindowViewModel), new UIPropertyMetadata(null));

        public Directory BaseDir
        {
            get { return (Directory)GetValue(BaseDirProperty); }
            set { SetValue(BaseDirProperty, value); }
        }

        public static readonly DependencyProperty BaseDirProperty =
            DependencyProperty.Register("BaseDir", typeof(Directory), typeof(MainWindowViewModel), new UIPropertyMetadata(null));


    }

-

public class Directory
    {
        public Directory()
        {
            Files = new List<File>();
            Directories = new List<Directory>();
        }
        public List<File> Files { get; private set; }
        public List<Directory> Directories { get; private set; }
        public string Name { get; set; }
        public int FileCount
        {
            get
            {
                return Files.Count;
            }
        }
        public int DirectoryCount
        {
            get
            {
                return Directories.Count;
            }
        }
        public override string ToString()
        {
            return Name;
        }
    }

-

public class File
    {
        public string DirectoryPath { get; set; }
        public string FileName { get; set; }
        public string Extension { get; set; }
        public double Size { get; set; }
        public string FullPath
        {
            get
            {
                return System.IO.Path.Combine(DirectoryPath, FileName);
            }
        }
        public override string ToString()
        {
            return FileName;
        }
    }

回答1:


Take a look at this again:

        <HierarchicalDataTemplate DataType="{x:Type local:Directory}" ItemsSource ="{Binding Directories}">
            <TextBlock Text="{Binding Path=Name}"/>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate DataType="{x:Type local:File}" ItemsSource ="{Binding Files}">
            <TextBlock Text="{Binding Path=FileName}"/>
        </HierarchicalDataTemplate>

What you're saying is that if you encounter an object of type File, display it with a text block and get its children from a property Files under the File. What you really want is for the Files to show up under each Directory, so you should create a new property that exposes both Directories and Files:

public class Directory
{
    //...
    public IEnumerable<Object> Members
    {
        get
        {
            foreach (var directory in Directories)
                yield return directory;

            foreach (var file in Files)
                yield return file;
        }
    }
    //...
}

and then your template becomes:

    <HierarchicalDataTemplate DataType="{x:Type local:Directory}" ItemsSource ="{Binding Members}">
        <TextBlock Text="{Binding Path=Name}"/>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type local:File}">
        <TextBlock Text="{Binding Path=FileName}"/>
    </DataTemplate>

UPDATE:

Actually, the above is not sufficient if you want to receive collection changed notifications for the Members. If that's the case, I recommend creating a new ObservableCollection and adding Directory and File entries to it in parallel to adding to the Files and Directories collections.

Alternately, you may wish to reconsider how you store your information and put everything in a single collection. The other lists are then simply filtered views of the main collection.



来源:https://stackoverflow.com/questions/4347956/wpf-heirachical-datatemplate-in-treeview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!