Introduction
Once upon a time, there was a project where testers and advanced users had to have a tool to watch complex data. That's why I made a tool inspired by the Visual Studio QuickWatch.
It has some extra features, in addition:
- It displays only fields/properties (i.e. members) a developer wants to be seen by users
- It allows to assign user-friendly names to displayed members
- It allows to save a selected sub-tree to a file
Builder
A few months ago, I was asked to display data which consists of over twenty classes; each class has about ten different members. It was just too complex to make a one-purpose piece of code to display it. Especially it was almost impossible to maintain it because the data structure changes.
So, how to automate the process? I use attributes to tag members that users should see:
TreeLeafNodeAttribute
– Defines a member as a tree leaf, it cannot contain any nodes or leafs. Usually used for strings, numbers and dates. TreeInternalNode
– Defines a member as an internal tree node, it contains nodes or leafs. Usually used for nested structures and collections.
Example:
public class Catalog
{
[TreeLeafNode]
public string name;
[TreeInternalNode]
public Source source;
[TreeInternalNode]
public List<Cd> cds;
}
Such an object is passed to the builder which is divided into three layers:
- A base layer – Implements the logic of building the tree
- A middle layer – Renders the tree content to a concrete output device
- A top layer – Makes the builder type-safe and handles special cases
The builder walks through all tagged members of a passed object recursively. To get the structure of tagged members, the builder uses the TreeStructure
class to extract the structure using System.Reflection
. The result is cached in a static
hash table because it does not change during the application run. It's re-used in all later builder runs.
The builder distinguishes between two types of internal nodes: collections and others. If the item tagged with the TreeInternalNode
attribute implements IEnumerable
interface, it's displayed as a collection (see the cds node in the picture above) otherwise it's displayed as another internal node (see the source node). If the item with the IEnumerable
interface implements the ICollection
interface or is an Array
in addition the number of items is displayed as well. The text of every displayed internal node is obtained by calling the ToString
method on the node object (the same way like in the Quick Watch in the Visual Studio).
The BuildTitle
method formats the appearance of leaf nodes. Strings are displayed in apostrophes, numbers without, decimal numbers are displayed with a precision at two decimal places. Dates are displayed as dates. There is a special NullDateTime
property and if the date has the same value like it, it's displayed as null
.
Names of all members can be translated to a user-friendly format if the TranslateName
method is overriden. For example the member name TrxNum
can be translated as Transaction number.
Data Input and Output
The Catalog content is deserialized from an XML data file because it's just the simplest way to populate a structure with some data. In real applications, the structure is usually populated from a database or another data source.
In this sample project, there is an additional functionality in the middle layer of the builder. It sets the Tag
property of the TreeNode
in every internal node and list item. If the node is selected, the user can serialize its whole content to a file which can be used later for reporting bugs or similar purposes.
In fact, the middle layer can have various implementations. In the sample application, I use the standard System.Windows.Forms.TreeView
. But our company uses our own visual components which are based on DataSet
s. The middle layer does not need to have a visual output at all. The output can be saved to a database or to an XML file.
Related Stuff
History
- 26th November, 2007: Initial post
I started to write my first programs more than 15 years ago on C64, later on Amiga computers. I'm from a small Czech town but after I finished my university I moved to Prague where a worked as a developer/consultant for about five years. Now I've been for more than a year in London, looking for some fun and experience.
Apart my work I like travelling, dancing and cats.