ObjectHierarchicalDataSource sample code
Some time ago, I released
the source code
for my
CompositeHierarchicalDataSource
that answers the problem that there is no out-of-the-box way
to declaratively bind a
Menu
or
TreeView
to a database in
ASP.NET
2.0: only XML and SiteMap have hierarchical data sources.
You could still do it programmatically, but declaratively is
so much more fun...
It's also noticeable that while there is a very handy
ObjectDataSource
control in
ASP.NET
2.0, it does not have a hierarchical equivalent. Basically,
the idea would be to declaratively bind a hierarchical
control to an arbitrary object graph.
Here's how you can use it:
<my:ObjectHierarchicalDataSource runat=server ID=ObjectInstance1 TypeName="Categories">
<SelectMethods>
<my:SelectMethod TypeName="CatsCategory" Method="GetCats" />
<my:SelectMethod TypeName="Cat" PropertyNames="Color,Gender" />
</SelectMethods>
</my:ObjectHierarchicalDataSource>
<asp:TreeView Runat=Server ID=categoryTree DataSourceID=ObjectInstance1
ExpandDepth=0 PopulateNodesFromClient=true>
<DataBindings>
<asp:TreeNodeBinding TextField="#" ValueField="#" ToolTipField="#" PopulateOnDemand=true />
<asp:TreeNodeBinding DataMember="CatsCategory" TextField="Name" ValueField="Name"
ToolTipField="Name" PopulateOnDemand=true />
<asp:TreeNodeBinding DataMember="Cat" TextField="Name" ValueField="Name"
ToolTipField="Description" PopulateOnDemand=true />
<asp:TreeNodeBinding DataMember="Color" FormatString="Color: {0}" PopulateOnDemand="true"
SelectAction="None" TextField="Name" ValueField="Name" ToolTipField="#" />
<asp:TreeNodeBinding DataMember="Gender" PopulateOnDemand="true" SelectAction="None"
TextField="#" ValueField="#" ToolTipField="#" />
</DataBindings>
</asp:TreeView>
We're giving the data source a type name that will be the
root of the graph. This type must have a default
parameterless constructor. We don't have to specify a select
method on this type because it is enumerable and if you
don't specify a select method on an enumerable object, the
data source will just assume the enumeration is describing
the next level.
CatsCategory, on the other hand, is not directly enumerable,
so we must provide the name of a method that will get us the
enumeration of objects in the next level: GetCats().
Finally, the third way to describe the children of an object
is a list of properties. That's what we're doing for Cat:
its children are simply the Color and Gender properties.
Now, all there is to do is to tell the
TreeView that will consume the data source (
Menu
would work in just the same way) how to bind itself to the
data. We're using the DataBindings property for that, and
for each type (DataMember) that will be present in the
object graph, we tell the tree which properties to use for
the text, value, tooltip or format string. We also have a
default binding for types that we don't want to special-case
(such as string).
The "#" bindings express that the "field" we want is not a
property but the return value of the ToString() method.
Notice how the tree will populate on demand even with this
exotic data source (which could be an absolute must if your
data source is infinitely deep).
One of the most interesting parts of the implementation is
the way we wrap the objects of the graph in objects that
know how to expose their children in the hierarchy in a
standardized way but still show all the properties of the
wrapped object under Reflection (which the data source of
course uses intensively, this is the price of extreme
flexibility and ease of declaration). The way it does that
is by implementing
ICustomTypeDescriptor, which is an amazingly useful interface. Have a look at
the ObjectHierarchyData source code if you want more
details.
The source code and sample page (which will auto-compile)
can be found here: