Make TreeNode not selectable but still functional - c#

I initialize the component in the designer code:
private void InitializeComponent(){
this.treeViewCategory.Name = "treeViewCategory";
this.treeViewCategory.Size = new System.Drawing.Size(287, 303);
this.treeViewCategory.TabIndex = 14;
this.treeViewCategory.DoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.treeViewCategory_TreeNodeMouseClickEventHandler);
}
Outside the designer, I modify it:
this.treeViewCategory.Update();
TreeNode node = this.treeViewCategory.Nodes.Add("Node 1");
node.Name = "Node 1";
node.Nodes.Add("Node 1-Child");
node = this.treeViewCategory.Nodes.Add("Node 2");
node.Name = "Node 2";
node.Nodes.Add("Node 2-Child 1");
node.Nodes.Add("Node 2-Child 2");
this.treeViewCategory.ExpandAll();
this.treeViewCategory.EndUpdate();
I want Node 1 and Node 2 to be functional but not selectable. So clicking on either Node 1 or Node 2 would expand/contract the branch, but the node itself is not highlighted.
private void treeViewCategory_TreeNodeMouseClickEventHandler(object sender, TreeNodeMouseClickEventArgs eventArgs)
{
TreeView treeView = (TreeView)sender;
TreeNode treeNode = eventArgs.Node; // parent or child
String nodeText = treeNode.Text;
// if parent node
if (nodeText.Contains("Node 1") || nodeText.Contains("Node 2")) {
// don't select the node
}
else { // child
}
}
In treeViewCategory_TreeNodeMouseClickEventHandler, I can distinguish between parent and child, but I see nothing that does what I want it do.

Add a handler for the treeview's BeforeSelect event, and cancel the selection there.
// Add unselectable nodes to this collection when you create them
private List<TreeNode> _unselectableNodes = new List<TreeNode>();
private void treeViewCategory_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
if (_unselectableNodes.Contains(e.Node))
{
e.Cancel = true;
}
}
Unfortunately, as noted in comments, this doesn't prevent selection so much as revert it when the user releases the mouse button. My preference would be for the BeforeSelect event to happen entirely before selection takes place. But there's probably a reason for it.
Handler setup, if you're not doing it via the form builder. This should go in the constructor for your Form. No need for the delegate constructor if treeViewCategory_BeforeSelect has the correct return and parameter types.
this.treeViewCategory.BeforeSelect += treeViewCategory_BeforeSelect;
Node creation:
TreeNode node = this.treeViewCategory.Nodes.Add("Node 1");
node.Name = "Node 1";
_unselectableNodes.Add(node);
node.Nodes.Add("Node 1-Child");
node = this.treeViewCategory.Nodes.Add("Node 2");
node.Name = "Node 2";
_unselectableNodes.Add(node);

Use the Tag property of the node.
The Tag can carry any kind of object.
// for unselectable
...
node0.Tag = false;
...
// for selectable
...
node1.Tag = true;
...
in the selection event you can simply:
if ((bool)node.Tag)
{ ... }

Related

showing tree node details separately in the same window

How can I show the details of a tree node, upon selection, in the same window but separately from the hierarchy tree.
So far I have successfully showed details in the treeview class using this code:
private void buttonCreateTree_Click(object sender, EventArgs e)
{
if (xd != null)
{
TreeNode rootNode = new TreeNode(xd.Root.FirstNode.ToString());
AddNode(xd.Root, rootNode);
treeView1.Nodes.Add(rootNode);
}
if (xd == null)
{
MessageBox.Show("No saved XML file!");
}
}
I've read about tags, but since I'm not very fond of Windows Forms, I don't know how to implement them correctly. What is the correct syntax for the solution?
Update: The details of a tree node are its child components with custom attributes i made like creationDate, LastAccessDate and LastModifiedDate so it needs to show the child elements of a tree node in the same window but apart from the hierarchy tree? that doesn't even make sense O.o
Not sure if that is what you want, or for that matter if you are but you can play with this:
Add a Panel panel1 to the form and hook up this event:
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
if (panel1.Controls.Count == 0)
{
panel1.Controls.Add(new TreeView());
panel1.Controls[0].Dock = DockStyle.Fill;
}
TreeView tv = panel1.Controls[0] as TreeView;
if (tv != null)
{
tv.Nodes.Clear();
// option 1 deep copy:
TreeNode tc = (TreeNode)e.Node.Clone();
tv.Nodes.Add(tc);
// option 2 shallow copy, 1 level
TreeNode tn = tv.Nodes.Add(e.Node.Text);
foreach (TreeNode cn in e.Node.Nodes)
tn.Nodes.Add(cn.Text);
}
tv.ExpandAll();
}
Do pick one of the two options and try..

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

Select TreeView Node

