ASP.net(C#) Custom Hierarchical Datasource

Source Code

Some time ago I wrote a post on populating a treeview control within windows forms. The same method (slightly altered) can be used to populate a treeview control in asp.net, even though its not quite what microsoft put in place with regards to binding hierarchical data.

One of these mechanisms (that they put in place) are the XmlDataSource control - which we can use to bind hierarchical data to a treeview control, lets have a quick look.

The xml we're going to use will look something like this:

 
<?xml version="1.0" encoding="utf-8" ?>
<nodes title="Programming Languages">
  <node title="Microsoft">
    <node title="C#">
      <node title="LinQ"></node>
    </node>
    <node title="VB.net"></node>
  </node>
  <node title="Open Source">
    <node title="Python"></node>
    <node title="Ruby"></node>
    <node title="PHP">
      <node title="5.2"></node>
      <node title="4.4"></node>
    </node>
    <node title="Perl"></node>
    <node title="Java"></node>
  </node>
</nodes>
 

The ASP.net markup will look something like this:
 
<asp:TreeView ID="tree" runat="server" DataSourceID="source" ExpandDepth="0">
    <DataBindings>
        <asp:TreeNodeBinding DataMember="node" TextField="title" />
        <asp:TreeNodeBinding DataMember="nodes" TextField="title" />
    </DataBindings>
</asp:TreeView>
<asp:XmlDataSource ID="source" runat="server" DataFile="~/xml/programming.xml">
</asp:XmlDataSource>
 

Obviously (looking at our TreeNodeBinding node) DataMember refers to the xml tag e.g. node & nodes, TextField refers to the attributes within the node.

This is basically a flat file approach, but what if we need to bind using SQL for example? We can always serialize our dataset/table to xml and bind it to an XmlDataSource - or alternatively one can bind using a quick and dirty method like this.

But lets make it more interesting and create our own hierarchical source, we can achieve this by using the IHierarchyData, IHierarchicalEnumerable interfaces and HierarchicalDataSourceView, HierarchicalDataSourceControl classes.

However instead of using the HierarchicalDataSourceControl class, I am going to inherit from the ObjectDataSource class and create a HierarchicalObjectDataSource:
 
public class HierarchicalObjectDataSource : ObjectDataSource, IHierarchicalDataSource
{
    public event EventHandler DataSourceChanged;
 
    public String DataTextField
    {
        get;
        set;
    }
 
    public String DataValueField
    {
        get;
        set;
    }
 
    public String DataParentField
    {
        get;
        set;
    }
 
    public HierarchicalDataSourceView GetHierarchicalView(string viewPath)
    {
        return new HierarchicalObjectDataSourceView(this, viewPath);
    }
}
 

The hierarchical data item will look something like this:
 
public class Hierarchical : IHierarchyData
{
    public HierarchicalCollection source
    {
        get;
        set;
    }
 
    public Int32 Value
    {
        get;
        set;
    }
 
    public Int32 ParentID
    {
        get;
        set;
    }
 
    public String Text
    {
        get;
        set;
    }
 
    private Hierarchical()
    {
    }
 
    public Hierarchical(int value, int parentID, string text)
    {
        this.Value = value;
        this.ParentID = parentID;
        this.Text = text;
    }
 
    public IHierarchicalEnumerable GetChildren()
    {
        HierarchicalCollection children = new HierarchicalCollection();
 
        foreach (Hierarchical Hierarchical in this.source)
        {
            if (Hierarchical.ParentID == this.Value)
            {
                children.Add(Hierarchical);
            }
        }
        return children;
    }
 
    public IHierarchyData GetParent()
    {
        foreach (Hierarchical Hierarchical in this.source)
        {
            if (Hierarchical.Value == this.ParentID)
                return Hierarchical;
        }
        return null;
    }
 
    public bool HasChildren
    {
        get
        {
            HierarchicalCollection children = GetChildren() as HierarchicalCollection;
            return children.Count > 0;
        }
    }
 
    public object Item
    {
        get { return this; }
    }
 
    public string Path
    {
        get { return this.Value.ToString(); }
    }
 
    public string Type
    {
        get { return this.GetType().ToString(); }
    }
 
    public override string ToString()
    {
        return this.Text;
    }
}
 

The collection used for binding will look like this:
 
public class HierarchicalCollection : List<Hierarchical>, IHierarchicalEnumerable
{
    public HierarchicalCollection()
        : base()
    {
    }
 
    public IHierarchyData GetHierarchyData(object enumeratedItem)
    {
        return enumeratedItem as IHierarchyData;
    }
}
 

The class used for collecting data for the HierarchicalObjectDataSource:
 
public class HierarchicalObjectDataSourceView : HierarchicalDataSourceView
{
    private string _viewPath;
    private HierarchicalCollection _source = new HierarchicalCollection();
 
    public HierarchicalObjectDataSourceView(HierarchicalObjectDataSource source, string viewPath)
    {
        _viewPath = viewPath;
        CreateCollection(source);
    }
 
    private void CreateCollection(HierarchicalObjectDataSource source)
    {
        IEnumerable data = source.Select();
        if (data != null)
        {
            foreach (Object dataItem in data)
            {
                Int32 value = Convert.ToInt32(DataBinder.GetPropertyValue(dataItem, source.DataValueField, null));
                Int32 parentID = Convert.ToInt32(DataBinder.GetPropertyValue(dataItem, source.DataParentField, null));
                String text = DataBinder.GetPropertyValue(dataItem, source.DataTextField, null);
                _source.Add(new Hierarchical(value, parentID, text));
            }
        }
    }
 
    public override IHierarchicalEnumerable Select()
    {
        HierarchicalCollection collection = new HierarchicalCollection();
        Int32 ParentId = (!String.IsNullOrEmpty(_viewPath)) ? Convert.ToInt32(_viewPath) : 0;
 
        foreach (Hierarchical Hierarchical in this._source)
        {
            if (Hierarchical.ParentID == ParentId)
            {
                Hierarchical.source = _source;
                collection.Add(Hierarchical);
            }
        }
        return collection;
    }
}
 

Eventually we simply bind our object (which is SQL/XML whatever based) like this:
 
<asp:TreeView ID="tree" runat="server" DataSourceID="source" ExpandDepth="0">
</asp:TreeView>
<asp:HierarchicalObjectDataSource ID="source" runat="server" SelectMethod="GetData"
	TypeName="CSTruter.com.Data" DataParentField="ParentID" DataValueField="Value"
	DataTextField="Text" />
 

Be sure to download the source code.


Post/View comments
 

Progressbar issue .net Windows 7/Vista

I noticed a rather annoying issue while working with progressbars in .net & Windows 7/Vista.

The progressbar in my application worked perfectly on Windows XP, but under Windows 7/Vista I found the bar lagging behind the actual percentages passed to the control (The bar eventually catches up).

After some googling I found that this is due to the Aero theme - apparently thanks to delays generated by the animation the theme adds to progressbars (If you disable the Areo theme, everything works fine).

I came up with the following workaround:

 
using System;
using System.Windows.Forms;
using System.Drawing;
 
class myProgressBar : ProgressBar
{
    public myProgressBar()
    {
        this.SetStyle(ControlStyles.UserPaint |
            ControlStyles.AllPaintingInWmPaint |
            ControlStyles.OptimizedDoubleBuffer, true);
    }
 
    protected override void OnPaint(PaintEventArgs e)
    {
        ProgressBarRenderer.DrawHorizontalBar(e.Graphics, this.ClientRectangle);
        Rectangle bounds = new Rectangle
        {
            X = this.ClientRectangle.X,
            Y = this.ClientRectangle.Y,
            Width = (Int32)Math.Floor(((double)this.Value / this.Maximum) * this.ClientRectangle.Width),
            Height = this.ClientRectangle.Height
        };
        bounds.Inflate(-1, -1);
        ProgressBarRenderer.DrawHorizontalChunks(e.Graphics, bounds);
    }
}
 

We simply inherit from the existing ProgressBar class and override its OnPaint method, visually the progressbar will look the same as the themed one, unfortunately this does however disable the animation effect - but at least the bar keeps up.


Post/View comments
 
First 6 7 8 9 10 11 12 13 14 15 Last / 42 Pages (83 Entries)

Latest Posts

Top 5 posts

Simple WYSIWYG Editor


Creating a WYSIWYG textbox for your website is actually quite simple.
2007-02-01 12:00:00

Moving items between listboxes in ASP.net/PHP example


Move items between two listboxes in ASP.net(C#, VB.NET) and PHP
2008-06-12 17:07:43

Cross Browser Issues: Firefox Word Wrapping


Firefox word wrapping issues
2008-06-09 09:51:21

Populate a TreeView Control C#


Populate a TreeView control in a windows application.
2009-08-27 16:01:03

What time will bring



2007-02-22 12:00:00