Displaying only searched treenode - c#

I have treeview in windowsform application when searching for node is performed I need to hide all the remaining node and I need to show only the searched node and its parent .like
grandParent
Parent1
child1
child2
child3.
parent2
child4
child5
if the searched node is child 3 i need to show the out put as..
grandParent
Parent1
child3
all auother are to be Hide.

Unfortunately (as far as I know) if you are using a WinForms TreeView control then hiding nodes is not as simple as setting the IsVisible property (due to the fact that the property is read only).
The only way of hiding nodes is to remove them from the Nodes collection.
This means displaying them again would require you to keep track of their location within the tree hierarchy to be able to restore them.
The following code seems to do what you require:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ClassLibrary;
using System.Xml;
using System.Diagnostics;
using System.IO;
using System.Xml.Linq;
namespace WindowsFormsApplication
{
public partial class HideRestoreNodesForm : Form
{
private List<RemovedTreeNode> _removedNodes = new List<RemovedTreeNode>();
public HideRestoreNodesForm()
{
InitializeComponent();
//AddNodesToTree();
}
private void searchButton_Click(object sender, EventArgs e)
{
TreeNode[] foundNodes = treeView1.Nodes.Find("NameOfNodeToFind", true);
if(foundNodes.Length > 0)
{
TreeNode foundNode = foundNodes[0];
HideNodes(treeView1.Nodes, foundNode);
}
}
private void HideNodes(TreeNodeCollection nodes, TreeNode visibleNode)
{
List<TreeNode> nodesToRemove = new List<TreeNode>();
foreach (TreeNode node in nodes)
{
if (!AreNodesRelated(node, visibleNode))
{
_removedNodes.Add(new RemovedTreeNode() { RemovedNode = node, ParentNode = node.Parent, RemovedNodeIndex = node.Index });
nodesToRemove.Add(node);
}
else
{
HideNodes(node.Nodes, visibleNode);
}
}
foreach (TreeNode node in nodesToRemove)
node.Remove();
}
private bool AreNodesRelated(TreeNode firstNode, TreeNode secondNode)
{
if (!IsNodeAncestor(firstNode, secondNode) && !IsNodeAncestor(secondNode, firstNode) && firstNode != secondNode)
{
return false;
}
else
{
return true;
}
}
private bool IsNodeAncestor(TreeNode nodeToCheck, TreeNode descendantNode)
{
TreeNode parentNode = descendantNode.Parent;
while (parentNode != null)
{
if (parentNode == nodeToCheck)
{
return true;
}
else
{
parentNode = parentNode.Parent;
}
}
return false;
}
private void restoreNodes_Click(object sender, EventArgs e)
{
RestoreNodes();
}
private void RestoreNodes()
{
_removedNodes.Reverse();
foreach (RemovedTreeNode removedNode in _removedNodes)
{
if (removedNode.ParentNode == null)
treeView1.Nodes.Add(removedNode.RemovedNode);
else
removedNode.ParentNode.Nodes.Insert(removedNode.RemovedNodeIndex ,removedNode.RemovedNode);
}
_removedNodes.Clear();
}
}
public class RemovedTreeNode
{
public TreeNode RemovedNode { get; set; }
public int RemovedNodeIndex { get; set; }
public TreeNode ParentNode { get; set; }
}
}
Hope this helps you.
I notice you are a new user, If this or any other questions you ask on the site provide the answers you are looking for remember to accept the answers.
See the following for more information: How does accepting an answer work?

Related

Converting tree view to byte[], then back again

