How to add editable first child node in winforms - c#

I have a treeview which represents filesystem, I have a New folder button, It works fine if the selected node has some child nodes, however when selected node does not have any child nodes it is not able to add a editable child node. I am using the following code:
tree.LabelEdit = true;
TreeNode node = new TreeNode("New Folder");
if(tree.SelectedNode.Nodes.Count>0)
tree.SelectedNode.Expand();
tree.SelectedNode.Nodes.Add(node);
if(tree.SelectedNode.Nodes.Count>0)
tree.SelectedNode.Expand();
node.BeginEdit();
Any ideas on whats going wrong?

Just change two lines in your code like that:
public Form1()
{
InitializeComponent();
tree.SelectedNode = tree.Nodes.Add("Hello", "Hello");
}
private void button1_Click(object sender, EventArgs e)
{
tree.LabelEdit = true;
TreeNode node = new TreeNode("New Folder");
tree.SelectedNode.Nodes.Add(node);
tree.SelectedNode.Expand();
node.BeginEdit();
}
Explanation: You cannot expand a node which hasn't child nodes. So you have to add the node before you expand the parent node.

Related

How to find the index of a root folder in a treeview?

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;
}

Trying to set all nodes checked status in a treeview - get StackOverflowException

I have some nodes added to my TreeView (trvP). The root element has Tag property set to Root.
I'm trying to make it so if I check the root element, all other nodes will have the same status. However, executing the code below results in StackOverflowException.
private void trvP_AfterCheck(object sender, TreeViewEventArgs e)
{
if(e.Node.Tag.Equals("Root"))
{
var nodes = TreeViewExtensions.GetAllNodes(e.Node.TreeView);
foreach (TreeNode node in nodes)
node.Checked = e.Node.Checked;
}
}
The code for GetAllNodes function:
public static List<TreeNode> GetAllNodes(this TreeView _trv)
{
List<TreeNode> result = new List<TreeNode>();
foreach (TreeNode child in _trv.Nodes)
{
result.AddRange(child.GetAllNodes());
}
return result;
}
public static List<TreeNode> GetAllNodes(this TreeNode _trn)
{
List<TreeNode> result = new List<TreeNode>();
result.Add(_trn);
foreach (TreeNode child in _trn.Nodes)
{
result.AddRange(child.GetAllNodes());
}
return result;
}
It seems from debugging that it runs the var nodes = ... piece of code over and over, while only setting single node inside the foreach loop (the root node). However, nodes is a proper list of nodes with right values.
I can't see why this is executing over and over resulting in an exception.
Your GetAllNodes() extension for a TreeView starts with the first child node and calls the GetAllNodes() extension for this TreeNode.
This GetAllNodes() extension adds this very TreeNode to the result list.
So the first TreeNode in your list is your root node again.
This means in the line
node.Checked = e.Node.Checked;
you set the Checked property of your root node, which in turn calls the handler trvP_AfterCheck again for this root node. This now repeats infinitly, flooding your stack and raising the StackOverflowException.
To solve this, filter out the root node:
private void trvP_AfterCheck(object sender, TreeViewEventArgs e)
{
if(e.Node.Tag.Equals("Root"))
{
var nodes = e.Node.TreeView.GetAllNodes();
foreach (TreeNode node in nodes)
{
if (node == e.Node) continue; // don't do it for root again
node.Checked = e.Node.Checked;
}
}
}
Btw: the nice thing about extension methods is that you can call them syntactically as if they were instanc methods. So this
var nodes = TreeViewExtensions.GetAllNodes(e.Node.TreeView)
can simply be written as
var nodes = e.Node.TreeView.GetAllNodes();
you are calling GetAllNodes recursively which makes loop into infinite state..
try
public static List<TreeNode> GetAllNodes(this TreeView _trv)
{
List<TreeNode> result = new List<TreeNode>();
foreach (TreeNode child in _trv.Nodes)
{
result.Add(child);
}
return result;
}
when you are already looping to treenode into _trv.Nodes, you jst need to add current node into foreach statement..

TreeView added to object not showing up in UI

I am trying to create a dialog where the user selects a node in a TreeView so that he can save some data to that node.
The tree itself is created in a separate control (that is used for browsing/opening content of nodes) and my idea is that i should use that tree in my save control.
My constructor looks like this:
public FrmSaveToRepository(TreeView tree)
{
InitializeComponent();
this.treeView = tree;
}
but when the form holding the control pops up the treeView is empty. Did i miss a step?
I open my form with this code:
var frm = new FrmSaveToRepository(tree);
frm.Show();
Right so the correct way to add the old nodes to the new tree is:
public FrmSaveToRepository(TreeView tree)
{
InitializeComponent();
foreach (TreeNode node in tree.Nodes)
{
this.treeView1.Nodes.Add(node.Clone() as TreeNode);
}
}
but that still means that all the events and handlers have to be implemented separately in the new tree.

how to assign image for Parent node and child nodes in treeview from assigned imagelist in c#?

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?

TreeView Control. Checkboxes. And clicking

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;
}
}
}

Categories

Resources