I've a TreeNode and I need to allow user to select only one child node for parent.
Example:
-Car
---Ferrari
---Lamborghini
---Porsche
-Shoes
---Nike
---Puma
---Adidas
I can select "Ferrari" and "Nike", but not other child in "Car" or "Shoes". How can I make it?
After I do this, I need to concat text of Parent and child like this: Car: Ferrari.
Can you help me?
Regards.
You could handle the BeforeCheck event and clear the siblings checkboxes, e.g. :
private bool skipEvents = false; // this flag to avoid infinite recursion
void treeView1_BeforeCheck(object sender, TreeViewCancelEventArgs e)
{
// if is a root (car or shoes), or it's a recursive call, just return
if (skipEvents || e.Node.Parent == null)
return;
skipEvents = true;
foreach (TreeNode sibling in e.Node.Parent.Nodes)
{
// set the other siblings to unchecked
if (sibling != e.Node)
sibling.Checked = false;
}
skipEvents = false;
}
Here's an example to concatenate the parents and childs selected:
public string GetSelectionString()
{
string categorySep = Environment.NewLine;
string parentChildSep = " : ";
StringBuilder sb = new StringBuilder();
foreach (TreeNode root in this.treeView1.Nodes)
{
foreach (TreeNode node in root.Nodes)
{
if (node.Checked)
{
if (sb.Length > 0)
sb.Append(categorySep);
sb.Append(root.Text);
sb.Append(parentChildSep);
sb.Append(node.Text);
break;
}
}
}
return sb.ToString();
}
for example if Ferrari and Puma are selected, it returns a string like this:
Car : Ferrari
Shoes : Puma
EDIT as per comment:
This code does what you ask in your comment (selection/deselection of parents children):
private bool skipEvents = false; // this flag to avoid infinite recursion
void treeView1_BeforeCheck(object sender, TreeViewCancelEventArgs e)
{
// if it's a recursive call, just return
if (skipEvents)
return;
skipEvents = true;
// it's a root (e.g. car or shoes)
if (e.Node.Parent == null)
{
// if root node is going to be checked, just cancel the action (i.e. block parent selection)
if (!e.Node.Checked)
{
e.Cancel = true;
}
}
else
{
foreach (TreeNode sibling in e.Node.Parent.Nodes)
{
// set the other siblings to unchecked
if (sibling != e.Node)
sibling.Checked = false;
}
}
skipEvents = false;
}
void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
// if it's a recursive call, just return
if (skipEvents)
return;
this.skipEvents = true;
// it's a root (e.g. car or shoes)
if (e.Node.Parent == null)
{
// root node has been unchecked, so uncheck the children
if (!e.Node.Checked)
{
foreach (TreeNode child in e.Node.Nodes)
child.Checked = false;
}
}
else
{
// if a child node has been checked --> check the parent
// otherwise, uncheck the parent
e.Node.Parent.Checked = e.Node.Checked;
}
this.skipEvents = false;
}
N.B.
TreeView class has a known bug that arises in Vista/Windows7 concerning checkboxes.
Basically if you double-click a checkbox it doesn't lauch any event, so this management will be compromised.
To solve this issue, you can disable double-click by using the class explained in this post instead of TreeView.
If you need one selection per tree, I would suggest using two TreeViews. I would also question whether or not you need to be using TreeViews or whether two ListBoxes or ComboBoxes might be more appropriate.
If you don't know how many 'trees' you'll have, but you do know how deep they are, you could use two or more ListBoxes (or ListViews) to display basically a list of lists:
Categories:
Shoes: Nike
Cars: Ferrari
Fruits: Apple (selected)
Selected Category (Fruits):
Apple (selected)
Orange
Pear
Kiwi

Tree View not showing update

