I've added checkboxes to my treeview, and am using the AfterSelect event (also tried AfterChecked).
My tree view is like this
1 State
1.1 City1
1.2 City2
1.3 City3
2 State
2.1 City1
2.2 City2
2.3 City3
etc.
I'm trying to run an event, so when a checkbox is clicked, the tag is added to an array ready for processing later. I also need to use it so if a state is clicked, it selects all the cities under that leaf.
treeSections.AfterSelect += node_AfterCheck;
private void node_AfterCheck(object sender, TreeViewEventArgs e)
{
MessageBox.Show("testing");
}
The above code works on the treeview if it has no heirarchy. But don't work on the treeview with the states and cities unless the text/label for each leaf is double clicked.
Any ideas?
I suggest using the combination of TreeView.NodeMouseClick and TreeView.KeyUp events... the click event will provide you the clicked node via event args and with the keyup you can use the currently selected node. Follow the example below...
//This is basic - you may need to modify logically to fit your needs
void ManageTreeChecked(TreeNode node)
{
foreach(TreeNode n in node.Nodes)
{
n.Checked = node.Checked;
}
}
private void convTreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
ManageTreeChecked(e.Node);
}
private void convTreeView_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
ManageTreeChecked(convTreeView.SelectedNode);
}
}
Using the node given each event you can now cycle through the Nodes collection on that node and modify it to be checked/unchecked given the status of the checked status of the node you acted upon.
You can even get fancy enough to uncheck a parent node when all child nodes are unchecked. If you desire a 3-state treenode (All Checked, Some Checked and None Checked) then you have to create it or find one that has been created.
Enjoy, best of luck.
Some code for you to consider :
Not considered here :
what to about which node is selected when checking, when a child node selection forces a parent node to be selected (because all other child nodes are selected).
could be other cases related to selection not considered here.
Assumptions :
you are in a TreeView with a single-node selection mode
only two levels of depth, as in OP's sample ("heavy duty" recursion not required)
everything done with the mouse only : extra actions like keyboard keypress not required.
if all child nodes are checked, parent node is auto-checked
unchecking any child node will uncheck a checked parent node
checking or unchecking the parent node will set all child nodes to the same check-state
...
// the current Node in AfterSelect
private TreeNode currentNode;
// flag to prevent recursion
private bool dontRecurse;
// boolean used in testing if all child nodes are checked
private bool isChecked;
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
// prevent recursion here
if (dontRecurse) return;
// set the currentNode
currentNode = e.Node;
// for debugging
//Console.WriteLine("after check node = " + currentNode.Text);
// select or unselect the current node depending on checkstate
if (currentNode.Checked)
{
treeView1.SelectedNode = currentNode;
}
else
{
treeView1.SelectedNode = null;
}
if(currentNode.Nodes.Count > 0)
{
// node with children : make the child nodes
// checked state match the parents
foreach (TreeNode theNode in currentNode.Nodes)
{
theNode.Checked = currentNode.Checked;
}
}
else
{
// assume a child node is selected here
// i.e., we assume no root level nodes without children
if (!currentNode.Checked)
{
// the child node is unchecked : uncheck the parent node
dontRecurse = true;
currentNode.Parent.Checked = false;
dontRecurse = false;
}
else
{
// the child node is checked : check the parent node
// if all other siblings are checked
// check the parent node
dontRecurse = true;
isChecked = true;
foreach(TreeNode theNode in currentNode.Parent.Nodes)
{
if(theNode != currentNode)
{
if (!theNode.Checked) isChecked = false;
}
}
if (isChecked) currentNode.Parent.Checked = true;
dontRecurse = false;
}
}
}
Related
I found a lot about treeview and checkboxes in treeviews...
but one thing seems to be undiscussed.
I have my treeview like
[x] Foo
L [x] One
L [x] Two
L [x] Three
[x] Bar
L [ ] One
L [x] Two
L [x] Three
[ ] Hello
L [ ] One
L [ ] Two
L [ ] Three
Now when I check my parent checkbox e.g. Foo than all child get selected. Also they get deselected by unchecking Foo. To do this I have this Method
private void TreeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
foreach (TreeNode childNode in e.Node.Nodes)
{
childNode.Checked = e.Node.Checked;
}
}
I tried a lot with using count and others but could not achive what i'm trying to achive.
So what I want is, when I check Two in TreeNode Hello than Hello should get checked. Without checking One and Three. Check-/Unchecking Hello should select/deselect all childs.
Is this even possible?
Because when Hello gets checked, it fires the TreeView1_AfterCheck-event! What actually check/unchecks all childs.
And most of my tries ended up in an endless loop.
So my Question actually is:
Can I achive the above described behavior?
Ask me if something is unclear.
I made a similar application where I had to do the same thing which you are trying to achieve. Its in VB.Net. Here's the snippet. If it did help, do accept this answer, comment further if you couldn't understand any part of it.
VB.NET :
Private isChildCheck As Boolean = False
Private Sub TREE_VIEW_AfterCheck(sender As Object, e As TreeViewEventArgs)
If e.Action <> TreeViewAction.Unknown Then ' The code only executes if the user caused the checked state to change.
If e.Node.Nodes.Count > 0 Then
If isChildCheck Then
isChildCheck = False
Exit Sub 'This will check the parent to exit AfterCheck loop
End If
' Calls the CheckAllChildNodes method, passing in the current
' checked value of the TreeNode whose checked state changed.
CheckAllChildNodes(e.Node, e.Node.Checked)
Else
e.Node.Parent.Checked = True
isChildCheck = True
End If
End If
End Sub 'After a tree node's Checked property is changed, all its child nodes are updated to the same value.
Private Sub CheckAllChildNodes(treeNode As TreeNode, nodeChecked As Boolean)
For Each node As TreeNode In treeNode.Nodes
node.Checked = nodeChecked
If node.Nodes.Count > 0 Then
' If the current node has child nodes, call the CheckAllChildsNodes method recursively.
CheckAllChildNodes(node, nodeChecked)
End If
Next node
End Sub 'Checks the childnodes of a node recursively
C#.NET (Edit by Dwza after clearing behavior changes for thoese who need it)
private bool isChildCheck = false;
private void TREE_VIEW_AfterCheck(object sender, TreeViewEventArgs e)
{
// The code only executes if the user caused the checked state to change.
if (e.Action != TreeViewAction.Unknown)
{
if (e.Node.Nodes.Count > 0)
{
if (isChildCheck)
{
isChildCheck = false;
return;
//This will check the parent to exit AfterCheck loop
}
// Calls the CheckAllChildNodes method, passing in the current
// checked value of the TreeNode whose checked state changed.
CheckAllChildNodes(e.Node, e.Node.Checked);
}
else
{
e.Node.Parent.Checked = GetCheckStateOfChildNodes(e.Node.Parent);
isChildCheck = !e.Node.Parent.Checked;
}
}
}
//After a tree node's Checked property is changed, all its child nodes are updated to the same value.
private void CheckAllChildNodes(TreeNode treeNode, bool nodeChecked)
{
foreach (TreeNode node in treeNode.Nodes)
{
node.Checked = nodeChecked;
if (node.Nodes.Count > 0)
{
// If the current node has child nodes, call the CheckAllChildsNodes method recursively.
CheckAllChildNodes(node, nodeChecked);
}
}
} //Checks the childnodes of a node recursively
//Additional Method to react on behavior when all childs are checked/unchecked
//This part was added by dwza and is not in the VB part
private bool GetCheckStateOfChildNodes(TreeNode treeNode)
{
foreach(TreeNode node in treeNode.Nodes)
{
if (node.Checked)
{
return true;
}
}
return false;
}
Hope this conversion will help you.(Converted using CodeConverter)
How to find the index of every root folder in a treeview?
Let's say there's a treeview with 4 root nodes. They're all on the same level and all of them have child nodes (not displayed):
|-a
|-b
|-c
|-d
Now let's suppose there's a selected node on the branch of the "c" root node. How could I get the index of the "c" node? (In this case, it's the third one between root nodes).
So, given a selected node, how could I get the index of its root node?
To achieve your task, you should find clicked node's parent and then parent's parent etc... So, we need a recursion here.
Take a look at sample code (with comments):
private void Form1_Load(object sender, EventArgs e)
{
//add test data on form load (you can do it on form design, too.
//there are 4 root nodes and each of them has one subnode.
//Additionally, c's first node, called 'c-1', has it's own child.
treeView1.Nodes.Add(new TreeNode("a"));
treeView1.Nodes.Add(new TreeNode("b"));
treeView1.Nodes.Add(new TreeNode("c"));
treeView1.Nodes.Add(new TreeNode("d"));
treeView1.Nodes[0].Nodes.Add(new TreeNode("a-1"));
treeView1.Nodes[1].Nodes.Add(new TreeNode("b-1"));
treeView1.Nodes[2].Nodes.Add(new TreeNode("c-1"));
treeView1.Nodes[3].Nodes.Add(new TreeNode("d-1"));
treeView1.Nodes[2].Nodes[0].Nodes.Add(new TreeNode("c-1-1"));
//expand those nodes to see things clearly
treeView1.ExpandAll();
//subscribe to after select event. When user selects one node, treeView1_AfterSelect method will be called.
//this can be done on form designer too, on properties panel
treeView1.AfterSelect += treeView1_AfterSelect;
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
//this method will be called when you select node
//find topmost parent by calling method FindTopMostParent and passing it selected node
var topmostParent = FindTopMostParent(e.Node);
//here's your index of topmost node (parent or grandparent or grand-grand of selcted node(
var index = treeView1.Nodes.IndexOf(topmostParent);
}
private TreeNode FindTopMostParent(TreeNode node)
{
//first, we check if passed node has parent. If not, return that node, because it's the topmost one
//then, repeat that search for parent again and again until you find that node which does not have parent
while (node.Parent != null)
return FindTopMostParent(node.Parent);
//return parentless node :)
return node;
}
I have a TreeView and an associated ImageList. What are the steps to add images to the Parent and child nodes ?
All the nodes are being added from the code. Nothing is done from the Design.
public void fill_tree()
{
host_listbox_new.Items.Clear();
foreach (KeyValuePair<string, host_config> hlitem in host_list)
{
string sitem = hlitem.Key;
if (host_list[sitem].sessionOptions == null)
host_list[sitem].sessionOptions = new SessionOptions();
host_list[sitem].sessionOptions.Protocol = Protocol.Sftp;
host_list[sitem].sessionOptions.HostName = host_list[sitem].ip;
host_list[sitem].sessionOptions.UserName = host_list[sitem].username;
host_list[sitem].sessionOptions.Password = host_list[sitem].password;
host_list[sitem].sessionOptions.PortNumber = Convert.ToInt32(host_list[sitem].port);
//host_list[sitem].sessionOptions.SshHostKeyFingerprint = host_list[sitem].rsa;
if (treeView1.SelectedNode != null)
{
treeView1.SelectedNode.Nodes.Add(hlitem.Key.ToString());
}
else
{
treeView1.Nodes[0].Nodes.Add(hlitem.Key.ToString());
}
}
}
private void Parent_Load(object sender, EventArgs e)
{
read_process_config();
read_host_config();
host_listbox.Items.Clear();
treeView1.BeginUpdate();
treeView1.Nodes.Add("Servers");
fill_tree();
treeView1.EndUpdate();
treeView1.ExpandAll();
connect_server_bttn.Enabled = false;
}
i want to add items i.e child nodes to Server Parent node each of them having one image before them ( green image if hlitem.Value.connected is true. red image if hlitem.Value.connected is false)
But i have no idea about treeview or imagelist.
Can anyone help me about the whole thing?
The Add command returns a reference to the new Node. You can use it to style the Node.
Change your code to this:
if (treeView1.SelectedNode != null)
{
TreeNode tn =treeView1.SelectedNode.Nodes.Add(hlitem.Key.ToString());
tn.ImageIndex = yourIndex;
}
else
{
TreeNode tn =treeView1.Nodes[0].Nodes.Add(hlitem.Key.ToString());
tn.ImageIndex = yourIndex;
}
Or whatever logic you need to set the index.
If you need the parent node's index you could write:
tn.ImageIndex = tn.Parent.ImageIndex;
You may also want ot check out the other formats of the Add method. Some let you include the ImageIndex directly. You can also include the SelectedIndex; especially if you don't want that you should include it to prevent the Tree using its default SelectedIndex!
This will set the node to show the 2nd image, whether selected or not:
TreeNode tn =treeView1.Nodes[0].Nodes.Add(sitem, sitem, 1,1 );
Since you can't set a property of an object before you have created it, you can't set the Child nodes when you create the parent node. Instead you can use a simple function to do the changes:
void copyImgIndexToChildren(TreeNode tn)
{
if (tn.Nodes.Count > 0)
foreach (TreeNode cn in tn.Nodes) cn.ImageIndex = tn.ImageIndex;
}
void copyImgIndexToAllChildren(TreeNode tn)
{
if (tn.Nodes.Count > 0)
foreach (TreeNode cn in tn.Nodes)
{
cn.ImageIndex = tn.ImageIndex;
copyImgIndexToAllChildren(cn);
}
}
The first method changes the direct ChildNodes only , the 2nd recursively changes all levels below the starting node.
BTW: Is there a reason to use hlitem.Key.ToString() in your code instead of sitem?
I have a treelist in my form with checkboxes enabled. I need to add anything checked to a list so I can write that list out. If I check any parent nodes (or highest level nodes) it includes them. If I select any parent nodes it also selects its children nodes (this is intentional), and displays those. But If I check any child nodes only, it won't add them to my list.
//check to see if there are any nodes checked
bool nodeHasCheck = false;
foreach (TreeNode n in nodes)
{
if (n.Checked)
{
nodeHasCheck = true;
break;
}
GetExtendedFeatures(n.Nodes);
}
//only return stuff if something's checked
if (nodeHasCheck == true)
{
foreach (TreeNode n in nodes)
{
if (n.Checked)
{
//n.BackColor = Color.Black;
nodeList.Add(n.Text);
}
GetExtendedFeatures(n.Nodes);
}
It also appears that if I select 2 parent nodes, the recursion that occurs (Think that's the right term) is causing it to find the first checked node, then starts over, and adds that same checked node a second time before it hits the second set of nodes.
I provided my node test, hopefully it's enough to identify why it's not detecting child nodes selected without the parent node selected.
UPDATED - 3/18/13
My button click code:
private void btnGenerate_Click(object sender, EventArgs e)
{
ScanNodes(treeView1.Nodes[0]);
}
private void ScanNodes(TreeNode parent)
{
foreach (TreeNode node in parent.Nodes)
{
if (node.Checked)
{
nodeList.Add(node.Text.ToString());
}
if (node.Nodes.Count > 0)
{
ScanNodes(node);
}
}
var message = string.Join(Environment.NewLine, nodeList);
message = message.Replace(Environment.NewLine, ", ");
MessageBox.Show(message);
nodeList.Clear();
}
To get a List of all the selected nodes in a TreeView you can use the following:
Supose the list is named nodeList:
//We First declare a recursive method to loop through all nodes,
//we need to pass a root node to start
private void ScanNodes(TreeNode parent)
{
foreach (TreeNode node in parent.Nodes)
{
if (node.Checked)
{
nodeList.Add(node.Text);
}
if (node.Nodes.Count > 0)
{
ScanNodes(node);
}
}
}
With that set up You just need to call the ScanNodes method and pass the root node of your TreeView:
ScanNodes(treeView1.Nodes[0]);
Regards,
I have an application with two forms. The first form is used to create TreeView nodes programmatically, and the second form has the actual TreeView. When the application loads I create two root nodes in the TreeView.
My problem is when I create my first sub-node for either of the root nodes, it is not highlighted. I give the Form and the TreeView focus, and also disabled the HideSelection property for the TreeView.
Once I add a another sub-node to either of the root nodes is when the inserted node becomes highlighted. I want each inserted node to be highlighted once it has been inserted, but that only works after the first one has been inserted.
Example Code:
m_ObjectAnimationForm.tr_vw_ANIMATION_OBJECT_LIST.SelectedNode = m_ObjectAnimationForm.tr_vw_ANIMATION_OBJECT_LIST.Nodes["OBJECTS_ROOT"].Nodes.Add(NewObject.ID, NewObject.ID);
I create a new tree node using the ID of the object for the 'KEY' and the string of the node, then that function returns the newly created tree node, making it the selected node in the tree.
After that code I call:
m_ObjectAnimationForm.tr_vw_ANIMATION_OBJECT_LIST.ExpandAll();
m_ObjectAnimationForm.tr_vw_ANIMATION_OBJECT_LIST.Focus();
I have a slightly different setup, in which I control the highlighted item in the TreeView, by way of selecting an item in a datagridview. It is not the most elegant methodology, but it works.
In Summary:
1.) Get Index from Source TreeView, Other Control, or elsewhere
2.) Expand all Nodes in Target TreeView
3.) Iterate through Tree Nodes in Target, until Index is Reached
4.) Set TreeView.SelectedNode = "the node that was found"
5.) Set Focus on TreeView
private void selectTreeViewItem(int dataGridViewRowIndex)
{
expandAllTreeViewNodes();
setTreeViewItem(dataGridViewRowIndex);
}
private void setTreeViewItem(int dataGridViewRowIndex)
{
int iterator = 0;
TreeNode tempNode = testStepTreeView.Nodes[iterator];
//don't need to actually return the integer...
iterator = findNode(tempNode, dataGridViewRowIndex, iterator);
testStepTreeView.Focus();
nodeFound = false;
}
private void expandAllTreeViewNodes()
{
if (testStepTreeView.Nodes.Count != 0)
{
foreach (TreeNode x in testStepTreeView.Nodes)
{
expandNode(x);
}
}
}
private void expandNode(TreeNode x)
{
if (x.IsExpanded == false)
{
x.Expand();
}
if (x.Nodes.Count > 0)
{
foreach (TreeNode y in x.Nodes)
{
expandNode(y);
}
}
}
private int findNode(TreeNode tempNode, int dataGridViewRowIndex, int iterator)
{
if (iterator > dataGridViewRowIndex)
{
return iterator;
}
if (iterator == dataGridViewRowIndex)
{
testStepTreeView.SelectedNode = tempNode;
nodeFound = true;
return iterator;
}
if (tempNode.Nodes.Count != 0)
{
iterator++;
if (iterator > dataGridViewRowIndex)
{
return iterator;
}
if (nodeFound == false)
{
iterator = findNode(tempNode.Nodes[0], dataGridViewRowIndex, iterator);
}
}
if (tempNode.NextNode != null)
{
iterator++;
if (iterator > dataGridViewRowIndex)
{
return iterator;
}
if (nodeFound == false)
{
iterator = findNode(tempNode.NextNode, dataGridViewRowIndex, iterator);
}
}
return iterator;
}
try using Node.Select(), it will select the node and highlight it too.
focus will not work here.