I'm trying to convert a treeview to a byte array and then back again. So far when the form loads, it will load the structure of my documents. Then as far as I know, it will convert it to a byte array and back but I'm not sure how to convert the byte array back to the tree view.
Here is my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string filepath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
ListDirectory(treeView1, filepath);
}
private static void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var stack = new Stack<TreeNode>();
var rootDirectory = new DirectoryInfo(path);
var node = new TreeNode(rootDirectory.Name) { Tag = rootDirectory };
stack.Push(node);
while (stack.Count > 0)
{
var currentNode = stack.Pop();
var directoryInfo = (DirectoryInfo)currentNode.Tag;
foreach (var directory in directoryInfo.GetDirectories())
{
var childDirectoryNode = new TreeNode(directory.Name) { Tag = directory };
currentNode.Nodes.Add(childDirectoryNode);
stack.Push(childDirectoryNode);
}
foreach (var file in directoryInfo.GetFiles())
currentNode.Nodes.Add(new TreeNode(file.Name));
}
treeView.Nodes.Add(node);
}
private Byte[] SerilizeQueryFilters()
{
BinaryFormatter bf = new BinaryFormatter();
List<TreeNode> list = new List<TreeNode>();
foreach(TreeNode node in treeView1.Nodes)
{
list.Add(node);
}
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, list);
return ms.GetBuffer();
}
}
private void DeSerilizeQueryFilters(byte[] items)
{
BinaryFormatter bf = new BinaryFormatter();
try
{
using (MemoryStream ms = new MemoryStream())
{
ms.Write(items, 0, items.Length);
ms.Position = 0;
_list = bf.Deserialize(ms) as List<TreeNode>;
treeView2.Nodes.AddRange(_list.ToArray());
}
}
catch(Exception e)
{
Console.WriteLine(e.StackTrace);
}
}
private void button1_Click(object sender, EventArgs e)
{
byte[] data = SerilizeQueryFilters();
DeSerilizeQueryFilters(data);
}
}
So the bit that's throwing an error at the moment is
_list = bf.Deserialize(ms) as List<TreeNode>;
and I get this error:
System.Runtime.Serialization.SerializationException
Does anyone have any ideas?
The solution was easy using the recommendation of #Fildor I stop using BinaryFormatter and now I use JSON and using the recommendation of #madreflection
I create a custom class to save the data of the TreeNode.
Custom TreeNode Class
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TreeViewStuff
{
public class Node
{
public int id;
public string text = "";
public int parentId = 0;
public bool cheked = false;
}
}
Deserialize/Serialize TreeView Class
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TreeViewStuff
{
public class TreeviewPersist
{
public static string strJson;
private static TreeView treeView_;
static public string ToJson(TreeView treeView)
{
List<Node> nodes = new List<Node>();
foreach(TreeNode node in treeView.Nodes)
{
SerializeTree(nodes, node);
}
nodes.RemoveAt(0);
return JsonConvert.SerializeObject(nodes);
}
public delegate void FunctionDelegate();
public static void FromJson(string strJson_, TreeView treeView)
{
strJson = strJson_;
treeView_ = treeView;
treeView.BeginInvoke(new FunctionDelegate(FromJson));
}
private static void FromJson()
{
treeView_.Nodes.Clear();
List<Node> nodes = JsonConvert.DeserializeObject<List<Node>>(strJson);
foreach(Node node in nodes)
{
if (node.parentId == 0)
{
TreeNode treeNode = treeView_.Nodes.Add(node.text);
treeNode.Name = $"{node.id}";
treeNode.Checked = node.cheked;
}
else
{
TreeNode[] foundNodes = treeView_.Nodes.Find($"{node.parentId}", true);
if (foundNodes.Length > 0)
{
TreeNode treeNode = foundNodes[0].Nodes.Add(node.text);
treeNode.Checked = node.cheked;
treeNode.Name = $"{node.id}";
}
}
}
}
static private void SerializeTree(List<Node> nodes, TreeNode treeNode)
{
Node node = new Node();
bool suces = Int32.TryParse(treeNode.Name, out node.id);
TreeNode parent = treeNode.Parent;
if (parent != null)
{
suces = Int32.TryParse(parent.Name, out node.parentId);
}
else
{
node.parentId = 0;
}
node.text = treeNode.Text;
node.cheked = treeNode.Checked;
nodes.Add(node);
foreach (TreeNode tn in treeNode.Nodes)
{
SerializeTree(nodes, tn);
}
}
}
}
Directories/Files to TreeView Method
private static void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
int id = 1;
var stack = new Stack<TreeNode>();
var rootDirectory = new DirectoryInfo(path);
var node = new TreeNode(rootDirectory.Name) { Tag = rootDirectory };
stack.Push(node);
while (stack.Count > 0)
{
var currentNode = stack.Pop();
var directoryInfo = (DirectoryInfo)currentNode.Tag;
foreach (var directory in directoryInfo.GetDirectories())
{
var childDirectoryNode = new TreeNode(directory.Name) { Tag = directory };
childDirectoryNode.Name = $"{id++}";
currentNode.Nodes.Add(childDirectoryNode);
stack.Push(childDirectoryNode);
}
foreach (var file in directoryInfo.GetFiles())
{
TreeNode treeNode = new TreeNode(file.Name);
treeNode.Name = $"{id++}";
currentNode.Nodes.Add(new TreeNode(file.Name));
}
}
treeView.Nodes.Add(node);
}
To make this work each TreeNode needs to have a Unique Id that is going to be saved in the name of the TreeNode as I do in the Directories/Files to TreeView Method. Thanks #Fildor and #madreflection for your help!

