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().Where(s => s.Extension == ".jpg"))
directoryNode.Nodes.Add(new TreeNode(file.Name));
return directoryNode;
}
using this code I can list all jpg files and folders from a given directory. But it will list empty folders of jpg files too. How can I avoid it?
The solution is to check whether there are any files on the return from the recursion, then add it if there are.
foreach (var directory in directoryInfo.GetDirectories())
{
TreeNode subNode = CreateDirectoryNode(directory);
if (subNode.Nodes.Count > 0)
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
}
Since you're doing a depth first search the nodes will effectively be culled from the leaf up to the root if there aren't any files.
You need to check if the number of sub directories is zero and the number of files in the directory is zero. If this is the case, you should not create the node.
One way to do this is to return null from the CreateDirectoryNode method if the directory does not meet the specification. And then only add sub nodes that are not null.
Consider this code:
private void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var rootDirectoryInfo = new DirectoryInfo(path);
var tree_node = CreateDirectoryNode(rootDirectoryInfo);
if (tree_node != null)
treeView.Nodes.Add(tree_node);
}
//This method will return null of the specified directory does not have sub folders or JPG files
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
//Obtain all sub directories
var sub_directories = directoryInfo.GetDirectories();
//Obtain all JPG files
var jpeg_files =
directoryInfo.GetFiles()
.Where(s => s.Extension.Equals(".jpg", StringComparison.OrdinalIgnoreCase))
.ToArray();
//If the above arrays are empty, return null
if (sub_directories.Length == 0 && jpeg_files.Length == 0)
return null;
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in sub_directories)
{
var sub_node = CreateDirectoryNode(directory);
if(sub_node != null) //Only add sub nodes if they are not null
directoryNode.Nodes.Add(sub_node);
}
foreach (var file in jpeg_files)
directoryNode.Nodes.Add(new TreeNode(file.Name));
return directoryNode;
}
Related
How would you modify this code to skip creating the root node and create only the other nodes?
void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var rootDirectoryInfo = new DirectoryInfo(path);
treeView.Nodes.Add(CreateDirectoryNode(rootDirectoryInfo));
}
TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in directoryInfo.GetDirectories())
{
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
}
return directoryNode;
}
Like this:
void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var rootDirectoryInfo = new DirectoryInfo(path);
foreach (var directory in rootDirectoryInfo.GetDirectories())
{
treeView.Nodes.Add(CreateDirectoryNode(directory));
}
}
TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in directoryInfo.GetDirectories())
{
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
}
return directoryNode;
}
I have a directory with files and sub directories with files and want to create xml from them. here is my folder structure:
C:\inputdata folder contains:
C:\inputdata\file1.txt
C:\inputdata\picture1.jpg
C:\inputdata\subfolder\picture2.jpg
C:\inputdata\subfolder\file2.txt
C:\inputdata\subfolder\anotherfolder \file3.txt
C:\inputdata\anotherfolder\
and i want to generate this xml file:
<?xml version="1.0" encoding="UTF-8"?>
<serverfiles>
<file name="picture1.jpg"/>
<file name="file1.txt"/>
<folder name="subfolder">
<file name="picture2.jpg"/>
<file name="file2.txt"/>
<folder name="anotherfolder">
<file name="file3.txt"/>
</folder>
</folder>
<folder name="anotherfolder">
</folder>
</serverfiles>
I have written following console app but i have two problems.
this produces the attached screenshot xml which is not exactly as above xml in terms of structure.
is there a way I could sort this with the name attribute with my code.
can someone please point me to right direction of how to do this please:
private const string folderLocation = #"c:\inputdata";
static void Main(string[] args)
{
DirectoryInfo dir = new DirectoryInfo(folderLocation);
var doc = new XDocument(CREATEXML(dir));
Console.WriteLine(doc.ToString());
Console.Read();
}
private static XElement CREATEXML(DirectoryInfo dir)
{
//get directories
var xmlInfo = new XElement("serverfiles", new XAttribute("name", dir.Name));
//get all the files first
foreach(var file in dir.GetFiles())
{
xmlInfo.Add(new XElement("file", new XAttribute("name", file.Name)));
}
//get subdirectories
foreach(var subDir in dir.GetDirectories())
{
xmlInfo.Add(CREATEXML(subDir));
}
return xmlInfo;
}
Nearly there: just some small edits to your code are what you need.
private const string folderLocation = #"c:\inputdata";
static void Main(string[] args)
{
DirectoryInfo dir = new DirectoryInfo(folderLocation);
// makes everything wrapped in an XElement called serverfiles.
// Also a declaration as specified (sorry about the standalone status:
// it's required in the XDeclaration constructor)
var doc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"),
CREATEXML(dir));
Console.WriteLine(doc.ToString());
Console.Read();
}
private static XElement CREATEXML(DirectoryInfo dir, bool writingServerFiles = true)
{
//get directories
var xmlInfo = new XElement(writingServerFiles ? "serverfiles" : "folder", writingServerFiles ? null : new XAttribute("name", dir.Name)); //fixes your small isue (making the root serverfiles and the rest folder, and serverfiles not having a name XAttribute)
//get all the files first
foreach(var file in dir.GetFiles())
{
xmlInfo.Add(new XElement("file", new XAttribute("name", file.Name)));
}
//get subdirectories
foreach(var subDir in dir.GetDirectories())
{
xmlInfo.Add(CREATEXML(subDir), false);
}
return xmlInfo;
}
You can add one more method that will handle subdirectories
private static XElement CreateXML(DirectoryInfo dir)
{
var xmlInfo = new XElement("serverfiles");
//get all the files first
foreach (var file in dir.GetFiles())
{
xmlInfo.Add(new XElement("file", new XAttribute("name", file.Name)));
}
//get subdirectories
foreach (var subDir in dir.GetDirectories())
{
xmlInfo.Add(CreateSubdirectoryXML(subDir));
}
return xmlInfo;
}
private static XElement CreateSubdirectoryXML(DirectoryInfo dir)
{
//get directories
var xmlInfo = new XElement("folder", new XAttribute("name", dir.Name));
//get all the files first
foreach (var file in dir.GetFiles())
{
xmlInfo.Add(new XElement("file", new XAttribute("name", file.Name)));
}
//get subdirectories
foreach (var subDir in dir.GetDirectories())
{
xmlInfo.Add(CreateSubdirectoryXML(subDir));
}
return xmlInfo;
}
EDIT:
Added sorting:
private static XElement CreateXML(DirectoryInfo dir)
{
var xmlInfo = new XElement("serverfiles");
//get all the files first
foreach (var file in dir.GetFiles())
{
xmlInfo.Add(new XElement("file", new XAttribute("name", file.Name)));
}
//get subdirectories
var subdirectories = dir.GetDirectories().ToList().OrderBy(d => d.Name);
foreach (var subDir in subdirectories)
{
xmlInfo.Add(CreateSubdirectoryXML(subDir));
}
return xmlInfo;
}
private static XElement CreateSubdirectoryXML(DirectoryInfo dir)
{
//get directories
var xmlInfo = new XElement("folder", new XAttribute("name", dir.Name));
//get all the files first
foreach (var file in dir.GetFiles())
{
xmlInfo.Add(new XElement("file", new XAttribute("name", file.Name)));
}
//get subdirectories
var subdirectories = dir.GetDirectories().ToList().OrderBy(d => d.Name);
foreach (var subDir in subdirectories)
{
xmlInfo.Add(CreateSubdirectoryXML(subDir));
}
return xmlInfo;
}
I think this solution can be better
//get directories
var xmlInfo = new XElement("folder",
new XElement("name", dir.Name),
new XElement("lastModify", dir.LastWriteTime),
new XElement("Attributes", dir.Attributes));
//get subdirectories
foreach (var subDir in dir.GetDirectories())
{
xmlInfo.Add(CREATEXML(subDir));
}
//get all the files
foreach (var file in dir.GetFiles())
{
xmlInfo.Add(new XElement("File",
new XElement("name", file.Name),
new XElement("size", file.Length),
new XElement("lastModify", file.LastWriteTime),
new XElement("Attributes", file.Attributes.ToString())));
}
return xmlInfo;
that's what i try but it return only the newest file from only the top directories under the root
if(Directory.Exists("YourPath"))
foreach (string _tempFiles in Directory.GetDirectories("YourPath")
.Select(directory => Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories)
.OrderByDescending(File.GetLastWriteTime)
.FirstOrDefault()))
This returns all newest files of each directory(including root):
var rootDirFile = Directory
.EnumerateFiles(yourPath, "*.*", SearchOption.TopDirectoryOnly)
.OrderByDescending(f => File.GetLastWriteTime(f))
.Take(1);
var allNewestFilesOfEachFolder = Directory
.EnumerateDirectories(yourParth, "*.*", SearchOption.AllDirectories)
.Select(d => Directory.EnumerateFiles(d, "*.*")
.OrderByDescending(f => File.GetLastWriteTime(f))
.FirstOrDefault());
// put both together, the root-file first
allNewestFilesOfEachFolder = rootDirFile.Concat(allNewestFilesOfEachFolder);
If there's no file in a directory the file is null, so the number of files is equal to the number of folders.
Note that Linq is not the right tool for System.IO since error-handling is difficult.
I wrote a basic recursive function to handle this:
// Dictionary:
// Key = The directory name.
// Value = The most recently modified file for that directory.
public static Dictionary<string, string> GetNewestFiles(string directory)
{
return GetNewestFiles(directory, null);
}
static Dictionary<string, string> GetNewestFiles(string directory,
Dictionary<string, string> dictionary)
{
if(dictionary == null)
dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
try
{
var files = from file in Directory.GetFiles(directory)
select new FileInfo(file);
var latestFile = files.OrderByDescending(file => { return file.LastWriteTimeUtc; }).FirstOrDefault();
if (latestFile != null)
dictionary[latestFile.DirectoryName] = latestFile.FullName;
}
catch { }
foreach (var subDirectory in Directory.GetDirectories(directory))
{
try
{
GetNewestFiles(subDirectory, dictionary);
}
catch { }
}
return dictionary;
}
So then you can just call it like so:
var fileDictionary = GetNewestFiles(#"C:\MyFolder");
Using C#, I have created a program that executes a command line perl script and captures the output to a text file. The output is from ClearCase and was a huge pain to figure out, there isnt much in the way of documentation of the ClearCase plugin.
Anyhow now what I would like to do is skip the file and only use the output from the console...
The output appears like
"\Directory\subDirectory\subsubDir\etc\dir\##main\branch\version\4"
"\Directory\subDirectory\subsubDir\etc\dir\somefile.txt##main\branch\version\3"
"\Directory\subDirectory\subsubDir\etc\dir\somefile.txt##\branch\version\1"
I then want to basically load that into a tree view. So that the tree appears as a directory listing...that could be sortable so it is easy to tell the latest version of any particular file or directory.
One problem is there are multiple instances of the same directory and files as each version of a particular file is counted and there may be different branches and versions...
I have trouble because I am only slightly experienced with C# and I can't quite comprehend how to load arrays in arrays then have it neatly go to a dynamic tree view (keeping its associations).
Most online examples for tree views I find have hard coded strings, not dynamic strings[].
Does anyone have any experience doing this? Or know of any tricks? I cant decide if Visual Studio's line editing is best to use to split the directories or to use a regex...I will at a later point (once I get this figured out) want to Re-send this data into clearcase via command prompt to auto checkout these associated files...but that part seems easier from this point of view...
I can't post the code that I have, closed loop lan only.
The example of treeview that I've been scratching my head on is from DotNetPerls :
private void Form1_Load(object sender, EventArgs e)
{
//
// This is the first node in the view.
//
TreeNode treeNode = new TreeNode("Windows");
treeView1.Nodes.Add(treeNode);
//
// Another node following the first node.
//
treeNode = new TreeNode("Linux");
treeView1.Nodes.Add(treeNode);
//
// Create two child nodes and put them in an array.
// ... Add the third node, and specify these as its children.
//
TreeNode node2 = new TreeNode("C#");
TreeNode node3 = new TreeNode("VB.NET");
TreeNode[] array = new TreeNode[] { node2, node3 };
//
// Final node.
//
treeNode = new TreeNode("Dot Net Perls", array);
treeView1.Nodes.Add(treeNode);
and for arrays to tree from D.Morton # MSDN
public void AddTreeViewItem(string[] item)
{
TreeNodeCollection nodes = treeView1.Nodes;
for (int i = 0; i < item.Length; i++)
nodes = nodes.Find(item[i], false).Length > 0 ? nodes.Find(item[i], false)
[0]. Nodes : nodes.Add(item[i], item[i]).Nodes;
}
The TreeBuilder class below will build a proper tree for you. I've tested it with your examples and it works. Where there are gaps in the tree it will recursively trim the path to create the missing parent nodes and then unwind to add the item itself. The TreeBuilder breaks the problem into handling file paths and version paths by splitting your lines at the ##. It treats the version path as a child of the file path. This allows the built in Path.GetFileName and Path.GetDirectoryName functions to be used to label the nodes and trim the paths to find parents.
using System;
using System.Windows.Forms;
using System.IO;
public class TreeBuilder
{
public TreeBuilder()
{
TreeNode rootNode = new TreeNode(#"\");
rootNode.Name = #"\";
RootNode = rootNode;
}
public TreeNode RootNode
{
get;
set;
}
public void AddItems(string[] items)
{
Array.Sort(items);
if (string.IsNullOrEmpty(RootNode.Name))
{
RootNode.Name = #"\";
}
foreach (string item in items)
{
string[] itemParts = item.Split(new string[] { "##" }, StringSplitOptions.None);
string filePath = itemParts[0].TrimEnd('\\');
string versionPath = itemParts[1];
TreeNode fileNode = AddNode(RootNode, filePath);
TreeNode versionNode = AddNode(fileNode, filePath + "##", versionPath);
}
}
public TreeNode AddNode(TreeNode topNode, string path)
{
return AddNode(topNode, null, path);
}
public TreeNode AddNode(TreeNode topNode, string pathPrefix, string path)
{
pathPrefix = pathPrefix ?? string.Empty;
TreeNode node = null;
if (!string.IsNullOrEmpty(path) && topNode.Name != path)
{
string parentPath = Path.GetDirectoryName(path);
TreeNode[] matchingNodes = topNode.Nodes.Find(pathPrefix + path, true);
if (matchingNodes == null || matchingNodes.Length == 0)
{
string nodeLabel = Path.GetFileName(path);
nodeLabel = string.IsNullOrEmpty(nodeLabel) ? #"\" : nodeLabel;
node = new TreeNode(nodeLabel);
node.Name = pathPrefix + path;
TreeNode[] parentNodes = topNode.Nodes.Find(pathPrefix + parentPath, true);
TreeNode parentNode = null;
if (parentNodes != null && parentNodes.Length > 0)
{
parentNode = parentNodes[0];
parentNode.Nodes.Add(node);
}
else
{
parentNode = AddNode(topNode, pathPrefix, parentPath);
parentNode.Nodes.Add(node);
}
}
else
{
node = matchingNodes[0];
}
}
else
{
node = topNode;
}
return node;
}
}
Here's an example of how you'd use it in a form with a TreeView on it:
private void Form1_Load(object sender, EventArgs e)
{
string[] fileStrings = new String[]
{
#"\Directory\subDirectory\subsubDir\etc\dir\##main\branch\version\4",
#"\Directory\subDirectory\subsubDir\etc\dir\somefile.txt##main\branch\version\3",
#"\Directory\subDirectory\subsubDir\etc\dir\somefile.txt##\branch\version\1"
};
TreeBuilder treeBuilder = new TreeBuilder();
treeBuilder.AddItems(fileStrings);
treeView1.Nodes.Add(treeBuilder.RootNode);
treeView1.ExpandAll();
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
TreeNode selectedNode = treeView1.SelectedNode;
MessageBox.Show(string.Format("Label: {0}\nClearCase path: {1}\nTree path: {2}", selectedNode.Text, selectedNode.Name, selectedNode.FullPath));
}
One of possible solutions could be split the string by directories:
string[] directories = "\Directory\subDirectory\subsubDir\etc\dir\##main\branch\version\4".Split(Path.DirectorySeparatorChar);
after have a Dictionary<string, List<string>> tree, where key is a Directory path, and List<string> a list of directories or files of "key" directory.
If it's not something you're searching for, please clarify.
Im working with file systems and I have a List<> of file objects that have the file path as a property. Basically I need to create a treeview in .NET but im struggling to think of the best way to go about doing this as I need to create a tree structure from a list like:
C:/WINDOWS/Temp/ErrorLog.txt
C:/Program Files/FileZilla/GPL.html
C:/Documents and Settings/Administrator/ntuser.dat.LOG
etc....
The list is not structured at all and I cant make any changes to the current object structure.
I'm working in C#.
Many thanks for all who contribute
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);
}
}
I would turn the string into a FileInfo.
Once you have the FileInfo object, you can use the Directory property to retrieve the DirectoryInfo for each path.
Once you have the DirectoryInfo for the path, it's easy to "walk up" the Parent reference in DirectoryInfo to turn each path into a list of directories + filename - ie:
{"C:","Windows","Temp","ErrorLog.txt"}
This should be fairly straightforward to insert into your treeview. Just look for each section of the path in turn, and if it doesn't exist, add it....
give recursion a try.
private void AddFiles()
{
// Iterate your list with FileInfos here
foreach( var fileInfo in new Collection<FileInfo>() )
{
GetOrCreateTreeNode( fileInfo.Directory ).Nodes.Add( new TreeNode( fileInfo.Name ) );
}
}
private TreeNode GetOrCreateTreeNode( DirectoryInfo directory )
{
if( directory.Parent == null )
{
// Access your TreeView control here:
var rootNode = <TreeView>.Nodes[directory.Name];
if( rootNode == null )
{
rootNode = new TreeNode(directory.Name);
// Access your TreeView control here:
<TreeView>.Nodes.Add( rootNode );
}
return rootNode;
}
var parent = GetOrCreateTreeNode( directory.Parent );
var node = parent.Nodes[directory.Name];
if( node == null )
{
node = new DirectoryNode( directory );
parent.Nodes.Add( node );
}
return node;
}
This code should give you only an idea - I have to admit that I did not test it before posting here.
private void Form1_Load(object sender, EventArgs e)
{
var paths = new List<string>
{
#"C:\WINDOWS\AppPatch\MUI\040C",
#"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727",
#"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MUI",
#"C:\WINDOWS\addins",
#"C:\WINDOWS\AppPatch",
#"C:\WINDOWS\AppPatch\MUI",
#"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MUI\0409"
};
treeView1.PathSeparator = #"\";
PopulateTreeView(treeView1, paths, '\\');
}
private static void PopulateTreeView(TreeView treeView, IEnumerable<string> paths, char pathSeparator)
{
TreeNode lastNode = null;
string subPathAgg;
foreach (string path in paths)
{
subPathAgg = string.Empty;
foreach (string subPath in path.Split(pathSeparator))
{
subPathAgg += subPath + pathSeparator;
TreeNode[] nodes = treeView.Nodes.Find(subPathAgg, true);
if (nodes.Length == 0)
if (lastNode == null)
lastNode = treeView.Nodes.Add(subPathAgg, subPath);
else
lastNode = lastNode.Nodes.Add(subPathAgg, subPath);
else
lastNode = nodes[0];
}
}
}
EHosca's piece worked for me perfectly, with one change - I had to set lastnode to nothing after the foreach path in paths area.
This is eHosca's code above, ported to VB.
Private Sub PopulateTreeView(tv As TreeView, paths As List(Of String), pathSeparator As Char)
Dim lastnode As TreeNode = Nothing
Dim subPathAgg As String
For Each path In paths
subPathAgg = String.Empty
lastnode = Nothing
For Each subPath In path.Split(pathSeparator)
subPathAgg += subPath + pathSeparator
Dim nodes() As TreeNode = tv.Nodes.Find(subPathAgg, True)
If nodes.Length = 0 Then
If IsNothing(lastnode) Then
lastnode = tv.Nodes.Add(subPathAgg, subPath)
Else
lastnode = lastnode.Nodes.Add(subPathAgg, subPath)
End If
Else
lastnode = nodes(0)
End If
Next
Next
End Sub