Populating TreeView From Object - c#

I have a fairly simple class structure where there are Graphic objects, each contains a List<Symbol> and each Symbol contains a List<Alias>: amgonst their other properties.
The obvious way (and my current method) is to use nested foreach loops to generate the nodes and to populate the tree view (this actually works fine) like below:
public void ToTree(TreeView treeControl)
{
treeControl.Nodes.Clear();
List<TreeNode> graphicsNodes = new List<TreeNode>();
foreach (Graphic graphic in Graphics)
{
List<TreeNode> symbolNodes = new List<TreeNode>();
foreach (Symbol symbol in graphic.Symbols)
{
List<TreeNode> aliasNodes = new List<TreeNode>();
foreach (Alias alias in symbol.Aliases)
{
aliasNodes.Add(new TreeNode(alias.AliasName + ": " + alias.AliasValue));
}
symbolNodes.Add(new TreeNode(symbol.SymbolName, aliasNodes.ToArray()));
}
graphicsNodes.Add(new TreeNode(graphic.FileName, symbolNodes.ToArray()));
}
treeControl.Nodes.AddRange(graphicsNodes.ToArray());
}
However, I'm curious if there is anything that I can implement in my class, or any methods that I can overload so that I can simply do something similar to treeView.Nodes.Add(graphic).
Ideally, this would allow for me to determine which object is being clicked on with the NodeClickEvent rather than me having to take the node's text and then perform a search separately.
This is so that I would have direct access to the fields and members of each object from within that node, making it much easier to modify properties from the TreeView click events.

What you can do is create subclasses of TreeNode that accept your classes in their constructor. You can then have each subclass implement the specific handling for that type. I looked into the suggestion from Hans to do something useful with the TreeNodeCollection but this class has no public constructor so I couldn't figure out how that should work.
A simple implementation looks like this for your three classes I distilled from your example:
Graphic and GraphicNode
public class GraphicNode:TreeNode
{
// takes a Graphic instance
public GraphicNode(Graphic grp)
{
// how do you want to represent it
this.Text = grp.FileName;
// and this class 'knows' how to handle its children
Nodes.AddRange(grp.Symbols.Select(s => new SymbolNode(s)).ToArray());
}
}
Symbol and SymbolNode
public class SymbolNode:TreeNode
{
public SymbolNode(Symbol sym)
{
this.Text = sym.SymbolName;
Nodes.AddRange(sym.Aliases.Select(ali => new AliasesNode(ali)).ToArray());
}
}
Alias and AliasNode
Notice how this class implements a public Click method. You can leverage that from an NodeMouseClick event.
public class AliasesNode:TreeNode
{
public AliasesNode(Aliases al)
{
this.Text = String.Format("{0} - {1}", al.AliasName, al.AliasValue);
}
public void Click()
{
MessageBox.Show(new String(this.Text.Reverse().ToArray()));
}
}
Populate the Treeview
The following code populates the Treeview by adding the GraphicNodes. By callimg BeginUpdate first, we prevent that the control draws its content on every node that is added. Don't forget to call EndUpdate so the control layouts and redraws all newly added nodes. Notice that you only need to add the GraphicNodes here, the adding of the other nodes is handled by the subclassed TreeNodes.
// tell the control to hold its paint/redraw events until
// we're done adding all items
tv.BeginUpdate();
foreach(var g in graphics)
{
tv.Nodes.Add(new GraphicNode(g));
}
// done, draw all nodes now
tv.EndUpdate();
If you wire up the NodeMouseClick to the following event handler you can reach into the specific implementation of the TreeNode:
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
var node = e.Node as AliasesNode;
if (node != null)
{
node.Click();
}
}
When we click an AliasTreenode we should see the MessageBox popup that is implemented in the Click method of that class:

Related

Convert tree-like class data structure to Winforms TreeView for ease of debugging visually