Beginner: TreeView with checkboxes and recursion in Winforms

first post and a new coder so bare with me if you need more info than I am giving. I am trying to create a treeview with checkboxes in a hierarchy (see pic). My issue is i want to create some sort of recursion which deselects and selects child nodes when parent nodes are checked or vice versa.
I am using VS with winforms and been googling for 2 days on how to do this, unfortunately the examples online are either too advanced for me or dont work. I found a Tutorial on how to do this exactly with indeterminate checkboxes as well which would be a big bonus but it is for WPF.
I managed to create buttons which are able to (un)check all buttons with some examples online. Please can someone guide, a beginner who is finding programming AMAZING so far, in the right direction :)
private void button_checkAllNodes_Click(object sender, EventArgs e)
{
checkAllNodes(treeView1.Nodes);
}
private void button_uncheckAllNodes_Click(object sender, EventArgs e)
{
UncheckAllNodes(treeView1.Nodes);
}
public void checkAllNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
node.Checked = true;
checkChildren(node, true);
}
}
public void UncheckAllNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
node.Checked = false;
checkChildren(node, false);
}
}
private void checkChildren(TreeNode rootNode, bool isChecked)
{
foreach (TreeNode node in rootNode.Nodes)
{
checkChildren(node, isChecked);
node.Checked = isChecked;
}
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
}
Picture Treeview with (un)check All buttons
Let's create a couple of extension methods for the TreeNode type, one that gets all the children of a node, and another that gets it's parents.
// Within your project's namespace...
static class TreeViewExtensions
{
public static IEnumerable<TreeNode> Children(this TreeNode node)
{
foreach (TreeNode n in node.Nodes)
{
yield return n;
foreach (TreeNode child in Children(n))
yield return child;
}
}
public static IEnumerable<TreeNode> Parents(this TreeNode node)
{
var p = node.Parent;
while (p != null)
{
yield return p;
p = p.Parent;
}
}
}
Now, all what you need to do is to handle the TreeView.AfterCheck event to toggle the Checked property of the nodes that the extension methods yield.
// +
using System.Linq;
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Action == TreeViewAction.Unknown) return;
foreach (TreeNode n in e.Node.Children())
n.Checked = e.Node.Checked;
// Comment this if you don't need it.
foreach (TreeNode p in e.Node.Parents())
p.Checked = p.Nodes.OfType<TreeNode>().Any(n => n.Checked);
}
Soon, you'll notice sometimes this solution won't work as it should when you click rapidly over the check boxes since they don't receive the mouse double click messages by default. Then, follow this post or this to solve this problem. For now click slowly.
If you prefer though to use buttons to toggle the check state, then delete the AfterCheck handler and do instead:
private void btnCheckAll_Click(object sender, EventArgs e)
{
ToggleCheck(treeView1.SelectedNode, true);
}
private void btnUncheckAll_Click(object sender, EventArgs e)
{
ToggleCheck(treeView1.SelectedNode, false);
}
private void ToggleCheck(TreeNode node, bool state)
{
node.Checked = state;
foreach (TreeNode n in node.Children())
n.Checked = state;
// Optional...
foreach (TreeNode n in node.Parents())
n.Checked = state;
}
I agree with #jdweng , you are using recursion in checkChildren(). The base case is missing.
In recursion checkChildren , add base case before
foreach (TreeNode node in rootNode.Nodes)
if node is Null : rootNode=isChecked

