File System TreeView
If you wanted to stick with the strings something like this would work...
TreeNode root = new TreeNode();
TreeNode node = root;
treeView1.Nodes.Add(root);
foreach (string filePath in myList) // myList is your list of paths
{
node = root;
foreach (string pathBits in filePath.Split('/'))
{
node = AddNode(node, pathBits);
}
}
private TreeNode AddNode(TreeNode node, string key)
{
if (node.Nodes.ContainsKey(key))
{
return node.Nodes[key];
}
else
{
return node.Nodes.Add(key, key);
}
}
Populate TreeView with file system directory structure
Option #1: Recursive approach:
private void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var rootDirectoryInfo = new DirectoryInfo(path);
treeView.Nodes.Add(CreateDirectoryNode(rootDirectoryInfo));
}
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in directoryInfo.GetDirectories())
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
foreach (var file in directoryInfo.GetFiles())
directoryNode.Nodes.Add(new TreeNode(file.Name));
return directoryNode;
}
Option #2: Non-recursive approach:
private static void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var stack = new Stack<TreeNode>();
var rootDirectory = new DirectoryInfo(path);
var node = new TreeNode(rootDirectory.Name) { Tag = rootDirectory };
stack.Push(node);
while (stack.Count > 0)
{
var currentNode = stack.Pop();
var directoryInfo = (DirectoryInfo)currentNode.Tag;
foreach (var directory in directoryInfo.GetDirectories())
{
var childDirectoryNode = new TreeNode(directory.Name) { Tag = directory };
currentNode.Nodes.Add(childDirectoryNode);
stack.Push(childDirectoryNode);
}
foreach (var file in directoryInfo.GetFiles())
currentNode.Nodes.Add(new TreeNode(file.Name));
}
treeView.Nodes.Add(node);
}
Using Tree View in Java to implement a view of a filesystem with hierarchy
I have done it this way. You may modify it according to your needs:
private void createTreeView(String dirPath) {
TreeItem<Object> tree = new TreeItem<>(dirPath.substring(dirPath.lastIndexOf(File.separator) + 1), new ImageView(icon));
List<TreeItem<Object>> dirs = new ArrayList<>();
try {
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(dirPath));
for (Path path : directoryStream) {
if (Files.isDirectory(path)) {
String pathString = path.toString();
TreeItem<Object> subDirectory = new TreeItem<>(pathString.substring(pathString.lastIndexOf(File.separator) + 1), new ImageView(icon));
getSubLeafs(path, subDirectory);
dirs.add(subDirectory);
}
}
tree.getChildren().addAll(dirs);
} catch (IOException e) {
e.printStackTrace();
}
treeView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
imageNumber = 1;
StringBuilder pathBuilder = new StringBuilder();
for (TreeItem<String> item = (TreeItem<String>) treeView.getSelectionModel().getSelectedItem(); item != null; item = item.getParent()) {
pathBuilder.insert(0, item.getValue());
pathBuilder.insert(0, "/");
}
String path = pathBuilder.toString();
populateImageView(path.substring(1) + "/");
});
tree.setExpanded(true);
treeView.setRoot(tree);
treeView.setShowRoot(true);
}
private void getSubLeafs(Path subPath, TreeItem<Object> parent) {
try {
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(subPath.toString()));
for (Path subDir : directoryStream) {
if (Files.isDirectory(subDir)) {
String subTree = subDir.toString();
TreeItem<Object> subLeafs = new TreeItem<>(subTree, new ImageView(icon));
subLeafs.setValue(subTree.substring(subTree.lastIndexOf(File.separator) + 1));
getSubLeafs(subDir, subLeafs);
parent.getChildren().add(subLeafs);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
How to display all files under folders in treeview
Change the code in the try block (from: List Drives and Folders in a TreeView Using C#) as following:
EDIT:
Added following code, because the files of the root-directory were ignored:
//add files of rootdirectory
DirectoryInfo rootDir = new DirectoryInfo(e.Node.Tag.ToString());
foreach (var file in rootDir.GetFiles())
{
TreeNode n = new TreeNode(file.Name, 13, 13);
e.Node.Nodes.Add(n);
}
Full class:
private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
if (e.Node.Nodes.Count > 0)
{
if (e.Node.Nodes[0].Text == "..." && e.Node.Nodes[0].Tag == null)
{
e.Node.Nodes.Clear();
//get the list of sub direcotires
string[] dirs = Directory.GetDirectories(e.Node.Tag.ToString());
//add files of rootdirectory
DirectoryInfo rootDir = new DirectoryInfo(e.Node.Tag.ToString());
foreach (var file in rootDir.GetFiles())
{
TreeNode n = new TreeNode(file.Name, 13, 13);
e.Node.Nodes.Add(n);
}
foreach (string dir in dirs)
{
DirectoryInfo di = new DirectoryInfo(dir);
TreeNode node = new TreeNode(di.Name, 0, 1);
try
{
//keep the directory's full path in the tag for use later
node.Tag = dir;
//if the directory has sub directories add the place holder
if (di.GetDirectories().Count() > 0)
node.Nodes.Add(null, "...", 0, 0);
foreach (var file in di.GetFiles())
{
TreeNode n = new TreeNode(file.Name, 13, 13);
node.Nodes.Add(n);
}
}
catch (UnauthorizedAccessException)
{
//display a locked folder icon
node.ImageIndex = 12;
node.SelectedImageIndex = 12;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "DirectoryLister",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
e.Node.Nodes.Add(node);
}
}
}
}
}
This look like following picture:
Populate TreeView with files and directories but without hidden folders and files
If you've based your code on that post, you could use the DirectoryInfo.Attributes
property to check for Hidden
or System
values, for example, so you could let those items out of your TreeView list.
WPF Treeview and databinding a Directory Tree
XAML
<TreeView
ItemsSource="{Binding RootDirectoryItems}"
>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:UserDirectory}" ItemsSource="{Binding Items}">
<Label Content="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:UserFile}">
<Label Content="{Binding Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
RootDirectoryItems
is presumed to be a property of the viewmodel, something like this:
public ObservableCollection<Object> RootDirectoryItems { get; }
= new ObservableCollection<object>();
In the C#, assume the presence of INotifyPropertyChanged
boilerplate on all property setters. I added two properties to UserDirectory
:
Name
, a readonly property which returns just the name segment ofDirectoryPath
. IfDirectoryPath
may change at runtime, its setter should callOnPropertyChanged("Name");
, so that bindings looking at theName
property will know they need to get the new value.UserFile
gained a similarName
property, which comes with the same advice about raisingPropertyChanged
if that's a possibility.Items
: Again, a readonly property, and you should raisePropertyChanged
appropriately if either of the constituent collections changes (handleICollectionChanged.CollectionChanged
, and do likewise in the setters if you have setters). Bindings don't care about the declared type of a property, so it just returnsSystem.Collections.IEnumerable
-- it could even returnobject
, and the XAML wouldn't care. But let's be just specific enough, without being so specific as to encourage anybody in C# to try to use the property for anything.
If it were me, I'd almost certainly make UserDirectory
and UserFile
bare immutable "POCO" classes without INotifyPropertyChanged
, and simply repopulate if anything changed on the disk. I might depart from immutability by giving UserDirectory
a FileWatcher
and having it repopulate itself, if I had some reason to expect directories to change a lot.
So here's the C#:
public class UserDirectory
{
public ObservableCollection<UserFile> Files { get; set; } = new ObservableCollection<UserFile>();
public ObservableCollection<UserDirectory> Subfolders { get; set; } = new ObservableCollection<UserDirectory>();
// Concat demands a non-null argument
public IEnumerable Items { get { return Subfolders?.Cast<Object>().Concat(Files); } }
public String DirectoryPath { get; set; }
public String Name { get { return System.IO.Path.GetFileName(DirectoryPath); } }
}
public class UserFile
{
public String FilePath { get; set; }
public Category Category { get; set; }
public String Name { get { return System.IO.Path.GetFileName(FilePath); } }
}
Your Item
class isn't needed, because XAML works by "duck typing".
Here's a simpler variant that also works, because both UserDirectory
and UserFile
have a Name
property, and UserFile
's missing Items
property is quietly shrugged off.
<TreeView
ItemsSource="{Binding RootDirectoryItems}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<Label Content="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Related Topics
How to Generate and Auto Increment Id with Entity Framework
Does Anyone Know of a Good C# API for Subversion
How to Display Progress During a Busy Loop
How to Parse String with Hours Greater Than 24 to Timespan
Generating Random Numbers Without Repeating.C#
Calling SQL Defined Function in C#
Creating a Database Programmatically in SQL Server
How to Create a Constant Value - Only Primitive Types
How to Make a Console Application Run Using Only a Single File in .Net Core
How to Handle Forms Authentication Timeout Exceptions in ASP.NET
Opening a "Known File Type" into Running Instance of Custom App - .Net
Field Initializer in C# Class Not Run When Deserializing
How to Hide Public Methods from Intellisense
Deserialize JSON String to Dictionary<String,Object>