I have a problem with nodes selection. Here is what I want to achieve:
+ [ ] Directory1
- [x] Files
[ ] File1
[ ] File2
[ ] File3
[ ] File4
[ ] File5
[ ] File6
When I click on Files (checkBox enabled), it select only Files the folder node, but instead I want it to check and select entire files while single Directory selection (i.e. By Clicking Files, it select all files contained in it). Though there are thousands of files in that directory so it is impossible to check each file manually.
I think I'm missing something here.
private void SetCheck(TreeNode node, bool check)
{
foreach (TreeNode n in node.Nodes)
{
n.Checked = check;
if (n.Nodes.Count != 0)
{
SetCheck(n, check);
}
}
}
and/or
private void GetCheckedFiles(TreeNode node, List<string> fileNames)
{
if (node.Nodes.Count == 0)
{
if (node.Checked)
{
fileNames.Add(node.FullPath);
}
}
else
{
foreach (TreeNode n in node.Nodes)
{
GetCheckedFiles(n, fileNames);
}
}
}
Assuming your code runs from the AfterCheck event, you would have to remove the handler or use a variable to prevent running the same code every time the checkmark changes for every node that gets affected by the SetCheck routine.
Example:
private bool ignoreCheckEvent = false;
void treeView1_AfterCheck(object sender, TreeViewEventArgs e) {
if (!ignoreCheckEvent) {
ignoreCheckEvent = true;
SetCheck(e.Node, e.Node.Checked);
ignoreCheckEvent = false;
}
}
Related
I am parsing an XML file and displaying it as a TreeView. Then, I need to do a phrase search, deleting all branches that do not contain a mention
Now I have the following recursive function code, but it does not work correctly. I must say right away that there can be quite a lot of branches and I cannot know in advance the degree of nesting
void RecursiveSearch(TreeViewItem item)
{
foreach (TreeViewItem children in item.Items)
{
if (children.Items.Count > 0)
{
RecursiveSearch(children);
}
if (children.Header.ToString().Contains(searchTB.Text) == false)
{
item.Items.Remove(children);
return;
}
}
return;
}
And call this on Button Click:
private void searchBtn_Click(object sender, RoutedEventArgs e)
{
RecursiveSearch((TreeViewItem)treeView.Items[0]);
}
Could you suggest what is wrong with it?
Thanks in advance
From what i read in your comments my suggestion would:
void RecursiveSearch(TreeViewItem item)
{
foreach (TreeViewItem children in item.Items)
{
if (children.Items.Count > 0)
{
RecursiveSearch(children);
}
if (children.Header.ToString().Contains(searchTB.Text) == false)
{
If (children.Items.Count == 0)
{
item.Items.Remove(children);
}
return;
}
}
return;
}
Consider a TreeView structure such as the following:
The goal is to have a node's descendants check or uncheck themselves accordingly when a particular node is checked. For example, in the above, if "D" is unchecked, "D A", "D A A" and "D A B" should uncheck themselves.
Currently, the code being used is as follows:
private void treeView_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Action != TreeViewAction.Unknown)
{
if (e.Node.Checked)
{
checkChildNodes(e.Node.Nodes);
}
else
{
uncheckChildNodes(e.Node.Nodes);
}
}
}
private void checkChildNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
node.Checked = true;
if (node.Nodes.Count>0)
checkChildNodes(node.Nodes);
}
}
private void uncheckChildNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
node.Checked = false;
if (node.Nodes.Count>0)
uncheckChildNodes(node.Nodes);
}
}
The problem with this is that sometimes the checking/unchecking of descendants does not occur, when the "root" node is clicked very fast. How can this be solved?
What has also been tried, is using the BeforeCheck event, as per the following link: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.treeview.aftercheck?view=netcore-3.1
I have an Observable collection of Paths.
The thing I want to do is to update my treeView on collection changed.
Could you please help me with creating method that takes Treeview, FilePath and PathSeparator as parameters and adding new node to my treeView. This is what i have now:
private void MyCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
TreeViewAddNode(TreeView,Path,PathSeparator)
}
TreeViewAddNode(TreeView treeView, string path, char pathSeparator)
{
foreach (string subPath in path.Split(pathSeparator))
{
//Hear should be logic to add new nodes if they don't exist }
}
}
As the Result I wanna have something like that:
C:
--Temp
----File1.txt
----File2.txt
----New Foledr
-------File3.txt
--AnotherFolder
----File4.txt
D:
--New Folder
----File.txt
EDIT
Now with better understanding on what is being asked:
private void TreeViewAddNode(TreeView treeView, string path, char pathSeparator)
{
string[] split = path.Split(pathSeparator);
for(int i = 0; i < split.Length; i++)
{
if(i == 0)
{
checkTreeView(treeView, split[0]);
}
else
{
TreeNode node = treeView1.Nodes.Find(split[i - 1], true)[0];
checkNodes(node, split[i]);
}
}
}
private void checkTreeView(TreeView treeView, string path)
{
bool exists = false;
foreach(TreeNode node in treeView.Nodes)
{
if(node.Text == path)
{
exists = true;
}
}
if(!exists)
{
TreeNode node = treeView.Nodes.Add(path);
node.Name = path;
}
}
private void checkNodes(TreeNode parent, string path)
{
bool exists = false;
foreach(TreeNode node in parent.Nodes)
{
if(node.Text == path)
{
exists = true;
}
}
if(!exists)
{
TreeNode node = parent.Nodes.Add(path);
node.Name = path;
}
}
checkTreeView checks if the path is allready present in the treeview nodes. if it isn't add it to the treeview. Same goes for checkNodes.
This is how i'm adding the nodes to the TreeNode:
private int total_dirs;
private int searched_until_now_dirs;
private int max_percentage;
private TreeNode directories_real_time;
private string SummaryText;
private TreeNode CreateDirectoryNode(string path, string name , int recursive_levl )
{
var directoryNode = new TreeNode(name);
var directoryListing = GetDirectoryListing(path);
var directories = directoryListing.Where(d => d.IsDirectory);
var files = directoryListing.Where(d => !d.IsDirectory);
total_dirs += directories.Count<FTPListDetail>();
searched_until_now_dirs++;
int percentage;
foreach (var dir in directories)
{
directoryNode.Nodes.Add(CreateDirectoryNode(dir.FullPath, dir.Name, recursive_levl+1));
if (recursive_levl == 1)
{
TreeNode temp_tn = (TreeNode)directoryNode.Clone();
this.BeginInvoke(new MethodInvoker( delegate
{
UpdateList(temp_tn);
}));
}
percentage = (searched_until_now_dirs * 100) / total_dirs;
if (percentage > max_percentage)
{
SummaryText = String.Format("Searched dirs {0} / Total dirs {1}", searched_until_now_dirs, total_dirs);
max_percentage = percentage;
backgroundWorker1.ReportProgress(percentage, SummaryText);
}
}
percentage = (searched_until_now_dirs * 100) / total_dirs;
if (percentage > max_percentage)
{
SummaryText = String.Format("Searched dirs {0} / Total dirs {1}", searched_until_now_dirs, total_dirs);
max_percentage = percentage;
backgroundWorker1.ReportProgress(percentage, SummaryText);
}
foreach (var file in files)
{
directoryNode.Nodes.Add(new TreeNode(file.Name));
numberOfFiles.Add(file.FullPath);
}
return directoryNode;
}
Then i'm updating the treeView1:
DateTime last_update;
private void UpdateList(TreeNode tn_rt)
{
TimeSpan ts = DateTime.Now - last_update;
if (ts.TotalMilliseconds > 200)
{
treeView1.BeginUpdate();
treeView1.Nodes.Clear();
treeView1.Nodes.Add(tn_rt);
TreeViewXmlPopulation.SerializeTreeView(treeView1, #"c:\XmlFile\Test.xml");
ExpandToLevel(treeView1.Nodes, 1);
treeView1.EndUpdate();
}
}
In the end when i see the directories and files structure in the treeView1 i want to know when i select an item with the mouse if it's a directory or a file.
The directories and files in the treeView1 i get them from my ftp server.
For example if i click on A i want to know that i selected a directory and if i click on install.exe i want to know that it's a file.
This is how i select an item in the treeView1:
private void treeView1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
treeView1.SelectedNode = treeView1.GetNodeAt(e.X, e.Y);
if (treeView1.SelectedNode != null)
{
menuStrip.Show(treeView1, e.Location);
}
}
}
I have a context menu strip with options:
private void menuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem.Text == "Delete")
{
Delete(treeView1.SelectedNode.FullPath);
}
}
In the FullPath it can be just a single file or a directory name with or without files inside.
How do i know if it's a file or a directory ? In the Delete method i want to decide what to do if it's a file then delete a file and if it's a directory then do something else.
I thought to use Tag propert but not sure how to use it. I tried to add in the CreateDirectoryNode this:
directoryNode.Tag = directoryNode.Nodes;
But i'm not sure if it's the right way to do it and how to use the Tag later to identify if it's file or directory.
This is my solution:
first save the root as a full dir (directly in the name or some string in background)
second once you click on a node get its full name node.FullPath
is this way you'll get a string that is the full path that this node represents (directory of file )
now as Scott Nimrod allready sed check if it is a file or dir using
!System.IO.Directory.Exists(node.FullPath) //==> yep this is a file
take a look of this example(from my project that iterate over recursivly and create a tree)
this example will iterate over and look for special extention
public TreeNode RecursiveDirToTree(TreeNode parentNode, string path,
string extension = ".txt")
{
var result = new TreeNode(parentNode == null ? path/*base line*/ : System.IO.Path.GetFileName(path));
foreach (string dir in System.IO.Directory.GetDirectories(path))
{
TreeNode node = RecursiveDirToTree(result, dir , extension);
if (node.Nodes.Count > 0)
{
result.Nodes.Add(node);
}
}
foreach (string file in System.IO.Directory.GetFiles(path))
{
if (System.IO.Path.GetExtension(file).ToLower() == extension.ToLower())
{
result.Nodes.Add(System.IO.Path.GetFileName(file));
}
}
return result;
}
Tag is ok, because it is unused prpoerty per default and it is really a property, which does not bother with being abused . The type is object. So it takes everything. But YOU MUST TAKE CARE YOURSELF for putting something in it and retrieve it properly.
Best way to do so is to save a list inside tag, which consists of two columns , type and reference.
But for Your case just put the nodes into tag and then retrieve them with a proper cast into a local object.
By the way, you also might want to load subdirs just in demand, in order to check, whether it is file or dir.
this can optimize performance.
File.Exists and Directory.Exists
I tride it in this way,
private void btnFind_Click(object sender, EventArgs
{
for (int i = 0; i < treeView1.Nodes.Count - 1; i++)
{
MessageBox.Show(i.ToString());
treeView1.Nodes[i].BackColor = Color.Empty;
}
var result = from TreeNode node in treeView1.Nodes
where node.Text.Contains( Convert.ToString(txtFind.Text))
select node.Index;
foreach (int search in result)
{
treeView1.Nodes[search].BackColor = Color.Yellow;
}
}
But in this way I can find only parent nodes. Is there a proper way to do this
You can have a method to process the TreeView and then another to recursively call the child nodes. This will load _matchingNodes with all of the nodes that match your text.
Private List<TreeNode> _matchingNodes;
// Process the TreeView.
private void ProcessTreeView(TreeView treeView, String FindText)
{
_matchingNodes = new List<TreeNode>();
// Process each node recursively.
foreach (TreeNode n in treeView.Nodes)
{
if(n.Text.Contains(FindText))
_matchingNodes.Add(n);
ProcessRecursive(n, FindText);
}
}
private void ProcessRecursive(TreeNode treeNode, String FindText)
{
// Process each node recursively.
foreach (TreeNode n in treeNode.Nodes)
{
if(n.Text.Contains(FindText))
_matchingNodes.Add(n);
ProcessRecursive(n, FindText);
}
}
private void btnFind_Click(object sender, EventArgs e)
{
CallRecursive(treeView1);
}
private void PrintRecursive(TreeNode treeNode)
{
if (treeNode.Text.Contains(txtFind.Text.ToString()))
{
//MessageBox.Show(treeNode.Text);
treeNode.BackColor = Color.Blue;
}
else
{
treeNode.BackColor = Color.Empty;
}
// Print each node recursively.
foreach (TreeNode tn in treeNode.Nodes)
{
PrintRecursive(tn);
}
}
// Call the procedure using the TreeView.
private void CallRecursive(TreeView treeView)
{
// Print each node recursively.
TreeNodeCollection nodes = treeView.Nodes;
foreach (TreeNode n in nodes)
{
PrintRecursive(n);
}
}
I solved It Like this and it works as expected.
TreeView.nodes.find(nodeName,1)
The numeral 1 specifies to look at all child nodes too. A 0 means say to not include children. Only tested in Powershell.
Perhaps not so helpful for searching the text of the nodes but hopefully you can obtain the node name.