C# TreeView becomes missmatched between data and display

I have a TreeView with checkboxes like
I have added some code to show what is selected:
/// <summary>
/// Highlight checked nodes
/// </summary>
private void HighlightCheckedNodes()
{
foreach (TreeNode topNode in treeView1.Nodes)
{
if (topNode.Checked)
{
topNode.BackColor = Color.Yellow;
}
else
{
topNode.BackColor = Color.White;
}
foreach (TreeNode myNode in topNode.Nodes)
{
// Check whether the tree node is checked.
if (myNode.Checked)
{
// Set the node's backColor.
myNode.BackColor = Color.Yellow;
}
else
{
myNode.BackColor = Color.White;
}
}
}
}
This function is called from treeView1_AfterSelect and treeView1_AfterCheck.
If I click a few times I get some nodes that are marked as checked but not yellow (the data says it's not checked) or vice versa.
So how do I make sure the data and display are in sync?
Long story short, the TreeView is buggy!
Here's an implementation that uses NativeWindow instead of deriving from TreeView. This will allow you to use the existing TreeView on your Form:
public partial class Form1 : Form
{
private TreeViewSuppressDoubleClick treeViewHelper;
private void Form1_Load(object sender, EventArgs e)
{
treeViewHelper = new TreeViewSuppressDoubleClick(this.treeView1);
}
public class TreeViewSuppressDoubleClick : NativeWindow
{
public TreeViewSuppressDoubleClick(TreeView treeView)
{
if (treeView != null && treeView.IsHandleCreated)
{
this.AssignHandle(treeView.Handle);
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg != 0x203)
base.WndProc(ref m);
}
}
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
e.Node.BackColor = e.Node.Checked ? Color.Yellow : Color.White;
}
}
After this you'll notice that the TreeView isn't as responsive to clicks, but it does stay in sync now.
Note the simplified method for changing the BackColor on a check/uncheck.
Also, here's an alternate HighlightCheckedNodes() using recursion to get all the nodes:
private void HighlightCheckedNodes(TreeNode node)
{
if (node == null)
{
foreach (TreeNode topNode in treeView1.Nodes)
{
HighlightCheckedNodes(topNode);
}
}
else
{
node.BackColor = node.Checked ? Color.Yellow : Color.White;
foreach (TreeNode curNode in node.Nodes)
{
HighlightCheckedNodes(curNode);
}
}
}
You'd call it using HighlightCheckedNodes(null);.

event serialization

