Dynamically Generate WinForms TreeView from Assembly - c#

I've almost got it, but I am having a hard time with doing the last part gracefully. This answer was update based on the answer submitted by Jeremy Thompson. This is what I have so far:
public void SetupTree()
{
var types = Assembly.Load("Data").GetTypes().Where(t => t.IsPublic && t.IsClass);
if (types.Count() > 0)
{
if (treeView_left.Nodes.Count == 0)
{
treeView_left.Nodes.Add(new TreeNode("Structure Data"));
treeView_left.Nodes[0].Nodes.Add(types.First().GetHashCode().ToString(), types.First().Name);
}
foreach (Type type in types)
{
BuildTree(types, type, treeView_left.Nodes[0].Nodes[0]);
}
}
treeView_left.Refresh();
}
private void BuildTree(IEnumerable<Type> types, Type type, TreeNode parentNode)
{
var tempNodes = treeView_left.Nodes.Find(type.BaseType.GetHashCode().ToString(), true);
if (tempNodes.Count() > 0)
{
parentNode = tempNodes[0];
if (tempNodes.Count() != 1)
{
//TODO: warning
}
}
if (parentNode != null)
{
if (treeView_left.Nodes.Find(type.GetHashCode().ToString(), true).Count() == 0)
{
parentNode.Nodes.Add(type.GetHashCode().ToString(), type.Name);
}
foreach (Type t in types.Where(x => x.IsSubclassOf(type)))
{
BuildTree(types, t, parentNode.Nodes[type.GetHashCode().ToString()]);
}
}
}
This produces the result I am looking for, but I suspect I am doing some of this the hardway. If anyone could point out a cleaner method for the last part I would appriciate it.

I haven't tested this yet, but notice the Recursive call LoadAllChildren calls itself.
public void SetupTree()
{
Assembly dataLib = Assembly.Load("Data");
TreeNode theTree = new TreeNode("Assembly Data");
foreach (Type type in dataLib.GetTypes())
{
LoadAllChildren(dataLib, type, theTree);
}
treeView_left.Nodes.Add(theTree); //Optimisation - bind all nodes in one go rather than adding individually
}
private void LoadAllChildren(Assembly dataLib,Type type, TreeNode parentNode)
{
if (type.IsPublic && type.IsClass)
{
TreeNode node = new TreeNode(type.Name);
parentNode.Nodes.Add(node);
var types = dataLib.GetTypes().Where(x => x.IsSubclassOf(type));
foreach (Type t in types)
{
LoadAllChildren(dataLib, t, node);
}
}
}
I hope this is enough to get you over the hurdle, feel free to ask Q's
I wont respond quickly as my PC is about to get rebuilt:(

Related

copy properties c# but check if a property is already set

I working hard on a JSON-Deserializer on my own. Just for training. I am nearly finished, but I have a copying issue.
I already have this:
public void CopyValues<T>(T target, T source)
{
Type t = typeof(T);
var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var value = prop.GetValue(source, null);
if (value != null)
prop.SetValue(target, value, null);
}
}
The Main Problem is here. I have a Property containing 2 Properties. Like content.link and content.value.
If I use the Copy Function it copys right. No discussion. But if I put the copy function into a loop, and the data is filled up, the source having also
"content", but without link and value.
If I copy again, the already correctly filled properties are getting overridden, and the result is that I have only null at conent.link and content.value.
Is there a way, to check if link and value is set to null ?
In order to copy the nested properties, you will need use a recursive function:
public static void DeepCopy<T>(T target, T source)
{
DeepCloneImpl(typeof(T), source, target);
}
public static T DeepClone<T>(T template)
where T : new()
{
return (T)DeepCloneImpl(typeof(T), template);
}
private static object DeepCloneImpl(Type type, object template, object stump = null)
{
if (template == null)
{
return null;
}
var clone = stump ?? Activator.CreateInstance(type);
var clonableProperties = type.GetProperties()
.Where(x => x.GetMethod != null && x.SetMethod != null);
foreach (var property in clonableProperties)
{
var propertyType = property.PropertyType;
if (propertyType.GetTypeInfo().IsValueType || propertyType == typeof(string))
{
var value = property.GetValue(template);
property.SetValue(clone, value);
}
else if (propertyType.GetTypeInfo().IsClass && propertyType.GetConstructor(Type.EmptyTypes) != null)
{
var value = DeepCloneImpl(propertyType, property.GetValue(template));
property.SetValue(clone, value);
}
else if (propertyType.IsArray)
{
var source = property.GetValue(template) as Array;
if (source == null)
{
continue;
}
var elementType = propertyType.GetElementType();
if (elementType.GetTypeInfo().IsValueType || elementType == typeof(string))
{
var copies = Array.CreateInstance(elementType, source.Length);
Array.Copy(source, copies, source.Length);
property.SetValue(clone, copies);
}
else if (elementType.GetTypeInfo().IsClass)
{
var copies = Array.CreateInstance(elementType, source.Length);
for (int i = 0; i < source.Length; i++)
{
var copy = DeepCloneImpl(elementType, source.GetValue(i));
copies.SetValue(copy, i);
}
property.SetValue(clone, copies);
}
}
}
return clone;
}
This should cover most of the use case, however you will have to handle self/circular references.
From what I can gather, the problem is values are being overridden. And from looking at your method, it appears to be doing what you'd expect, it's a soft clone of property values. Depending on what a correctly filled property is in your situation, you'll have to make a comparison that prevents the property from being set again. Perhaps value != null should actually be value == null so that you're only setting the value when there wasn't already one. Because of the generic typing of this method you don't have to worry about property mismatches if that's why you're using != null. Both arguments, source and target, will be of the same type.

