C# build menu from directory structure - c#

I have currently the following problem. I have a directory structure like
root
- level 1
- level 1.1
- level 1.2
- level 2
- level 2.1
- level 3
- level 4
- level 4.1
from this I want to build a menu. so root will be the menu item to click on and all the level will be needed to drill down to the information you want to get.
As I'm pretty new to C# (not programming) I wanted to know if there is any help from .NET for this task. I don't want to start to fiddel around with code that is already there...
Thanks for any input!

You can use the DirectoryInfo class to obtain a list of all sub-directories for a given root folder. You should the perform a recursive search on sub-directories and build your menu using that data.
Here is some code that will do the job for you, it assumes you already have a MenuStrip called menuStrip1:
public void BuildMenu()
{
//first we get the DirectoryInfo for your root folder, this will be used to find the first set of sub-directories
DirectoryInfo dir = new DirectoryInfo(#"C:\MyRootFolder\");//Change this
//next we create the first MenuItem to represent the root folder, this is created using the GetMenuItem function
ToolStripMenuItem root = GetMenuItem(dir);
//we add our new root MenuItem to our MenuStrip control, at this point all sub-menu items will have been added using our recursive function
menuStrip1.Items.Add(root);
}
public ToolStripMenuItem GetMenuItem(DirectoryInfo directory)
{
//first we create the MenuItem that will be return for this directory
ToolStripMenuItem item = new ToolStripMenuItem(directory.Name);
//next we loop all sub-directory of the current to build all child menu items
foreach (DirectoryInfo dir in directory.GetDirectories())
{
item.DropDownItems.Add(GetMenuItem(dir));
}
//finally we return the populated menu item
return item;
}
Dont forget to change the root folder path!
NOTE: Yorye Nathan has made a good point about short-cut folders. If any of your folders is a short-cut to a parent folder this will cause an endless loop. The easiest way to solve this is to make sure your structure doesn't contain any short-cuts. This may be an easy option for you assuming you have a specifically built structure for this application. If however, you are running this on a user-defined root folder you will want to check for these.
You could modify the GetMenuItem function as below to account for this, assuming .Net 3.5 or higher (LINQ + optional parameters):
public ToolStripMenuItem GetMenuItem(DirectoryInfo directory, List<DirectoryInfo> currentFolders = null)
{
if (currentFolders == null)
currentFolders = new List<DirectoryInfo>();
currentFolders.Add(directory);
ToolStripMenuItem item = new ToolStripMenuItem(directory.Name);
foreach (DirectoryInfo dir in directory.GetDirectories())
{
if (!currentFolders.Any(x => x.FullName == dir.FullName))//check to see if we already processed this folder (i.e. a unwanted shortcut)
{
item.DropDownItems.Add(GetMenuItem(dir, currentFolders));
}
}
return item;
}

EDITED Now supporting recursive folders (ignore to prevent endless loop)
public static MenuStrip CreateMenu(string rootDirectoryPath)
{
var dir = new DirectoryInfo(rootDirectoryPath);
var menu = new MenuStrip();
var root = new ToolStripMenuItem(dir.Name);
var includedDirs = new List<string> {dir};
menu.Items.Add(root);
AddItems(root, dir, includedDirs);
return menu;
}
private static void AddItems(ToolStripDropDownItem parent, DirectoryInfo dir, ICollection<string> includedDirs)
{
foreach (var subDir in dir.GetDirectories().Where(subDir => !includedDirs.Contains(subDir.FullName)))
{
includedDirs.Add(subDir.FullName);
AddItems((ToolStripMenuItem)parent.DropDownItems.Add(subDir.Name), subDir, includedDirs);
}
}

http://msdn.microsoft.com/en-us/library/bb513869.aspx
http://www.stillhq.com/dotnet/000003.html
http://www.codeproject.com/Articles/11599/Recursive-function-to-read-a-directory-structure

Related

why in treeView1 when i make CollapseAll and Expand it's not really doing it ?

When i'm running the program i see the root node
Countries
Then when i click on it i see all the countries nodes under Countries
But i want when running the program that it already will show all the countries nodes without clicking on Countries.
I tried in the constructor:
PopulateTree(mainPath, treeView1.Nodes.Add("Countries"));
treeView1.CollapseAll();
treeView1.Nodes[0].Expand();
The populatetree
public void PopulateTree(string dir, TreeNode node)
{
DirectoryInfo directory = new DirectoryInfo(dir);
foreach (DirectoryInfo d in directory.GetDirectories())
{
TreeNode t = new TreeNode(d.Name);
PopulateTree(d.FullName, t);
node.Nodes.Add(t);
}
foreach (FileInfo f in directory.GetFiles())
{
TreeNode t = new TreeNode(f.Name);
node.Nodes.Add(t);
}
}
But it's not doing it i still see Countries when running the program and to see all the childs nodes i need to click on Countries.
This lines not effect
treeView1.CollapseAll();
treeView1.Nodes[0].Expand();
TreeNode.Expand expands only Nodes[0] down to the next level of nodes. You should use TreeNode.ExpandAll to expand all child nodes of Countries node:
treeView1.Nodes[0].ExpandAll()
NOTE: There is one thing you should keep in mind. If handle is not created for TreeView control, then something like lazy collapsing-expanding is working here. I.e. each node has expandOnRealization and collapseOnRealization fields. When you are trying to expand node before tree handle is created, then just expandOnRealization flag is set to true. No TVM_EXPAND windows messages are sent to actually expand that node. Same for collapsing. When tree node is realized, then there is following code executed:
// If node expansion was requested before the handle was created,
// we can expand it now.
if (expandOnRealization) {
Expand();
}
// If node collapse was requested before the handle was created,
// we can expand it now.
if (collapseOnRealization) {
Collapse();
}
So, if node was marked both for collapsing and expanding, then it would be expanded first and then collapsed. I believe it's your case.

Get a list of files/directories in an open Explorer window in C#

I'm trying to pull out the list of files and directories listed in an open Explorer window (in the same order as they're displayed) so that I can look through it, then set focus to a particular item.
I found this code here that allows me to get the selected items, however I'm not sure if it's possible to use this approach to get all items:
List<string> SelectedFiles() {
string filename;
List<string> selected = new List<string>();
var shell = new Shell32.Shell();
foreach (SHDocVw.InternetExplorer window in new SHDocVw.ShellWindows()) {
filename = Path.GetFileNameWithoutExtension(window.FullName).ToLower();
if (filename.ToLowerInvariant() == "explorer") {
((Shell32.IShellFolderViewDual2)window.Document).SelectItem()
foreach (Shell32.FolderItem item in items) {
selected.Add(item.Path);
}
}
}
return selected;
}
It looks like this Shell32 approach would also allow me to select an item programmatically, which is the other part I'm trying to accomplish. Instead of SelectedItems(), I would call SelectItem(), though I'm not sure how to use that function.
Anyone know of a way to get the list of files/directories from an open Windows Explorer window (and ideally set focus to an item)? Perhaps a P/Invoke kind of thing?
I was able to modify that code snippet I found to list all files/directories instead of just the selected ones.
Here's what I ended up with:
List<string> FilesAndFolders() {
string filename;
List<string> explorerItems = new List<string>();
var shell = new Shell32.Shell();
foreach (SHDocVw.InternetExplorer window in new SHDocVw.ShellWindows()) {
filename = Path.GetFileNameWithoutExtension(window.FullName).ToLower();
if (filename.ToLowerInvariant() == "explorer") {
Shell32.Folder folder = ((Shell32.IShellFolderViewDual2)window.Document).Folder;
Shell32.FolderItems items = folder.Items();
foreach (Shell32.FolderItem item in items) {
explorerItems.Add(item.Path);
}
}
}
return explorerItems;
}
Edit:
To select an item, you call:
((Shell32.IShellFolderViewDual2)window.Document).SelectItem(item, 1);
where window is a SHDocVw.InternetExplorer, and item is a Shell32.FolderItem (from folder.Items() in the above example).
To deselect, it, pass in 0 instead of 1 as the second overload.

Populating treeview asp.net

Is there a way of populating a treeview including the parent's sub folder? My code only can only view files on its parent folder but once it in a sub folder it won't open.
Main problem: I can't open a file when it's inside a sub folder of my MapPath
Here's mine, so far it only gets the parent node it doesn't get the parent's sub folder:
protected void Page_Load(object sender, EventArgs e)
{
TreeView1.Nodes[0].Value = Server.MapPath("~/Files");
}
protected void TreeView1_TreeNodePopulate(object sender, TreeNodeEventArgs e)
{
if (e.Node.ChildNodes.Count == 0)
{
DirectoryInfo directory = null;
directory = new DirectoryInfo(e.Node.Value);
foreach (DirectoryInfo subtree in directory.GetDirectories())
{
TreeNode subNode = new TreeNode(subtree.Name);
subNode.Value = subtree.FullName;
try
{
if (subtree.GetDirectories().Length == 0 | subtree.GetFiles().Length == 0)
{
subNode.SelectAction = TreeNodeSelectAction.SelectExpand;
subNode.PopulateOnDemand = true;
subNode.NavigateUrl = "";
}
}
catch
{
}
e.Node.ChildNodes.Add(subNode);
}
foreach (FileInfo fi in directory.GetFiles())
{
TreeNode subNode = new TreeNode(fi.Name);
e.Node.ChildNodes.Add(subNode);
subNode.NavigateUrl = "Files/" + fi.Name;
}
}
}
There's absolutely nothing wrong with your code. I've run a test it works like a charm. So, a few things to point out which are NOT exactly clear in your question.
1.
You need to hook the TreeView1_TreeNodePopulate to your TreeView control. You can do that declaratively from the markup...
<asp:TreeView ID="TreeView1" runat="server" OnTreeNodePopulate="TreeView1_TreeNodePopulate">
or, imperatively from code behind...
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
TreeView1.TreeNodePopulate += TreeView1_TreeNodePopulate;
}
otherwise this event handler will never get hit
2.
In addition to hooking up the OnTreeNodePopulate event you need to add at least one node from the markup and set its PopulateOnDemand property to true...
<Nodes>
<asp:TreeNode PopulateOnDemand="true" Text="Root"></asp:TreeNode>
</Nodes>
if you don't set this property this event will never get triggered. Another reason to add this "root" node is to avoid an IndexOutOfRangeException or NullReference exception here...
TreeView1.Nodes[0].Value = Server.MapPath("~/Files");
Keeping all that in mind, it should work just fine
Edit based on comment
I didn't noticed the bit where you said you want to open the files when the tree node is clicked. And that happens because you are passing the url when creating and adding the nodes. Basically I'd recommend not using Server.MapPath on page load, add the virtual server path only...
TreeView1.Nodes[0].Value = "~/Files";
then use Server.MapPath when creating the DirectoryInfo object...
directory = new DirectoryInfo(Server.MapPath(e.Node.Value));
and set the value of the tree node (inside the "directories" iteration) by appending a parent value's...
subNode.Value = string.Format("{0}/{1}", e.Node.Value, subtree.Name);
and finally, within the "files" iteration, set the NavigateUrl's property of the node like below...
subNode.NavigateUrl = string.Format("{0}/{1}", e.Node.Value, fi.Name);
That should give you a proper link in your file nodes. Notice, that this is similar to issuing an http request using a web browser and the request will be handled by IIS and the ASP.NET pipeline...which means that you will only be able to see files that can be handled by IIS by default (e.g. images, etc)

