Introduction

Part II: Introduced a Controller class to instantiate objects that back tree nodes, as well as a PropertyGrid to edit properties of node instances.

Part III: Working with trees and DataSets. Not really relevant to this article.

Over the years, I've used the Xtree class extensively, making only minor modifications. In actual practice though, I noticed that I was writing a
Controller class to handle every backing instance class. So, for example, in a schema designer that I wrote, I have 25 Controller classes for handling
tables, views, fields, calculated fields, XML fields, matrices, etc. It gets quite burdensome to write a Controller class for every backing instance
class, and I've been wanting to write a generic Controller for a while. Turns out there's a significant implementation issue though: how does a generic
controller handle multiple child collections? For example, in the schema designer, a view can have fields that map to a table as well as calculated fields.

Because each child collection is strongly typed, we can't create a list of collections unless we treat the list type as "object" for the
underlying typed collection (does that make sense?).

An interface doesn't work, because List<SomeType> cannot be cast to List<IHasCollection>.

I was unwilling to use an interface in the collection definition. I want the code in the backing class to be expressed like List<TableFields>
rather than List<IHasCollection>. In other words, I don't want the fact that we're working with a generic tree controller
to affect the implementation of the backing class.

I didn't want to explicitly use Reflection.

Base classes to the backing class are not allowed. Only interfaces, because I don't want to restrict a backing class to being
derived from a class solely for the view presentation.

These issues appear in both the instantiation of tree nodes as well as the deserialization of an object graph to reconstruct the tree (the view). In fact,
the deserialization is particularly challenging because the object graph (essentially the model), once deserialized, must be iterated for each of the
entries at a particular level and recursed through for children of each entry to reconstruct the tree nodes.

Implementation Tradeoff

I wanted as minimal an impact on the backing class as possible. I'm not convinced I've achieved this, but it's close enough. The basic idea is
that the backing class must provide information on the collections that it maintains. This could be handled by reflecting through public properties
and looking for an attribute decorating the property that indicates that this is a serializable collection. Alternatively, the backing class itself could
create the list of collections and provide support for the property to access this list of collections. This is the approach that I ended up taking, because of its simplicity.

which requires that the backing class provides a Name property (so something can be populated in the tree node, this is not a huge issue because most things
already have names in these object graphs), and secondly, the list of collections. Note that this list is actually implemented as a dictionary. The key field
allows us to access a specific collection, and the value field, being dynamic, allows us to access methods on the collection itself, something
you can't do if the dictionary was defined like this: Dictionary<string, object> Collection { get; }. Yes, this hides explicit Reflection calls, but the code is sexier!

Implementation Example

So, for example, for another article that I'm working on, I want to be able to manage a graph of entities, relationships, and attributes. So, my top
level schema defines containers for these collections (and allows different named containers of a specific type):

This part of the code I think could be handled better--the string key has to match the collection type, so the code is prone to typo's and the
Collection could be initialized in a smarter way to avoid this issue. However, I'm considering the best approach for this, for example, a Collection
factory implemented through an extension method would remove this code completely, requiring only that the class implement the Collection property. I'm open to suggestions!

The Tree Definition

The definition of the tree, in XML, needs to mirror the backing object graph and uses Reflection to instantiate the instances at runtime. The instances
are added to the appropriate collection by the generic controller, which I'll show next, but first, let's take a quick tour of the XML that defines the tree
structure. Because I'm not finished with the article that I actually want to write, this implementation is simple, but hopefully illustrative.

The most relevant part of the above XML is the TypeName attribute, which defines the specific backing class type to instantiate. By itself, this
will create a simple tree with the required nodes (note the IsRequired attribute).

The Xtree class handles the popup menus, instantiation of the appropriate controller type, and initialization of the tree. The generic controller
handles the instantiation of the actual classes backing each tree node as well as adding and removing items from the appropriate collection. We'll look at the generic controller now.

The Generic Controller

This is a snippet of the salient pieces of the generic controller.

We have two constructors: the default constructor is used for instantiating a new backing class instance when the user selects "Add..."
something from the popup menu. The constructor taking the bool parameter is used to create the controller after deserializing an existing
graph, when the backing class instance has already been created.

The property GenericTypeName parses the type being managed by the collection, which represents a controller associated with the child
collection. Thus, the EntitiesContainer class has a collection of Entity objects, and when the user adds a node of this controller type, we
can determine the collection instance type by parsing the type name. This could also be done by inspecting the type Instance,
but I chose to work with the controller type instead.

The GenericTypeName is used as the key to index the dictionary of collections so that the instance that was created is added to the correct parent collection.

Because the dictionary value is of type dynamic, we can call the Add/Remove methods for strongly typed, generic instances,
letting System.Core handle the Reflection messiness.

I don't much care about the performance hit of using the dynamic keyword because this code is called only during the creation of nodes (interacting with
the user) and during deserialization, a one-time event when loading a graph.

Deserialization and Populating the Tree

The deserialization process requires populating the tree (the View in our Model-View-Controller implementation), which is an interesting mix of using
the dynamic and var keywords, since the process has no clue as to the actual types of the backing classes.

The algorithm starts with the root node, the schema instance.

It iterates through the dictionary of collections, finding any node definition that matches the type of the collection.

The items in the collection are iterated through, and...

Given the node definition, it instantiates the appropriate controller and initializes it with the deserialized backing class instance.

Lastly, for each item in the collection of items, the algorithm recurses, so that collections of the child can be processed, thus building the object graph in a tree representation.

Other Miscellaneous Things Going On

Because of the use of the dynamic keyword, Visual Studio 2010 with .NET 4.0 is required.

The names of classes and their organization with relation to the demo and the actual Xtree controller could be improved.

What is ROP? This is a small schema that I want to use for an article on Relationship Oriented Programming, which I haven't written yet?

Support for moving nodes around, while implemented by the Xtree control, is not currently supported by the generic controller. I'm working on that.

Conclusion

More work needs to be done, and you might be wondering why I'm submitting an article with code that isn't fully polished. Three reasons:

I'd like to get feedback from the community as to what "polished" actually means, in terms of interest, requirements, and so forth.

This is actually a precursor to a much larger project. I need to get some foundational aspects, such as this code, written so that the
article that is the end goal is not muddled with implementation details better described elsewhere.

This is actually more of an architecture article than a "use it right out of the box" solution.