How to add new node when building Binary Tree

I'm working with an alternate version of the BinaryTree class called RedBlackTree. I'm trying to build the tree with this add method.
public void Add(T val)
{
RedBlackNode<T> newNode = _rootNode;
while(newNode != null)
{
if(val.CompareTo(newNode.Data) < 0)
{
newNode = newNode.LeftChild;
}
else if(val.CompareTo(newNode.Data) >= 0)
{
newNode = newNode.RightChild;
}
}
newNode.Data = val; //nullReferenceException thrown here <---
newNode.IsBlack = false;
FixColors(newNode);
}
_rootNode is a private field giving the root of the tree. It is initialized to null as given by specific instructions. This Add method is being called from within another class from a method that is reading in file info. On the first iteration, a nullReferenceException is thrown, I assume because the node I am trying to change is null. I don't know how else I am supposed to change the data value of these nodes. I will post the code to the rest of the program if necessary.
Well, the while loop isn't going to exit while newNode isn't null. That implies newNode is going to be null once the loop exits. I think what you want is to create a new node if the RightChild or LeftChild nodes is null, and set newNode to that.
In addition, as Partha's answer points out, you need to initialize the _rootNode value if it isn't already set.
I don't really know how the parent property of the node is set, so I'm going to assume the parent node is injected as a constructor argument of the child node:
public void Add(T val)
{
RedBlackNode<T> parent = _rootNode;
RedBlackNode<T> target = null;
while(target == null)
{
if(val.CompareTo(parent.Data) < 0)
{
if (parent.LeftChild == null)
target = parent.LeftChild = new RedBlackNode<T>(parent);
else
parent = parent.LeftChild;
}
else if(val.CompareTo(newNode.Data) >= 0)
{
if (parent.RightChild == null)
target = parent.RightChild = new RedBlackNode<T>(parent);
else
parent = parent.RightChild;
}
}
target.Data = val;
target.IsBlack = false;
FixColors(newNode);
}
You need to create the node before you access the other fields. So if _rootNode is empty you create using new keyword.
public void Add(T val)
{
if(_rootNode == null)
{
RedBlackNode<T> newNode = new RedBlackNode<T>();
newNode.Data = val;
newNode.IsBlack = false;
_rootNode = newNode;
return;
}
//Followed by your logic
}

Get next item in a tree