Using C#, I have created a "node" class (which contains a List of type itself) to allow for recursive tree-like data structures. This is a simplified version of the class:
public class node
{
public List<node> c = null;
public int data = 0;
public node(int data)
{
this.data = data;
}
}
Unfortunately, I've come across a point in my program where it's getting very hard to debug due to the nature of trees. Large trees especially are difficult to probe visually.
For this reason, I had the brain wave to use Winform's TreeView control to display the data so I can more easily visualize what's going on. I understand and have implemented preorder and inorder traversal for a minimax algorithm, and so I was wondering if I could utilize some of that code to help with converting from my own node tree to a TreeView tree.
The method skeleton would look like this:
private void convertNodetreetoTreeview(node n)
{
TreeNode root = new TreeNode("Root node");
...
treeView1.Nodes.Add(root);
}
How would I go about this?
For speed, I would prefer an iterative solution, but a recursive solution would be fine too.
I take the following statement and as I mentioned in the comments, as an idea you can also Create a custom data visualizer to use in Visual Studio to visualize your data at debug-time.
for ease of debugging visually
Then, when you are debugging your application wherever you have a breakpoint for the node class, you will see the visualizer icon:
And if you click on it, you can see your custom visualizer, which could be a Form containing a TreeView:
Create a custom debugger visualizer for my tree-like data structure
There are a few nice articles and guides in Microsoft Docs which explains the architecture of the visualizers and teach you how to create an install a simple visualizer. Here I also put a very simplified guideline to show you how you can achieve what I showed in above screenshots:
Create a .NET Framework Class Library for you data class. Name the project MyDataObjects. This project will contain your data objects which you want to create a custom visualizer for them. Here, Node class. Use the same node class in your main project as well.
Add the following Node.cs class to it:
using System;
using System.Collections.Generic;
namespace MyDataObjects
{
[Serializable]
public class Node
{
private Node() { }
public List<Node> Nodes { get; } = new List<Node>();
public string Text { get; set; }
public Node(string data)
{
this.Text = data;
}
}
}
Create a .NET Framework Class Library for you visualizer. Name the project MyDataObjectsVisualizers.
Add reference to Microsoft.VisualStudio.DebuggerVisualizers.dll which you can browse from:
<Visual Studio Install Directory>\Common7\IDE\PublicAssemblies
Add reference to System.Windows.Forms.dll which you can find in framework assemblies.
Add the following NodeExtensions.cs class to it:
using MyDataObjects;
using System.Windows.Forms;
namespace MyDataObjectsVisualizers
{
public static class NodeExtensions
{
public static TreeNode ToTreeNode(this Node node)
{
var treeNode = new TreeNode(node.Text);
foreach (Node child in node.Nodes)
treeNode.Nodes.Add(child.ToTreeNode());
return treeNode;
}
}
}
Add a new form to the project and name it NodeVisualizerForm.cs. Drop a TreeView on the form and set its Dock to Fill. Then add the following code to the form:
using MyDataObjects;
using System;
using System.Windows.Forms;
namespace MyDataObjectsVisualizers
{
public partial class NodeVisualizerForm : Form
{
public NodeVisualizerForm()
{
InitializeComponent();
}
public Node Node { get; set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (Node != null)
treeView1.Nodes.Add(Node.ToTreeNode());
}
}
}
Add a DebuggerSide.cs class to the project with the following code:
using Microsoft.VisualStudio.DebuggerVisualizers;
[assembly: System.Diagnostics.DebuggerVisualizer(
typeof(MyDataObjectsVisualizers.DebuggerSide),
typeof(VisualizerObjectSource),
Target = typeof(MyDataObjects.Node),
Description = "Sample Visualizer")]
namespace MyDataObjectsVisualizers
{
public class DebuggerSide : DialogDebuggerVisualizer
{
protected override void Show(IDialogVisualizerService windowService,
IVisualizerObjectProvider objectProvider)
{
var node = objectProvider.GetObject() as MyDataObjects.Node;
var form = new NodeVisualizerForm();
form.Node = node;
windowService.ShowDialog(form);
}
}
}
Build the solution, and copy the following files to the following locations:
Copy MyDataObjectsVisualizers.dll to:
[VS Installation Folder]\Common7\Packages\Debugger\Visualizers
Copy MyDataObjects.dll to:
[VS Installation Folder]\Common7\Packages\Debugger\Visualizers\netstandard2.0
Then close all visual studio instances. Then open the solution which you ant to debug and set a breakpoint for node, and run the application. There you go! You can see the visualizer icon (the magnifier) close to the node, like what I showed in the screenshot above and by clicking on it, you will see the visualizer form.
Please take note, in the main project, you should use the same Node dll that we created in MyDataObjects project.
More information:
Walkthrough: Writing a Visualizer in C#
How to: Install a Visualizer
Create custom data visualizers
Below is the code to go about just what you're asking for. Instead of utilizing a Stack and a List (as you might for usual preorder traversal), you utilize TWO stacks - one for the node tree you have, and one for the TreeView tree. From there, it's a simple case of pushing TreeView nodes when custom nodes are being pushed, and popping TreeView nodes when your custom nodes are being popped. Whilst you're pushing TreeView nodes, you simultaneously add these nodes to the TreeView using the top-most TreeView stack reference.
Feel free to replace the mystringdata variable with one of your choice.
private void convertNodetreeToTreeview(node n)
{
Stack<node> stack1 = new Stack<node>();
Stack<TreeNode> stack2 = new Stack<TreeNode>();
stack1.Push(n);
TreeNode root = new TreeNode( n.mystringdata );
stack2.Push(root);
while (stack1.Count > 0)
{
node t = stack1.Pop();
TreeNode r = stack2.Pop();
for (int i = 0; i < t.c.Count; i++)
{
if (t.c[i] != null)
{
stack1.Push(t.c[i]);
TreeNode rchild = new TreeNode(t.c[i].mystringdata );
r.Nodes.Add(rchild);
stack2.Push(rchild);
}
}
}
treeView1.Nodes.Add(root);
}
To convert a custom node class to a TreeNode, you can write a simple recursive extension method.
Assuming you have a class like this:
public class Node
{
public List<Node> Nodes { get; } = new List<Node>();
public string Text { get; set; }
public Node(string data)
{
this.Text = data;
}
}
Then you can write a simple recursive extension method like the following:
public static class NodeExtensions
{
public static TreeNode ToTreeNode(this Node node)
{
var treeNode = new TreeNode(node.Text);
foreach (Node child in node.Nodes)
treeNode.Nodes.Add(child.ToTreeNode());
return treeNode;
}
}
Later, to use it to populate a TreeView:
var node = new Node("1");
node.Nodes.Add(new Node("1-1"));
node.Nodes.Add(new Node("1-2"));
node.Nodes[0].Nodes.Add(new Node("1-1-1"));
node.Nodes[1].Nodes.Add(new Node("1-2-1"));
node.Nodes[1].Nodes.Add(new Node("1-2-2"));
treeView1.Nodes.Add(node.ToTreeNode());
To see a general solution to populate a TreeView based on a DataTable or any list which contains tree-like data, take a look at: Populate TreeView from DataTable or List that contains tree-like data
More information:
How to implement and call a custom extension method (C# Programming Guide)
.NET Naming Guidelines
C# Coding Conventions

addition assignment for variable c#

How would I create a property in a class that can only be set using an addition assignment. I know events work this way in C#. I was wondering how I may implement the construct in my own code.
Eg. For Events I can do the following
public event EventHandler myEventHandler;
myEventHandler += NewEvent;
And that will add NewEvent to the chain of events that gets run when myEventHandler is called.
I want to be able to do something similar. I want to create a Graph class which models mathematical graphs with nodes and connections. The Graph class would have a nodes property which should only be able to have nodes added or removed but not set as a whole property.
Once again using the EventHandler example, I can't do the following with EventHandler
myEventHandler = OnlyOneEventWillRun;
I also want to be able to implement logic to the addition assignment similar to the set{} accessor.
For starters, myEventHandler is a multicast delegate - you can make your own to mirror that functionality, but that doesn't really solve your problem as you stated.
The approach I would take is to use a private setter (or no setter at all) - this would allow you to set the value from within the class that defines the property (where presumably you will know and remember what you are doing) but not from outside the class (where another developer could unwittingly screw it up):
private List<object> _nodes; // private backing field
public List<object> Nodes
{
get
{
return _nodes; // can be "gotten" by any class
}
private set // can only be set from within this class
{
if (value != _nodes)
{
// do additional logic
_nodes = value; // set the backing variable
}
}
}
public void AddNode(object Node)
{
Nodes.Add(Node);
}
public void RemoveNode(object Node)
{
Nodes.Remove(Node);
}
The Graph class would have a nodes property which should only be able
to have nodes added or removed but not set as a whole property.
While this is great for encapsulation, there are simpler and more obvious ways to implement it than overloading the += operator.
Here is one example:
class Graph
{
private List<Node> nodes;
public ReadOnlyCollection<Node> Nodes
{
get { return new ReadOnlyCollection<Node>(nodes); }
//note no setter
}
public void AddNode(Node node)
{
//whatever additional logic you need
nodes.Add(node);
}
}
If you want to make your API more fluent, you could return the Graph object itself as the return value from AddNode, so it could be used with chained adds:
graph.AddNode(node1)
.AddNode(node2)
.AddNode(node3);
And/or accept a params array parameter for the node(s) so that it could be called as:
graph.AddNode(node1, node2);
graph.AddNode(node3);

Notify property in another class when property has changed?

I have a class which is a collection (MapAdapter) and takes a collection as a constructor parameter .MapAdater adds a Map object on CollectionChanged:
public MapAdapter(UndoRedoCollection undoRedoCollection)
{
this.undoRedoCollection = undoRedoCollection;
this.undoRedoCollection.CollectionChanged += this.OnCollectionChanged;
}
This is my collection changed event where the Map objects are added:
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
GetMapRules(TurbineUndoRedoCollection);
}
private void GetMapRules(UndoRedoCollection undoRedoCollection)
{
foreach (var item in undoRedoCollection)
{
this.Add(new Map(item));
}
}
I have a property on MapAdapter which determines whether the layer is visible on a map:
public bool IsLayerVisible
{
get
{
return this.isLayerVisible;
}
set
{
this.isLayerVisible = value;
this.OnPropertyChanged(new PropertyChangedEventArgs("IsLayerVisible"));
}
}
I have a similar property on my Map class. I need to listen to the IsLayerVisible Property on my MapAdapter and change IsLayerVisible on each of my maps within the MapAdapter collection. The reason being is that I have a control which draws an image and is passed in a Map, it knows nothing of the MapAdater therefore I cannot control when & when not to draw based on visibility.
If the title of your post is provides any clues as to what you're trying to do then all you need to do is implement the INotifyPropertyChanged interface and fire the PropertyChanged event when the value of your property changes.
Then, modify your Map's constructor to accept a MapAdapter instance. You don't need to hold a reference to the MapAdapter within Map, just use it to wire up the event handler for the PropertyChanged event against your MapAdapter.
Here is an example of what the Map class might look like...
public class Map
{
public Map(object item, MapAdapter adapter)
{
...
adapter.PropertyChanged += this.AdapterPropertyChanged;
}
private void AdapterPropertyChanged(object sender, PropertyChangedEventArg e)
{
if (e.PropertyName == "IsLayerVisible")
{
// Do something
}
}
}
And then update your GetMapRules method like so...
private void GetMapRules(UndoRedoCollection undoRedoCollection)
{
foreach (var item in undoRedoCollection)
{
this.Add(new Map(item, this));
}
}
}
Look up pub-sub or Event Aggregator patterns.
Specifically, Prism has an implementation that may be of interest.
This link has a standalone example of this pattern that may be instructive as well.

