Easy object binding to Treeview Node - c#

How can I bind an object to a TreeView (WinForms) node in C#?
I thought of something like ExNode : Windows.Forms.Node that can take an object as member besides the treenode name... however I am not sure that is the right approach.

imho you have several strategies :
stick an object of any type in the Tag property of any Node : downside : you'll have to cast it back to its 'native form' when you retrieve it to use it : if that "native form" is anything but type 'Object.
sub-class TreeNode, and add a public fields, Public Properties, or whatever, for your objects ... or even List ... ... or whatever you need to associate with the Node.
assuming your objects are of the same type, you could create a dictionary of type : Dictionary <TreeNode, myObjectType>, instantiate it, and, as needed, store a TreeNode and its associated Object(s) that way as a Key/Value Pair.
Strategies #1, and #3 have the advantage that you can store an associated object ONLY as needed Strategy #2 : is more suited to the case where you anticipate every TreeNode is going to have an associated object(s).
Of course with stragies #1 and #3, you will need to test at run-time for the presence or absence of an object associated with a particular Node.
Strategy #1's an easy test : if the Tag propety of the Node is Null : you know there's no object : if not null ... and there may be more than one type of object stored in the Tag field ... then you'll have to pull out the Tag object, and make sure it's the right type as in : (the example that follows assumes a public class, "Class1," has been assigned to tag of the first node in the TreeView :
TreeNode thisNode = theTreeView.Nodes[0];
if (((thisNode.Tag != null) && (thisNode.Tag is Class1))) ... handle the object ...
Strategy #3 is a little easier since you can just evaluate if the Dictionary<Node, myObject>.Contains the Node as a Key.

Are you looking for something like the Tag property on TreeNodes? It can hold any object.
http://msdn.microsoft.com/en-us/library/system.windows.forms.treenode.tag.aspx

This MSDN article has some good information, for example:
class myTreeNode : TreeNode
{
public string FilePath;
public myTreeNode(string fp)
{
FilePath = fp;
this.Text = fp.Substring(fp.LastIndexOf("\\"));
}
}

You might look into the TreeListView. It isn't perfect, but it works very well and makes the process of displaying objects in a tree view much easier than any other way I have found.

Related

How to tell MessageQueue.SendMessageConnection how to "XMLize" the object to be sent?

I'm working in C# with a System.Messaging.MessageQueue.SendMessageConnection for sending an object, containing some parameters, something like:
_sendQueue.Send(myObject, ...);
My myObject is an object, containing some attributes, like Field1.
I'm checking how my messages get sent, using:
Computer Management
Services and Applications
Message Queuing
Private Queues
open the right queue, and in the "queue messages", right-click and check "Properties", "Body".
There I see tags like:
<Field1>content_Field1</Field1>
Instead of this, I would like to see something like:
<F1>content_Field1</F1>
Is there an easy mapping between the attributes in my object and the XML tags I would like to be used?
Thanks in advance
That's actually quite easy to do. Check out Control XML serialization using attributes :
By default, an XML element name is determined by the class or member name. In a simple class named Book, a field named ISBN will produce an XML element tag , as shown in the following example.
public class Book
{
public string ISBN;
}
// When an instance of the Book class is serialized, it might
// produce this XML:
// <ISBN>1234567890</ISBN>.
This default behavior can be changed if you want to give the element a new name. The following code shows how an attribute enables this by setting the ElementName property of a XmlElementAttribute.
public class TaxRates {
[XmlElement(ElementName = "TaxRate")]
public decimal ReturnTaxRate;
}
- Microsoft article as of 2017-03-30, various authors (emphasis by me)
The whole article is about a ~6minutes read and I really recommend it.

Syntax rewriting