GeneratorPosition Error on Bound Observable Collection

I am using a Transitionals Slideshow control which has an observable collection of strings bound to the itemsource. These strings are the file paths to each picture in the slidehow. When I first load the WPF app, it runs this method correctly (using a directory path to generate the PicSlideShowCollection):
public void SelectImages(string path)
{
// Validate
if (string.IsNullOrEmpty(path)) throw new ArgumentException("path");
PicSlideShowCollection.Clear();
// Get directory info for specified path
DirectoryInfo di = new DirectoryInfo(path);
// Image mask
string[] extensions = new string[] { "*.jpg", "*.png", "*.gif", "*.bmp" };
// Search for all
foreach (string extension in extensions)
{
foreach (FileInfo fi in di.GetFiles(extension.ToLower()))
{
PicSlideShowCollection.Add(fi.FullName);
}
}
}
However, I have a button that allows the user to change the directory of images to use in the slideshow and re-runs the above method. When that is executed, I get this error:
GeneratorPosition '-1,1' passed to Remove does not have Offset equal
to 0.
This occurs on the PicSlideShowCollection.Clear() instruction.
If I comment that instruction, the new directory images get ADDED TO the original directory pictures which is NOT what I want.
I know this has to do with the PicSlideShowCollection being used as an item source to the Slide show control, but I need to know how I can prevent this error from occuring.
Thank you!
Slideshow.AutoAdvance = false;
Slideshow.SelcetedIndex=-1;
var count=PicSlideShowCollection.Count;
forearch(var item in newsources)
{
PicSlideShowCollection.Add(item);
}
while(count--)
PicSlideShowCollection.RemoveAt(0);
Slideshow.SelcetedIndex=0;
I can't explain why this error occurs. GeneratorPosition is used by the ItemContainerGenerator of an ItemsControl, which should simply work when you bind to its ItemsSource property and add or remove items to/from the source collection. Clearing the source collection is of course also a valid operation.
A possible workaround for the problem would be to reset the ItemsSource each time you switch to another image directory. So instead of clearing the existing collection
PicSlideShowCollection.Clear();
create a new collection and set ItemsSource to the new collection:
PicSlideShowCollection = new ObservableCollection<string>();
slideShowControl.ItemsSource = PicSlideShowCollection;