General recursive method for iterate all controls and their related controls in a windows form

Is there any general recursive method that can iterate all controls(include toolstrip and its items, bindingnavigator and its items,...) in a windows form? (some of them are not inherited from Control class) or at least iterate toolstrip and its items, bindingnavigator and its items?
You're going to run into a snag here because ToolStrip uses Items instead of Controls, and ToolStripItem does not inherit from Control. Both ToolStripItem and Control inherit from Component, so at best you can get an IEnumerable<Component>.
You could accomplish this with the following extension method:
public static class ComponentExtensions
{
public static IEnumerable<Component> GetAllComponents(this Component component)
{
IEnumerable<Component> components;
if (component is ToolStrip) components = ((ToolStrip)component).Items.Cast<Component>();
else if (component is Control) components = ((Control)component).Controls.Cast<Component>();
else components = Enumerable.Empty<Component>(); // figure out what you want to do here
return components.Concat(components.SelectMany(x => x.GetAllComponents()));
}
}
On a Windows Form you could process all of these components in a foreach loop:
foreach (Component component in this.GetAllComponents())
{
// Do something with component...
}
Unfortunately, you'll do a lot of manual type checking and casting.
This is what I usually do in this case:
First of all define a delegate that accept a Control as parameter:
public delegate void DoSomethingWithControl(Control c);
Then implement a method that takes this delegate as the first parameter, and the control upon which to recursively execute it as the second. This methods first executes the delegate, then loops on the Controls collection of the control to recursively call itself. This works since Controls is defined in Control and returns an empty collection for simple controls like:
public void RecursivelyDoOnControls(DoSomethingWithControl aDel, Control aCtrl)
{
aDel(aCtrl);
foreach (Control c in aCtrl.Controls)
{
RecursivelyDoOnControls(aDel, c);
}
}
Now you can put the code you want to execute for each control in a method and call it on the Form through the delegate:
private void DoStg(Control c)
{
// whatever you want
}
RecursivelyDoOnControls(new DoSomethingWithControl(DoStg), yourForm);
EDIT:
Since you are dealing with ToolStripItems, too, you could define the delegate to deal with generic objects, and then write different overloads of the recursive method. I.e. something like this:
public delegate void DoSomethingWithObject(Object o);
public void RecursivelyDo(DoSomethingWithObject aDel, Control aCtrl)
{
aDel(aCtrl);
foreach (Control c in aCtrl.Controls)
{
RecursivelyDoOnControls(aDel, c);
}
}
public void RecursivelyDo(DoSomethingWithObject aDel, ToolStrip anItem)
{
aDel(anItem);
foreach (ToolstripItem c in anItem.Items)
{
RecursivelyDo(aDel, c);
}
}
public void RecursivelyDo(DoSomethingWithObject aDel, ToolStripDropDownButton anItem)
{
aDel(anItem);
foreach (ToolStripItem c in anItem.DropDownItems)
{
RecursivelyDo(aDel, c);
}
}
//and so on