I have a TreeView in my Windows Form user interface.
I want to fill it up from a database, but it does not refresh ever, even though if I WriteLine() every node, it is in memory as I expect.
In order to make it more easy to understand, I wrote a little example program that only has one button that creates a TreeView and a TreeView called treeView1 to display its content.
If anyone can tell me where I have misunderstood the use of the TreeView, it would be a tremendous help.
private void button1_Click(object sender, EventArgs e)
{
// create a tree
TreeView t = new TreeView();
TreeNode[] child = new TreeNode [1];
child[0]=new TreeNode("myCat");
child[0].Name = "IndependantOne";
TreeNode categoryNode = new TreeNode("catIdTag", child);
categoryNode.Name = "Citizen Cat 5239002147";
t.Nodes.Add(categoryNode);
// some stuff under the first node
TreeNode[] mouseNode = new TreeNode[1];
mouseNode[0] = new TreeNode("myMouse");
mouseNode[0].Name = "SqueakyOne";
TreeNode[] childItem = new TreeNode[1];
childItem[0] = new TreeNode("mouseIdTag", mouseNode);
childItem[0].Name = "Citizen Mouse 54655654649";
TreeNode eltNode = new TreeNode("Cheese", childItem);
eltNode.Name = "Emmental";
t.Nodes["Citizen Cat 5239002147"].Nodes.Add(eltNode);
// fill in the winform treeview
if (t != null)
{
//treeView1.Visible = false;
treeView1.BeginUpdate();
treeView1.Nodes.Clear();
treeView1.TopNode = t.TopNode;
foreach (TreeNode n in t.Nodes)
{
treeView1.Nodes.Add(n);
System.Diagnostics.Debug.WriteLine("Category Node contains: " + treeView1.Nodes[n.Name].Name + " at " + treeView1.Nodes[n.Name].Level);
foreach (TreeNode no in treeView1.Nodes[n.Name].Nodes)
{
System.Diagnostics.Debug.WriteLine("Category Node Nodes contains: " + no.Name);
}
}
/*
This part I tried and it doesn't work either, still add it in the question if anyone knows if it's wiser?
this.treeView1 = t;
this.treeView1.Location = new System.Drawing.Point(233, 12);
this.treeView1.Name = "treeView1";
this.treeView1.Size = new System.Drawing.Size(351, 277);
this.treeView1.TabIndex = 11;
this.treeView1.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView1_AfterSelect);
*/
treeView1.EndUpdate();
this.treeView1.Update();
this.treeView1.Refresh();
treeView1.Show();
//this.Refresh();
}
}
I also tried setting the treeView1 with
treeView1 = t;
It was not a success...
change
treeView1.Nodes.Add(n);
with
treeView1.Nodes.Add((TreeNode)n.Clone());
You cannot host the same node in more than one TreeView control.
Alternatively, you can remove nodes from source TreeView before adding them to other TreeView:
while(t.Nodes.Count != 0)
{
var node = t.Nodes[0];
t.Nodes.RemoveAt(0);
treeView1.Nodes.Add(node);
}
And I hope that there is a real reason why you create and fill tree view in your method instead of directly filling an already existing one. If it is not an intended behavior, remove the if block completely and change TreeView t = new TreeView(); to var t = treeView1.
This behavior is expected because with this code you`re confusing node-to-treeview relationship nature of the native .NET treeview control. Instead when moving nodes between treeviews (t -> treeView1) you need to clone them as suggested. Without that moved node still linked with the old treeview (see node.Treeview property) and because original tree (t) is not visible/added to any parent (form), I guess node will be invisible as well.
Also, the way you`re working with data loading (through creating new treeview) is pretty bad pattern. Instead you need to download your data (async I guess) into a temp buffer and recreate the treeView1 at once when data will be available with BeginUpdate/EndUpdate calls.
PS. Replacing treeView1 variable with 't' won't work as well because you don't replace the treeview control instance in the parent form/panel Controls property with this code.

How to select a node of treeview programmatically in c#?

Used treeview.SelectedNode to select a child node. How to invoke treeview.AfterSelect event when a node is selected programmatically?
this.treeView1.SelectedNode = this.treeView1.Nodes[0].Nodes[0].Nodes[0].Nodes[0];
if (this.treeView1.Nodes[0].Nodes[0].Nodes[0].Nodes[0].IsSelected)
{
MessageBox.Show("Node is selected");
}
Apologies for my previously mixed up answer.
Here is how to do:
myTreeView.SelectedNode = myTreeNode;
(Update)
I have tested the code below and it works:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
treeView1.Nodes.Add("1", "1");
treeView1.Nodes.Add("2", "2");
treeView1.Nodes[0].Nodes.Add("1-1", "1-1");
TreeNode treeNode = treeView1.Nodes[0].Nodes.Add("1-2", "1-3");
treeView1.SelectedNode = treeNode;
MessageBox.Show(treeNode.IsSelected.ToString());
}
}
treeViewMain.SelectedNode = treeViewMain.Nodes.Find(searchNode, true)[0];
where searchNode is the name of the node.
I'm personally using a combo "Node + Panel" where Node name is Node + and the same tag is also set on panel of choice.
With this command + scan of panels by tag i'm usually able to work a treeview+panel full menu set.
Call the TreeView.OnAfterSelect() protected method after you programatically select the node.
yourNode.Toggle(); //use that function on your node, it toggles it
TreeViewItem tempItem = new TreeViewItem();
TreeViewItem tempItem1 = new TreeViewItem();
tempItem = (TreeViewItem) treeView1.Items.GetItemAt(0); // Selecting the first of the top level nodes
tempItem1 = (TreeViewItem)tempItem.Items.GetItemAt(0); // Selecting the first child of the first first level node
SelectedCategoryHeaderString = tempItem.Header.ToString(); // gets the header for the first top level node
SelectedCategoryHeaderString = tempItem1.Header.ToString(); // gets the header for the first child node of the first top level node
tempItem.IsExpanded = true; // will expand the first node
private void btn_CollapseAllAndExpandFirstLevelUnderRoot(object sender, EventArgs e)
{
//this example collapses everything, then expands the first level under the root node.
tv_myTreeView.CollapseAll();
TreeNode tn = tv_myTreeView.Nodes[0];
tn.Expand();
}

Categories