get folder to treeview

I am using the following code to get the contents of a folder into a TreeView. But the current code always adds the contents to the root of the TreeView. It does not add them as child nodes of their parent folder's node.
Can you help me?
void Recurse(string path)
{
DirectoryInfo info = new DirectoryInfo(path);
TreeNode root = new TreeNode(info.Name);
string[] sub = Directory.GetDirectories(info.FullName);
TreeNode node = new TreeNode();
MailTree.Nodes.Add(root);
if (sub.Length == 0) {
}
else
{
foreach(string i in sub)
{
DirectoryInfo subinfo = new DirectoryInfo(i);
root.Nodes.Add(subinfo.Name);
Recurse(i);
}
//MailTree.Nodes.Add(root);
}
}
You should be passing a root node as part of your Rescure method, something like Rescure(string path, TreeNode currentRoot).
Now, you can call currentRoot.Nodes.Add(root) in place of MailTree.Nodes.Add(root), which will ensure that the brances are added only to the current level. You also need to change your call in the loop to Rescure(i, root).
Finally, your initial call to Rescure should include a reference to a pre-created root node, so something like Rescure(initialDirectory, initialRootNode).
One thing I would add is that your method and variable names should be changed to reflect their meaning. Yes, you are recursing, but why? A better name for the method might be TraverseDirectory. Similarly, rather than foreach(string i in sub), why not foreach(string directoryName in sub)? Having clear code is almost as important as having correct code.
The recursive part of your function is always adding the child nodes to the root. You need to add in the "parent node" as a parameter of your recursive function. Something like this:
void Recurse(string path, TreeNode parentNode)
{
DirectoryInfo info = new DirectoryInfo(path);
TreeNode node = new TreeNode(info.Name);
if (parentNode == null)
MailTree.Nodes.Add(node);
else
parentNode.Nodes.Add(node);
string[] sub = Directory.GetDirectories(path);
if (sub.Length != 0)
{
foreach(string i in sub)
{
Recurse(i, node);
}
}
}
I can't se any error in the code at a first glance, but I can suggesto to take another approach: the one you show is too expensive. Just fill a level on the tree, and put some dummy item as a leaf in each nodes you add. Then intercept the NodeExpanding event, remove the dummy node, and add the subnodes ( applying recursively the same strategy of adding the dummy child nodes )

Categories