Having a tree (logical in DB) with items in the form
List item A
List item B
List item C
List item D
List Item E
List Item F
List Item G
and so on (nesting depth not limited), I want to get the next node down (or up), starting from an arbitrary node.
Let's say, List Item D is given I want to write a function GetNextNode() that would return List Item E.
My idea would be to do some recursion stuff, but maybe there is a more clever way to handle this?
My question:
How would you solve this?
EDIT 1:
The tree can be accessed with functions like:
GetParentNode()
GetChildrenNodes()
GetNextSiblingNode()
etc.
So it's similar to e.g. e Windows Forms TreeView.
I've had to do this several times. From memory:
public Node GetBelowNode()
{
if (GetChildrenNodes().count > 0)
return GetChildrenNodes()[0];
else
if (GetNextSiblingNode() != null)
return GetNextSiblingNode();
else
{
Node curr = this;
Node parent;
while (true)
{
parent = curr.GetParentNode();
if (parent == null)
return null;
else
{
if (parent.GetNextSiblingNode() != null)
return parent.GetNextSiblingNode();
else
curr = parent;
}
}
}
}
You can handle this via recursion or... worst xD
I think there are only 3 basic cases:
private string getNext(TreeNode node)
{
if (node.FirstNode != null)
{
return node.FirstNode.Name;
}
else
{
if (node.NextNode != null)
{
return node.NextNode.Name;
}
else if (node.Parent.NextNode != null)
{
return node.Parent.NextNode.Name;
}
}
return "";
}
This doesn't works for every scenario. You should search the parent's next node too. Thanks to Vincent Vancalbergh for the comment ;-)
public Party Next {
get {
if (this.children.Count > 0) return this.children[0];
Party target = this;
do {
if (target.NextSibling != null) return target.NextSibling;
} while ((target = target.Parent) != null);
return null;
}
}
public Party Previous {
get {
if (Parent != null && Parent.children.Count > 0 && this == Parent.children[0]) {
return Parent;
}
Party target = this;
do {
if (target.PreviousSibling != null) { target = target.PreviousSibling; break; }
} while ((target = target.Parent) != null);
if (target != null) {
while (target.children.Count > 0) {
target = target.children[target.children.Count - 1];
}
}
return target;
}
}
Since I got a great reply for the "down" part, I'll added my own "up" part. Maybe it is for some help of you; the up part is similar to:
Get the previous sibling.
If there is a previous sibling, get the deepest child node of this
sibling.
If there is no previous sibling, get the direct parent.
To get the deepest sibling (2.), I use the following code:
function getDeepestChild( page )
dim result
set result = nothing
dim childPages
set childPages = page.ChildPages
if childPages.Count>0 then
dim p
set p = childPages(childPages.Count-1)
' recurse.
set result = getDeepestChild( p )
else
set result = page
end if
set getDeepestChild = result
end function
(Yes, I do know that this is VBScript; I needed it in fact in this language)
Try this maybe:
TreeNode currentNode = treeView1.SelectedNode;
treeView1.selectedNode = currentNode.NextNode;

Single Linked List : remove method

