Can't detect duplicate treeview nodes and assign it as child properly - c#

I have a method that is used as a threadsafe callback to update a treeview. It takes two string parameters. The first is the passed data and the second is the IP of the host it checked.
I am trying to check whether or not the treeview currently contains the string containing the input string and if it doesn't than it is supposed to add it to the treeview as a parent node, then add the ip string underneath as a child. Although if it does already contain that input string as a parent node than it should add only the Ip address underneath the parent that the data string matches. So it basically sorts the ips. Each parent node will have multiple ips underneath.
My issue is that my method is adding each string as it's own parent regardless if it is a duplicate, which also means that it is not adding the IP of the duplicate input underneath the parent. Can anyone take a look and see where I am going wrong?
public void UpdateScan(string input, string ip)
{
lock (outputTree)
{
outputTree.BeginUpdate();
if (!(outputTree.Nodes.ContainsKey(input)))
{
TreeNode treeNode = new TreeNode(input);
//Add our parent node
outputTree.Nodes.Add(treeNode);
//Add our child node
treeNode.Nodes.Add(ip);
}
else
{
TreeNode[] treeNode = outputTree.Nodes.Find(input, true);
//Add only child node
foreach (var node in treeNode)
{
node.Nodes.Add(ip);
}
}
outputTree.EndUpdate();
}
}

I was able to get it working. By dynamically adding a key containing the data to the parent node, so I can then use that key to find the parents to add the correct children to them.
public void UpdateScan(string input, string ip)
{
lock (outputTree)
{
outputTree.BeginUpdate();
if (! outputTree.Nodes.ContainsKey(input))
{
TreeNode treeNode = new TreeNode(input);
treeNode.Name = input;
//Add our parent node
outputTree.Nodes.Add(treeNode);
//Add our child node
treeNode.Nodes.Add(ip);
}
else
{
TreeNode[] found = outputTree.Nodes.Find(input, true);
TreeNode newChild = new TreeNode(ip);
//Add only child node
found[0].Nodes.Add(newChild);
}
outputTree.EndUpdate();
}
}

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.

Copy all Selected (Checked) TreeNodes from one treeview to another (include unchecked parents) C#

I've created a directory and file browser TreeView on the left side. I want the users to be able to browse the tree and check the files and directories that they would like to move to another treeview.
The other TreeView is a user control I found online called TreeViewColumn. I will be using that control to allow the user to add other data (categories, attributes) to the files and folders selected.
The trouble I'm running into is two-fold, I need to recursively add all children (I can figure this out) but I need to add unchecked parents to checked children (to preserve the hierarchy).
private void IterateTreeNodes(TreeNode originalNode, TreeNode rootNode)
{
//Take the node passed through and loop through all children
foreach (TreeNode childNode in originalNode.Nodes)
{
// Create a new instance of the node, will need to add it to the recursion as a root item
// AND if checked it needs to get added to the new TreeView.
TreeNode newNode = new TreeNode(childNode.Text);
newNode.Tag = childNode.Tag;
newNode.Name = childNode.Name;
newNode.Checked = childNode.Checked;
if (childNode.Checked)
{
// Now we know this is checked, but what if the parent of this item was NOT checked.
//We need to head back up the tree to find the first parent that exists in the tree and add the hierarchy.
if (tvSelectedItems.TreeView.Nodes.ContainsKey(rootNode.Name)) // Means the parent exist?
{
tvSelectedItems.TreeView.SelectedNode = rootNode;
tvSelectedItems.TreeView.SelectedNode.Nodes.Add(newNode);
}
else
{
AddParents(childNode);
// Find the parent(s) and add them to the tree with their CheckState matching the original node's state
// When all parents have been added, add the current item.
}
}
IterateTreeNodes(childNode, newNode);
}
}
private TreeNode AddParents(TreeNode node)
{
if (node.Parent != null)
{
//tvDirectory.Nodes.Find(node.Name, false);
}
return null;
}
Could anyone help with this code so that it recursively adds checked nodes (and their parent, regardless of checked state). I need to maintain directory hierarchy.
Thanks for any help!
A rather (not-so-clean and not-preferred) solution could be to clone the tree first, and then remove unchecked branches.
Else, when you are adding a node, write a recursive method to traverse through node's parent node until you hit the root. And simply optimize it by checking if childNode.parent already exists, just ignore the branch and move on. i.e. Backtrack to root node.
I got it working. I was already aware of what #Yahya was saying, I was hoping for an easier way / better approach.
The code below is certainly not optimal, I will continue improving it on my end but at this point it is looking through a treeview on the left and copying all of the checked items (and their parents - regardless of CheckedState) to a treeview on the right.
Hopefully this helps someone and thanks for answering #Yahya.
I'm open to improvements but keep in mind this is a one-time use utility.
private void cmdMoveRight_Click(object sender, EventArgs e)
{
tvSelectedItems.TreeView.Nodes.Clear();
// Traverse first level Tree Nodes
foreach (TreeNode originalNode in tvDirectory.Nodes)
{
TreeNode newNode = new TreeNode(originalNode.Text);
newNode.Name = originalNode.Name;
newNode.Tag = originalNode.Tag;
newNode.Checked = originalNode.Checked;
//Only add to the new treeview if the node is checked
if (originalNode.Checked)
{
tvSelectedItems.TreeView.Nodes.Find(originalNode.Parent.Name,true)[0].Nodes.Add(newNode);
}
//Start recursion - this will be called for each first level node - there should technically only be 1 "ROOT" node.
IterateTreeNodes(originalNode, newNode);
}
}
private void IterateTreeNodes(TreeNode originalNode, TreeNode rootNode)
{
//Take the node passed through and loop through all children
foreach (TreeNode childNode in originalNode.Nodes)
{
// Create a new instance of the node, will need to add it to the recursion as a root item
// AND if checked it needs to get added to the new TreeView.
TreeNode newNode = new TreeNode(childNode.Text);
newNode.Tag = childNode.Tag;
newNode.Name = childNode.Name;
newNode.Checked = childNode.Checked;
if (childNode.Checked)
{
// Now we know this is checked, but what if the parent of this item was NOT checked.
//We need to head back up the tree to find the first parent that exists in the tree and add the hierarchy.
TreeNode[] nodestest = tvSelectedItems.TreeView.Nodes.Find(childNode.Parent.Name, true);
if (nodestest.Length > 0)
{
tvSelectedItems.TreeView.Nodes.Find(childNode.Parent.Name,true)[0].Nodes.Add(newNode);
}
else
{
AddParents(childNode);// Find the parent(s) and add them to the tree with their CheckState matching the original node's state
}
}
//recurse
IterateTreeNodes(childNode, newNode);
}
}
private void AddParents(TreeNode node)
{
if (node.Parent != null)// Check if parent is null (would mean we're looking at the root item
{
TreeNode[] nodestest = tvSelectedItems.TreeView.Nodes.Find(node.Parent.Name, true);
if (nodestest.Length > 0)
{
TreeNode[] nodes = tvDirectory.Nodes.Find(node.Name, true);
TreeNode newNode = new TreeNode(nodes[0].Text);
newNode.Name = nodes[0].Name;
newNode.Tag = nodes[0].Tag;
newNode.Checked = nodes[0].Checked;
tvSelectedItems.TreeView.Nodes[node.Parent.Name].Nodes.Add(newNode);
}
else
{
AddParents(node.Parent);
TreeNode newNode = new TreeNode(node.Text);
newNode.Name = node.Name;
newNode.Tag = node.Tag;
newNode.Checked = node.Checked;
tvSelectedItems.TreeView.Nodes.Find(node.Parent.Name,true)[0].Nodes.Add(newNode);
}
}
else // deal with root node
{
TreeNode rootNode = new TreeNode(node.Text);
rootNode.Name = node.Name;
rootNode.Tag = node.Tag;
rootNode.Checked = node.Checked;
tvSelectedItems.TreeView.Nodes.Add(rootNode);
}
}

