WPF Heirachical DataTemplate in TreeView
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开发者_Python百科(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;
}
}
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.
精彩评论