Retrieve all winforms components in all subclasses

I would like to retrieve all components which are part of a Form's or UserControl's components collection.
The components collection is added by VS winforms designer. The components variable is private and the problem is how to retrieve all components from all descendants. I would like to have a method which returns list of components throught the type hierarchy. For example let's say I have MyForm (descendant of BaseForm) and BaseForm (descendant of Form). I would like to put method "GetComponents" which returns components of both MyForm and BaseForm.
Do you suggest any other option than using the reflection?
Some time ago I have implemented the solution in which I created custom base form and control implementations, adding one property and overriding the OnLoad method:
public partial class FormBase : Form
{
public FormBase ()
{
this.InitializeComponent();
}
protected ConsistencyManager ConsistencyManager { get; private set; }
protected override void OnLoad(System.EventArgs e)
{
base.OnLoad(e);
if (this.ConsistencyManager == null)
{
this.ConsistencyManager = new ConsistencyManager(this);
this.ConsistencyManager.MakeConsistent();
}
}
}
The ConsistencyManager class finds all controls, components and also supports search of custom child controls within specific control. Copy/paste of code from MakeConsistent method:
public void MakeConsistent()
{
if (this.components == null)
{
List<IComponent> additionalComponents = new List<IComponent>();
// get all controls, including the current one
this.components =
this.GetAllControls(this.parentControl)
.Concat(GetAllComponents(this.parentControl))
.Concat(new Control[] { this.parentControl });
// now find additional components, which are not present neither in Controls collection nor in components
foreach (var component in this.components)
{
IAdditionalComponentsProvider provider = GetAdditinalComponentsProvider(component.GetType().FullName);
if (provider != null)
{
additionalComponents.AddRange(provider.GetChildComponents(component));
}
}
if (additionalComponents.Count > 0)
{
this.components = this.components.Concat(additionalComponents);
}
}
this.MakeConsistent(this.components);
}
If anyone would like full sample or source let me know.
Best regards,
Zvonko
PS: In the same manner I have also created the performance counter that counts number of invocations on main thread.

Categories

Resources