I found a working code serialization of controls, but it has not one function: there is a controls, the controls has an event, after saving it does not saves. How can I solve this problem?
Following is the code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
namespace serial
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Save control
int i = 0;
foreach (XElement element in
panel1.Controls
.OfType<Control>()
.Select(ToXml))
{
element.Save("Control" + i++ + ".xml");
}
}
private static XElement ToXml(Control control)
{
Type controlType = control.GetType();
var root = new XElement("Root",
new XAttribute("Type", controlType.AssemblyQualifiedName));
PropertyInfo[] fieldInfos = controlType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo fieldInfo in fieldInfos)
{
if (fieldInfo.CanRead && fieldInfo.CanWrite &&
fieldInfo.Name != "Font" && fieldInfo.Name != "Handle")
{
object content = fieldInfo.GetValue(control, null);
if (content != null && content.GetType().IsSerializable)
{
var serializer = new DataContractSerializer(content.GetType());
var str = new StringBuilder();
using (XmlWriter stream = XmlWriter.Create(str))
{
serializer.WriteObject(stream, content);
}
XElement data = XElement.Parse(str.ToString());
var element = new XElement("Property",
new XAttribute("Name", fieldInfo.Name),
new XAttribute("Type", fieldInfo.PropertyType.AssemblyQualifiedName)
, data);
root.Add(element);
}
}
}
return root;
}
private void button2_Click(object sender, EventArgs e)
{
panel1.Controls.Clear();
}
private void button3_Click(object sender, EventArgs e)
{
// Clear panel
panel1.Controls.Clear();
// Load control
IEnumerable<string> newControlsNames = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.xml");
Control[] newControls = newControlsNames
.Select(XElement.Load)
.Select(GetControl)
.Select(c => c as Control)
.ToArray();
// Add control on panel
panel1.Controls.AddRange(newControls);
}
// get control from xml
private static object GetControl(XElement xml)
{
Type controlType = Type.GetType(xml.Attribute("Type").Value);
object control = Activator.CreateInstance(controlType);
IEnumerable<XElement> elements = xml.Elements("Property");
foreach (XElement element in elements)
{
string name = element.Attribute("Name").Value;
Type type = Type.GetType(element.Attribute("Type").Value);
XNode first = element.Nodes().First();
var serializer = new DataContractSerializer(type);
object value;
using (var stream = new MemoryStream(Encoding.Default.GetBytes(first.ToString())))
{
value = serializer.ReadObject(stream);
}
if (value != null)
{
PropertyInfo fieldInfo = controlType.GetProperty(name);
fieldInfo.SetValue(control, value, null);
}
}
return control;
}
private void button4_Click(object sender, EventArgs e)
{
MessageBox.Show("1");
}
private void button5_MouseEnter(object sender, EventArgs e)
{
MessageBox.Show("2");
}
}
}
edit:
Here's my project! http://www.fileserve.com/file/t7kUwWM
The problem with evets is, the list of delegates are not accesible from "outside" the class.
There is workaround - must use reflection and search for private fields typed as delegate (I mean custom delegate not only System.Delegate) and than serialize references to target object and target method (via System.Reflection.MethodInfo).
However this solution is very proprietary and does not guarantee the correct behaviour in all cases, because depends on private state of object.

c# object loses reference when removed from list

I have a very strange issue. Basically I have created a class called TreeNode that represents a node in a tree. I then create the tree by adding all the nodes to a List.
class TreeNode
{
private TreeNode parent, lChild, rChild;
private int key, val;
public int Key
{
get { return key; }
set { key = value; }
}
public int Val
{
get { return val; }
set { val = value; }
}
public TreeNode Parent
{
get { return parent; }
set { parent = value; }
}
public TreeNode LChild
{
get { return lChild; }
}
public TreeNode RChild
{
get { return rChild; }
}
public TreeNode(int k, int v)
{
key = k;
val = v;
}
public void SetChild(TreeNode leftChild, TreeNode rightChild)
{
this.lChild = leftChild;
this.rChild = rightChild;
}
public bool isLeaf()
{
if (this.lChild == null && this.rChild == null)
{
return true;
} else
{
return false;
}
}
public bool isParent()
{
if (this.parent == null)
{
return true;
}
else
{
return false;
}
}
public void SetParent(TreeNode Parent)
{
this.parent = Parent;
}
}
So if i put a breakpoint just after the creation of the tree and hover over the list in Visual Studio I can see the structure of the tree - with all the references working perfectly from the root down to the leaves.
If however I do the following:
TreeNode test = newTree[newTree.Count - 1];
please note:
private List<TreeNode> newTree = new List<TreeNode>();
which returns the root node - and hover over again I can do down one level (i.e. left child or right child) but these children do not have any references for their children after that.
I'm wondering if I'm losing the reference in memory to the other nodes in the list as the test node is not part of the list?
Any help would be greatly appreciated.
Thanks
Tom
you sure you don't have (note no space between new and tree in your code)
TreeNode test = new Tree[newTree.Count - 1];
Which would create a new empty array of tree (probably not what you intended), and leave your original tree un-rooted and inaccessible.
Can you make sure your code is correct, please?
Seems as though I found the issue - I wasn't correctly updating some of the parent nodes with their relevant child nodes - problem solved.
Thanks for your help
Tom

Categories

Resources