I need to transform all of the properties of a certain class using Roslyn.
What is the recommended way to apply more than one transformation to a syntax tree without invalidating references into it?
Here is what I've tried and where I'm stuck:
In the first pass a descendant of CSharpSyntaxWalker is visiting all PropertyDeclarationSyntax nodes, and storing them in a list.
In the second pass a CSharpSyntaxRewriter is transforming the nodes while checking that each visited node equals one in the list before transforming it.
The problem with my attempt is: When I transform a property I add new fields to the class which causes the class to mutate. All the references in the list to the other properties become invalid in the new tree for the class.
It seems to be inefficient to revisit the whole class, and either way I cannot detect the property nodes already handled due to the reference difference.
I would not recommend to reference nodes from a SyntaxTree you want to modify. In your case just using a CSharpSyntaxRewriter in a single pass (without keeping references from a pre-processing pass) would be sufficient, because its VisitPropertyDeclaration method will only be called once per property, so there is no need to keep track of the nodes you've already modified.
The CSharpSyntaxRewriter also visits the nodes bottom-up, so the overrides should always be called with a node from the original tree. Most likely you have modified the node through the call to base.VisitPropertyDeclaration() before comparing its reference to the stored one. So you could still keep and compare references if you really wanted to.
public class PropertyRewriter : CSharpSyntaxRewriter
{
public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
{
// 'node' should be still from the original syntax tree here
node = (PropertyDeclarationSyntax)base.VisitPropertyDeclaration(node);
// 'node' might be replaced here
return node;
}
}
Instead of keeping references to the nodes you want to modify, you could annotate them. Annotations to a node will survive modifications of the containing SyntraxTree as long as the node itself isn't replaced. You can add them like this:
node = node.WithAdditionalAnnotations(
new SyntaxAnnotation("propertyToChange", "todo"));
To retrieve the annotation again either use node.GetAnnotations("propertyToChange") or use GetAnnotatedNodesOrTokens("propertyToChange") to retrieve all nodes or tokens with an annotation of the given kind ("propertyToChange").

Serialization - Viewing the Object Graph from a Stream

I'm wondering if there's a way in which I can create a tree/view of a serialised object graph, and whether anyone has any pointers? EDIT The aim being that should we encounter a de-serialization problem for some reason, that we can actually view/produce a report on the serialized data to help us identify the cause of the problem before having to debug the code. Additionally I want to extend this in the future to take two streams (version 1, version 2) and highlight differences between the two of them to help ensure that we don't accidently remove interesting information during code changes. /EDIT
Traditionally we've used Soap or XML serialization, but these are becoming too restricted for our needs, and Binary serialization would generally do all that we need. The reason that this hasn't been adopted, is because it's much harder to view the serialized contents to help fix upgrade issues etc.
So I've started looking into trying to create a view on the serialized information. I can do this from an ISerializable constructor to a certain extent :
public A(SerializationInfo info, StreamingContext context)
{}
Given the serialization info I can reflect the m_data member and see the actual serialized contents. The problem with this approach is
It will only display a branch from the tree, I want to display the entire tree from the root and it's not really possible to do from this position.
It's not a convenient place to interrogate the information, I'd like to pass a stream to a class and do the work there.
I've seen the ObjectManager class but this works on an existing object graph, whereas I need to be able to work from the stream of data. I've looked through the BinaryFormatted which uses an ObjectReader and a __BinaryParser, hooking into the ObjectManager (which I think will then have the entire contents, just maybe in a flat list), but to replicate this or invoke it all via reflection (2 of those 3 classes are internal) seems like quite a lot of work, so I'm wondering if there's a better approach.
You could put a List<Child class> in every parent class (Even if there the same)
and when you create a child you immediately place it in that list or better yet declare it whilst adding it the list
For instance
ListName.Add(new Child(Constructer args));
Using this you would serialize them as one file which contains the hierarchy of the objects and the objects themselves.
If the parent and child classes are the same there is no reason why you cannot have dynamic and multi leveled hierarchy.
In order to achieve what you describe you would have to deserialize whole object graph from stream without knowing a type from which it was serialized. But this is not possible, because serializer doesn't store such information.
AFAIK it works in a following way. Suppose you have a couple of types:
class A { bool p1 }
class B { string p1; string p2; A p3}
// instantiate them:
var b = new B { p1 = "ppp1", p2 = "ppp2", p3 = new A { p1 = true} };
When serializer is writing this object, it starts walking object graph in some particular order (I assume in alphabetic order) and write object type and then it's contents. So your binary stream will like this:
[B:[string:ppp1][string:ppp2][A:[bool:true]]]
You see, here there are only values and their types. But order is implicit - like it is written.
So, if you change your object B, to suppose
class B { A p1; string p3; string p3;}
Serialzer will fail, because it will try to assing instance of string (which was serialized first) to pointer to A. You may try to reverse engineer how binary serialization works, then you may be able to create a dynamic tree of serialized objects. But this will require considerable effort.
For this purpose I would create class similar to this:
class Node
{
public string NodeType;
public List<Node> Children;
public object NodeValue;
}
Then while you will be reading from stream, you can create those nodes, and recreate whole serialized tree and analyze it.