I am writing a single linked list in C#, could you please suggest to me if there any way to write a better remove method than the one I have:
using System;
class Node
{
public int data = int.MinValue;
public Node m_NextNode;
public Node(int data_in)
{
data = data_in;
}
public Node()
{
}
}
class LinkedList
{
private Node m_HeadNode;
public void InsertData(int data)
{
Node aCurrentNode = m_HeadNode;
if(m_HeadNode == null)
{
m_HeadNode = new Node();
m_HeadNode.data = data;
}
else
{
while(aCurrentNode.m_NextNode != null)
aCurrentNode = aCurrentNode.m_NextNode;
aCurrentNode.m_NextNode = new Node();
aCurrentNode.m_NextNode.data = data;
}
}
public void DisplayData()
{
Node aCurrentNode = m_HeadNode;
while (aCurrentNode != null)
{
Console.WriteLine("the value is {0}", aCurrentNode.data);
aCurrentNode = aCurrentNode.m_NextNode;
}
}
public void RemoveData(int data)
{
Node aCurrentNode = m_HeadNode;
while (aCurrentNode != null)
{
//if the data is present in head
//remove the head and reset the head
if (m_HeadNode.data == data)
{
m_HeadNode = null;
m_HeadNode = aCurrentNode.m_NextNode;
}
else
{
//else save the previous node
Node previousNode = aCurrentNode;
if (aCurrentNode != null)
{
//store the current node
Node NextNode = aCurrentNode.m_NextNode;
if (NextNode != null)
{
//store the next node
Node tempNode = NextNode.m_NextNode;
if (NextNode.data == data)
{
previousNode.m_NextNode = tempNode;
NextNode = null;
}
}
aCurrentNode = aCurrentNode.m_NextNode;
}
}
}
}
}
class Program
{
static void Main(string[] args)
{
LinkedList aLinkedList = new LinkedList();
aLinkedList.InsertData(10);
aLinkedList.InsertData(20);
aLinkedList.InsertData(30);
aLinkedList.InsertData(40);
aLinkedList.DisplayData();
aLinkedList.RemoveData(10);
aLinkedList.RemoveData(40);
aLinkedList.RemoveData(20);
aLinkedList.RemoveData(30);
aLinkedList.DisplayData();
Console.Read();
}
}
I would pull the 'if removing the head node' out of the while loop, and make the while loop simpler. Just keep track of the current and previous nodes, and switch the reference when you find the node to remove.
public void RemoveData(int data)
{
if (m_HeadNode == null)
return;
if (m_HeadNode.data == data)
{
m_HeadNode = m_HeadNode.m_NextNode;
}
else
{
Node previous = m_HeadNode;
Node current = m_HeadNode.m_NextNode;
while (current != null)
{
if (current.data == data)
{
// If we're removing the last entry in the list, current.Next
// will be null. That's OK, because setting previous.Next to
// null is the proper way to set the end of the list.
previous.m_NextNode = current.m_NextNode;
break;
}
previous = current;
current = current.m_NextNode;
}
}
}
Is RemoveData supposed to remove one instance of that integer from the list, or all instances? The previous method removes just the first one, here's one that removes all of them.
public void RemoveAllData(int data)
{
while (m_HeadNode != null && m_HeadNode.data == data)
{
m_HeadNode = m_HeadNode.m_NextNode;
}
if(m_HeadNode != null)
{
Node previous = m_HeadNode;
Node current = m_HeadNode.m_NextNode;
while (current != null)
{
if (current.data == data)
{
// If we're removing the last entry in the list, current.Next
// will be null. That's OK, because setting previous.Next to
// null is the proper way to set the end of the list.
previous.m_NextNode = current.m_NextNode;
// If we remove the current node, then we don't need to move
// forward in the list. The reference to previous.Next, below,
// will now point one element forward than it did before.
}
else
{
// Only move forward in the list if we actually need to,
// if we didn't remove an item.
previous = current;
}
current = previous.m_NextNode;
}
}
}
You have a line you do not need:
m_HeadNode = null;
m_HeadNode = aCurrentNode.m_NextNode;
You don't need to set m_HeadNode to null before you set it to something else.
public bool RemoveNode(int data) {
Node prev = null;
for (Node node = head; node != null; node = node.next) {
if (node.data == data) {
if (prev == null) head = node.next;
else prev.next = node.next;
return true;
}
prev = node;
}
return false;
}
public void RemoveData(int data)
{
if(null == m_HeadNode) return;
if(m_HeadNode.data == data)
{
// remove first node
}
else
{
Node current = m_HeadNode;
while((null != current.m_NextNode) && (current.m_NextNode.data != data))
current = current.m_NextNode;
if(null != current.m_NextNode)
{
// do remove node (since this sounds like homework, I'll leave that to you)
}
}
}
with only one local variable.
There is a bug in you code, imagine you have list that has 3 elements with data = 5 and you want to remove 5, you codes stores the head in aCurrentNode and starts the loop. There the condition is true so you move the head to the next. but aCurrentNode is not updated so in the next iteration it's pointing to the previous Head and aCurrentNode.m_NextNod would be your current Head, Hence you got yourself in a never ending loop!
public void Remove(int data)
{
for(var cur = new Node {Next = Head}; cur.Next != null; cur = cur.Next)
{
if (cur.Next.Data != data) continue;
if (cur.Next == Head)
Head = Head.Next;
else
cur.Next = cur.Next.Next;
}
}
a trick to make you loop simpler is to start with a fake node that points to head. This way you don't need to place an If to check head differently, however you need an if to set the head. the other trick is to check for next nodes data. that way you don't need to keep a previous Node.
public SLElement Remove(int index)
{
SLElement prev = _root;
if (prev == null) return null;
SLElement curr = _root._next;
for (int i = 1; i < index; i++)
{
if (curr == null) return null;
prev = curr;
curr = curr._next;
}
prev._next = curr._next;
curr._next = null;
return curr;
}
This deletes the element with a specific index, not with a specific value.
To Make really simple. Please follow the link. Below is a code snippet to remove item from a single link list.
public void DeleteNode(int nodeIndex)
{
int indexCounter = 0;
Node TempNode = StartNode;
Node PreviousNode = null;
while (TempNode.AddressHolder != null)
{
if (indexCounter == nodeIndex)
{
PreviousNode.AddressHolder = TempNode.AddressHolder;
break;
}
indexCounter++;
PreviousNode = TempNode;
TempNode = TempNode.AddressHolder;
}
}

create a copy of an ASP.NET Control object