Filter a Treeview with a Textbox in a C# winforms app

I have a TreeView in my a C# winform. I would like to be able to add a search functionality through a search box.
Basically as the user types in letters (I'm guessing on the _TextChanged event), I show only the nodes that contain childnodes with the inputed letters...
My TreeView contains 53 parent nodes for a total of over 15000 Nodes so I need something a bit performant. I build my TreeView from a csv that I load into a DataTable and then make queries on to get the Parent nodes with associated child nodes...
UPDATE
I have an idea.
The final aim is that when a user doubleclicks on a child node it gets added to a listView.
I had first implemented this search function in a simple list view where I didn't separate my data into categories.
My idea is that once the user starts typing in things, I turn off my Tree view and show the list view instead...
I'll try and implement and see what it gives performance wise... Any critics on this idea are welcome.
Finally this is what I did, it suits my requirements.
I first make a copy of my TreeView and store into fieldsTreeCache. I then clear the fieldsTree. I then search through the cache and add any node containing my search parameter to the fieldsTree. Note here that once you search, you no longer have the parent nodes that show. You just get all of the end nodes. I did this because if not I had 2 choices:
Expand all the parent nodes containing childs that match but then it was slow and one parent might have 50 children which isn't great visually.
Not expand the parent nodes but then you just get the categories and not the children nodes that you're searching for.
void fieldFilterTxtBx_TextChanged(object sender, EventArgs e)
{
//blocks repainting tree till all objects loaded
this.fieldsTree.BeginUpdate();
this.fieldsTree.Nodes.Clear();
if (this.fieldFilterTxtBx.Text != string.Empty)
{
foreach (TreeNode _parentNode in _fieldsTreeCache.Nodes)
{
foreach (TreeNode _childNode in _parentNode.Nodes)
{
if (_childNode.Text.StartsWith(this.fieldFilterTxtBx.Text))
{
this.fieldsTree.Nodes.Add((TreeNode)_childNode.Clone());
}
}
}
}
else
{
foreach (TreeNode _node in this._fieldsTreeCache.Nodes)
{
fieldsTree.Nodes.Add((TreeNode)_node.Clone());
}
}
//enables redrawing tree after all objects have been added
this.fieldsTree.EndUpdate();
}
Here's a small simple example (with code from msdn) is that a very simple way to filter out the TreeView node displays.
winforms in a tree view you can only add or remove TreeNode.
The search for the nodes can still be improved if the nodes are stored with their data into a dictionary (with a unique key).
using System.Collections;
using System.Windows.Forms;
namespace FilterWinFormsTreeview
{
// The basic Customer class.
public class Customer : System.Object
{
private string custName = "";
protected ArrayList custOrders = new ArrayList();
public Customer(string customername) {
this.custName = customername;
}
public string CustomerName {
get { return this.custName; }
set { this.custName = value; }
}
public ArrayList CustomerOrders {
get { return this.custOrders; }
}
}
// End Customer class
// The basic customer Order class.
public class Order : System.Object
{
private string ordID = "";
public Order(string orderid) {
this.ordID = orderid;
}
public string OrderID {
get { return this.ordID; }
set { this.ordID = value; }
}
}
// End Order class
public static class TreeViewHelper
{
// Create a new ArrayList to hold the Customer objects.
private static ArrayList customerArray = new ArrayList();
public static void FilterTreeView(TreeView treeView1, string orderText) {
if (string.IsNullOrEmpty(orderText)) {
FillMyTreeView(treeView1);
} else {
// Display a wait cursor while the TreeNodes are being created.
Cursor.Current = Cursors.WaitCursor;
// Suppress repainting the TreeView until all the objects have been created.
treeView1.BeginUpdate();
foreach (TreeNode customerNode in treeView1.Nodes) {
var customer = customerNode.Tag as Customer;
if (customer != null) {
customerNode.Nodes.Clear();
// Add a child treenode for each Order object in the current Customer object.
foreach (Order order in customer.CustomerOrders) {
if (order.OrderID.Contains(orderText)) {
var orderNode = new TreeNode(customer.CustomerName + "." + order.OrderID);
customerNode.Nodes.Add(orderNode);
}
}
}
}
// Reset the cursor to the default for all controls.
Cursor.Current = Cursors.Default;
// Begin repainting the TreeView.
treeView1.EndUpdate();
}
}
public static void FillMyTreeView(TreeView treeView1) {
// Add customers to the ArrayList of Customer objects.
if (customerArray.Count <= 0) {
for (int x = 0; x < 1000; x++) {
customerArray.Add(new Customer("Customer" + x.ToString()));
}
// Add orders to each Customer object in the ArrayList.
foreach (Customer customer1 in customerArray) {
for (int y = 0; y < 15; y++) {
customer1.CustomerOrders.Add(new Order("Order" + y.ToString()));
}
}
}
// Display a wait cursor while the TreeNodes are being created.
Cursor.Current = Cursors.WaitCursor;
// Suppress repainting the TreeView until all the objects have been created.
treeView1.BeginUpdate();
// Clear the TreeView each time the method is called.
treeView1.Nodes.Clear();
// Add a root TreeNode for each Customer object in the ArrayList.
foreach (Customer customer2 in customerArray) {
var customerNode = new TreeNode(customer2.CustomerName);
customerNode.Tag = customer2;
treeView1.Nodes.Add(customerNode);
// Add a child treenode for each Order object in the current Customer object.
foreach (Order order1 in customer2.CustomerOrders) {
var orderNode = new TreeNode(customer2.CustomerName + "." + order1.OrderID);
customerNode.Nodes.Add(orderNode);
}
}
// Reset the cursor to the default for all controls.
Cursor.Current = Cursors.Default;
// Begin repainting the TreeView.
treeView1.EndUpdate();
}
}
}
Every node in TreeView has Expanded and IsVisible properties. The number of items which are visible at the same time is limited (TreeView.VisibleCount). Based on this information you can reduce the number of nodes to probe dramatically.
When scanning the node and it's child nodes you can abort recursion as you find the first match inside collapsed node, thus you already know it has at least one child and will be visible anyway.
Perform filtering asynchronously. (use new Task() for instance) Start the first task after minimal number of chars was typed (let's say 3). Every next typed char must cancel the running task and start the new one.

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 )

Umbraco: List Child Nodes in User Control

I have a user control in which I need to return child nodes based on parentID. I am able to get the parentID, but don't know the syntax for returning child nodes.
Getting child nodes is pretty straightforward.
Not sure how far you are with your code so here's a complete example with the various options:
using umbraco.presentation.nodeFactory;
namespace cogworks.usercontrols
{
public partial class ExampleUserControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
//If you just want the children of the current node use the following method
var currentNode = Node.GetCurrent();
//If you need a specific node based on ID use this method (where 123 = the desired node id)
var specificNode = new Node(123);
//To get the children as a Nodes collection use this method
var childNodes = specificNode.Children;
//Iterating over nodes collection example
foreach(var node in childNodes)
{
Response.Write(string.Format("{0}<br />", node.Name));
}
//To get the nodes as a datatable so you can use it for DataBinding use this method
var childNodesAsDataTable = node.ChildrenAsTable();
//Databind example
GridViewOnPage.DataSource = childNodesAsDataTable;
GridViewOnPage.DataBind();
}
}
}

Categories