Deserialized xml - check if has child nodes without knowing specific type

I have deserialized an xml file into a C# object and have an "object" containing a specific node I have selected from this file.
I need to check if this node has child nodes. I do not know the specific type of the object at any given time.
At the moment I am just re-serializing the object into a string, and loading it into an XmlDocument before checking the HasChildNodes property, however when I have thousands of nodes to check this is extremely resource intensive and slow.
Can anyone think of a better way I can check if the object I have contains child nodes?
Many thanks :)
try using Linq2xml, it has a class called XElement (or XDocument) which are much easier to use then the XmlDocument.
something like this:
XElement x = XElement.Load("myfile.xml");
if (x.Nodes.Count() > 0)
{
// do whatever
}
much less code, much more slick, very readable.
if you have the xml already as a string, you can replace the Load with the Parse function.
I guess you could reverse the process (looking at all public members/properties that aren't marked [XmlIgnore], aren't null, and don't have a public bool ShouldSerialize*() that returns false or any of the other patterns), but this seems a lot of work...

Downcasting in C#

I'm facing a problem that I don't know how to solve and am hoping the community can help.
I'm writing an app that manages "Lead" objects. (These are sales leads.) One part of my program will import leads from a text file. Now, the text file contains lots of potential leads, some of which I will want to import and some of which I won't.
For ease of programming (and use), I'm parsing the text file into a List<Lead> object, and using a DataGridView to display the leads by setting the DataSource property of the DataGridView.
What I want to do is add a column to the grid, called "Import," with a checkbox that the user can check to indicate whether or not each lead should be imported.
My first thought is to derive a class from Lead:
public Class LeadWithImportCheckbox : Lead
{
bool bImport = false;
public bool Import
{
get { return bImport;}
set { bImport = value;}
}
}
However, the parsing engine returns a list of Lead objects. I can't downcast a Lead to a LeadWithImportCheckbox. This fails:
LeadWithImportCheckbox newLead = (LeadWithImportCheckbox)LeadFromParsingEngine;
This is an invalid cast.
The other option I see is to create a constructor for LeadWithImportCheckbox:
public LeadWithImportCheckbox(Lead newlead)
{
base.Property1 = newlead.Property1;
base.Property2 = newlead.Property2;
....
base.Property_n = newlead.Property_n;
}
This is problematic for two reasons. One, the Lead object has several dozen properties and writing this constructor is a PITA.
But worse, if I ever change the underlying structure of Lead, I need to remember to go back and change this constructor for LeadWithImportCheckbox. This is a danger to my code maintenance.
Is there a better way of accomplishing my goal?
or, to avoid the PITA aspect, use reflection... (try this...)
EDIT: use property, not Field as I had originally written...
public class NewLead : Lead
{
public bool Insert;
public NewLead(Lead lead, bool insert)
{
Insert = insert;
foreach (PropertyInfo pi in typeof(Lead).GetProperties())
GetType().GetProperty(pi.Name).SetValue
(this, pi.GetValue(lead,null), null);
}
}
public class LeadListItem
{
public Lead Lead { get; set; }
public bool ShouldImport { get; set; }
}
i.e. don't copy the Lead object's contents, just store a reference to it in a new LeadListItem object, which adds extra info "outside" the original object.
If you want the properties of Lead to appear in the grid, there is almost certainly a way of doing that. Why not ask that question, instead of downvoting me for telling you the right answer to this question!
A couple options you might have missed:
You could update the Lead object itself to have an Import property (that defaults to false).
You could have your "ImportLead" object treat the Lead as payload (even make it generic, if you want), so you don't need the big constructor.
Build a new Lead object list or enumerable that only contains the objects you want to import in the first place.
You can only downcast, if the object to be downcast is really an object of that type.
An easier way to solve your problem would be to have a DisplayLead class, such as:
public class DisplayLead {
Lead lead;
bool bImport;
}
which would also help you separating stored data from their representation in a GUI.
What you want to do is display the checkbox column on your grid and not have it related at all to your Lead objects. You use the marked columns (and possible the original List) to build a new set of List which will be your import list.
Then handle whatever you wish to do with the newly created List.
Edit: One thing to be careful of when working with lists is the fact every class object is actually only a pointer to the class so if you work with the original list and do something like:
List<Lead> Importable = new List<Lead>();
for(int i=0, i++, i<viewGrid.Count)
if(viewGrid[i].CheckedColumn.Checked)
Importable.Add(OriginalList[i]);
That objects will exist in both lists and if you edit data of a Lead on either list both will be changed.
I cannot downcast to something it is not. If the object was instantiated as a Lead, then it can't be downcast to any derived class. If it were instantiated as a LeadWithImportCheckbox and then returned to your code as Lead, then you can downcast it.
Protip: Check type at runtime with is operator.
There are many ways to do this, but the "right" way pops out because of what you said, here:
For ease of programming (and use), I'm
parsing the text file into a
List object, and using a
DataGridView to display the leads by
setting the DataSource property of the
DataGridView.
What I want to do is add a column to
the grid, called "Import," with a
checkbox that the user can check to
indicate whether or not each lead
should be imported.
Your Lead object stands well on its own, and you want to attach some metadata to it -- you don't want to create another Lead classification (i.e. the LeadWithImportCheckbox class).
So, the best approach in your case is to have a class like so:
public class LeadInfo
{
private Lead lead;
private bool shouldImport;
public LeadInfo(Lead lead)
{
this.lead = lead;
this.ShouldImport = false;
}
public bool ShouldImport
{
get { return shouldImport; }
set { shouldImport = value; }
}
}
This will scale well when you want to add more metadata to your list, like if you want to send yourself email reminders about them every week.
I've seen the correct solution listed so many times I feel like a heel posting it again, but the best way to approach this is to write a wrapper for the Lead object that includes the import flag.
If the properties of the Lead object don't appear in the GridView because you're databinding to the object, then write passthrough properties that mirror the Lead properties on the wrapper object.
The issue is that you want something displayed to the user that isn't an inherent part of the data model. The answer is to wrap the data before presenting it to the user so you can control what they see without changing the underlying model.
If you're concerned that the Lead object will change so many times in the future that changes to the wrapper will be cumbersome, you could look into dynamic code generation based on the Lead object that will automatically generate a wrapper object with the same fields as the Lead object plus the import flag. Though frankly, that's a lot more work than you'll probably need for something as straightforward as this.
As a quick and dirty solution, you can create your 'checkbox' object as a different object that contains an instance of Lead.
public GridLead {
public bool Import { get; set; }
public Lead Lead { get; set; }
}
This way you can easily add more 'grid' properties to this object, while still always retaining a reference to the Lead details without hardcoding property cloning into it.
Recommend you try modifying (upgrading) your imported lead objects.
Try starting with the examples here...
If your Lead class had a copy constructor (e.g. "Lead(Lead otherLead)"), LeadWithImportCheckbox would inherit that and you could just call the base Lead constructor in the LeadWithImportCheckbox constructor - hence no need for LeadWithImportCheckbox to be aware of the details of Lead.

Categories

Resources