I have a cached control object form a UserControl in asp.net cache.
I like to access my control and make a copy of it to freely change it without having any affect on the base cached control.
Hi
I found the code that I was looking for.
I put it here maybe it helps you too.
/// <summary>
/// this method makes a copy of object as a clone function
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public static object Clone_Control(object o)
{
Type type = o.GetType();
PropertyInfo[] properties = type.GetProperties();
Object retObject = type.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, null, o, null);
foreach (PropertyInfo propertyInfo in properties)
{
if (propertyInfo.CanWrite)
{
propertyInfo.SetValue(retObject, propertyInfo.GetValue(o, null), null);
}
}
return retObject;
}
It is certainly possible to do what you are asking to do. And it is very straightforward. Something like this maybe?
private static void ChangeMyControlTextFromCache(string controlName, string newText, Panel containingPanel)
{
MyControl original = ViewState[controlName] as MyControl;
if (original == null)
{
original = new MyControl();
ViewState[controlName] = original;
}
MyControl clone = CloneMyControl(original);
clone.Text = newText;
if (containingPanel.Children.Contains(original))
containingPanel.Children.Remove(original);
containingPanel.Children.Add(clone);
}
private static MyControl CloneMyControl(MyControl original)
{
MyControl clone = new MyControl();
clone.Text = original.Text;
clone.SomeOtherProperty = original.SomeOtherProperty;
return clone;
}
Here is another clone method that will also work with MONO.
It clones the passed control and it's children.
Note: A cloned control cannot be added to a different page (or in a later post-back) even it has not been added to any page yet. It might seem to work on Windows but will crash on MONO so I guess it is generally not meant to be done.
public static Control CloneControl(Control c) {
var clone = Activator.CreateInstance(c.GetType()) as Control;
if (c is HtmlControl) {
clone.ID = c.ID;
foreach (string key in ((HtmlControl)c).Attributes.Keys)
((HtmlControl)clone).Attributes.Add(key, (string)((HtmlControl)c).Attributes[key]);
}
else {
foreach (PropertyInfo p in c.GetType().GetProperties()) {
// "InnerHtml/Text" are generated on the fly, so skip them. "Page" can be ignored, because it will be set when control is added to a Page.
if (p.CanRead && p.CanWrite && p.Name != "InnerHtml" && p.Name != "InnerText" && p.Name != "Page") {
try {
p.SetValue(clone, p.GetValue(c, p.GetIndexParameters()), p.GetIndexParameters());
}
catch {
}
}
}
}
for (int i = 0; i < c.Controls.Count; ++i)
clone.Controls.Add(CloneControl(c.Controls[i]));
return clone;
}
try this for page includes LiteralControl and ResourceBasedLiteralControl.
public static System.Web.UI.Control CloneControl(Control c)
{
Type type = c.GetType();
var clone = (0 == type.GetConstructors().Length) ? new Control() : Activator.CreateInstance(type) as Control;
if (c is HtmlControl)
{
clone.ID = c.ID;
AttributeCollection attributeCollection = ((HtmlControl)c).Attributes;
System.Collections.ICollection keys = attributeCollection.Keys;
foreach (string key in keys)
{
((HtmlControl)c).Attributes.Add(key, (string)attributeCollection[key]);
}
}else if(c is System.Web.UI.LiteralControl){
clone = new System.Web.UI.LiteralControl(((System.Web.UI.LiteralControl)(c)).Text);
}
else
{
PropertyInfo[] properties = c.GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
// "InnerHtml/Text" are generated on the fly, so skip them. "Page" can be ignored, because it will be set when control is added to a Page.
if (p.Name != "InnerHtml" && p.Name != "InnerText" && p.Name != "Page" && p.CanRead && p.CanWrite)
{
try
{
ParameterInfo[] indexParameters = p.GetIndexParameters();
p.SetValue(clone, p.GetValue(c, indexParameters), indexParameters);
}
catch
{
}
}
}
}
int cControlsCount = c.Controls.Count;
for (int i = 0; i < cControlsCount; ++i)
{
clone.Controls.Add(CloneControl(c.Controls[i]));
}
return clone;
}
ref:
Dynamic Web Controls, Postbacks, and View State
By Scott Mitchell
Upvoted answers didn't work for me. Here's my solution to creating a copy of an ASP.NET control object.
// Copies the custom properties on the control. To use it, cast your object to it.
public static object CopyUserControl(object obj, object obj2)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
Type type2 = obj2.GetType();
PropertyInfo[] properties2 = type.GetProperties();
foreach (PropertyInfo property in properties)
{
foreach (PropertyInfo property2 in properties2)
{
// Match the loops and ensure it's not a non-custom property
if (property.Name == property2.Name && property.SetMethod != null && !property.SetMethod.IsSecuritySafeCritical)
property2.SetValue(obj2, property.GetValue(obj, null));
}
}
return obj2;
}
It's useful when you have to copy an ASCX control on the same page with a different html id. Usage:
// Copying myAscxControl1 onto myAscxControl2
myAscxControl2 = (ObjectNameToBeCasted)CopyUserControl(myAscxControl1, myAscxControl2);
It should work with different typed objects too. Property names that you want to copy must match though.

Categories