TreeView and HierarchicalDataSource Control

Most of the examples on the web of using a hierarchical data source object against a tree view control use either the file system or xml files.  I wanted to be able to have the hierarchical data source connect to a custom object, which ultimately came from a sql table.

In the example posted below I've mocked up class containing my custom object which I connect to the hierarchical data source contol.  In real life this class would be replaced by a data access layer and possibly even a business layer.

Here's the Content.cs file that the web page references, it contains the code for the heirarchical data source control that is added to the page and the sample Content class which contains the mock data and access methods.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Collections;

namespace TreeviewHierarchicalDataSource
{
    /// 
    /// This class defines our data object, some example data and the methods used by
    /// the hierarchical data source control.  You could replace this class with your
    /// business logic and data layer classes.
    /// 
    public class Content
    {
        public int Id { get; set; }
        public string Subject { get; set; }
        public int ParentId { get; set; }
        private static List contentData = new List();

        /// 
        /// Populate some sample data.
        /// 
        private static void populateData()
        {
            contentData.Add(new Content(1, "United Kingdom", 0));
            contentData.Add(new Content(2, "Dorset", 1));
            contentData.Add(new Content(3, "Hampshire", 1));
            contentData.Add(new Content(4, "Somerset", 1));
            contentData.Add(new Content(5, "Poole", 2));
            contentData.Add(new Content(6, "Bournemouth", 2));
            contentData.Add(new Content(7, "USA", 0));
            contentData.Add(new Content(8, "Ohio", 7));
            contentData.Add(new Content(9, "Columbus", 8));
        }

        /// 
        /// 
        /// 
        /// Auto increment, primary key./// String containing the node text./// Id of the nodes parent.private Content(int Id, string Subject, int ParentId)
        {
            this.Id = Id;
            this.Subject = Subject;
            this.ParentId = ParentId;
        }

        /// 
        /// Returns a single Content object given it's Id.
        /// 
        /// Integer - key of the Content object to return./// Content object that mayches the given Id.
        public static Content GetById(int Id)
        {
            if (contentData.Count == 0) populateData();
            Content content = contentData.Find(item => item.Id == Id);
            return content;
        }

        /// 
        /// Checks if the Content object has any child nodes.
        /// 
        /// Integer - the Id of the Content object to check for children./// Boolean - True if the Content object has child nodes.
        public static bool HasChildren(int Id)
        {
            if (contentData.Count == 0) populateData();
            return contentData.FindAll(item => item.ParentId == Id).Count > 0;
        }

        /// 
        /// Returns a list of child nodes for the Content object.
        /// 
        /// Integer - Id of the Content object to fetch the children for./// List of Content objects.
        public static List GetChildren(int Id)
        {
            if (contentData.Count == 0) populateData();
            return contentData.FindAll(item => item.ParentId == Id);
        }
    }

    /// 
    /// User Control for the data source.
    /// 
    public class ContentDataSource : HierarchicalDataSourceControl
    {
        public ContentDataSource() : base() { }

        /// 
        /// Override of the GetHierarchyView method.
        /// 
        /// Integer - Id of the Content object the TreeView has requested./// HierarchicalDataSourceView.
        protected override HierarchicalDataSourceView GetHierarchicalView(string ContentId)
        {            
            return new ContentDataSourceView(ContentId);
        }
    }

    /// 
    /// ArrayList of ContentHierarchyData.
    /// 
    public class ContentHierarchy : ArrayList, IHierarchicalEnumerable
    {
        /// 
        /// Get an ArrayList of objects returned from the HeirarchyDataSourceView.
        /// 
        /// ArrayList of ContentHierarchyData/// ArrayList of ContentHierarchyData containing Content objects
        public IHierarchyData GetHierarchyData(object enumeratedItem)
        {
            return (ContentHierarchyData)enumeratedItem;            
        }
    }

    /// 
    /// ContentHierarchyData is the HierarchyData object, essentially our Content object.
    /// 
    public class ContentHierarchyData : IHierarchyData, IComparable
    {
        public Content _content;

        /// 
        /// Sets the hierarchy data object to our Content object.
        /// 
        /// Content - the content object that has been returned from the HierarchyDataSourceView.public ContentHierarchyData(Content content)
        {
            _content = content;
        }

        /// 
        /// Overrides the default ToString() which will return the node text value.
        /// 
        /// String - The string value that will becomes the node text value.
        public override string ToString()
        {
            return _content.Subject;
        }

        /// 
        /// Check to see if the node has any children.
        /// 
        public bool HasChildren
        {
            get
            {
                return Content.HasChildren(_content.Id);
            }
        }

        /// 
        /// Returns the current path of the nodes.
        /// 
        public string Path
        {
            get
            {
                return _content.Id.ToString();
            }
        }

        /// 
        /// Returns the Content object represented by the HierarchyData.  
        /// This is accesible in the TreeViewNode Data Bound event.
        /// 
        public object Item
        {
            get
            {
                return (object)_content;
            }
        }

        /// 
        /// Defines the underlying object type of the HierarchyData.
        /// 
        public string Type
        {
            get
            {
                return "Content";
            }
        }

        /// 
        /// Gets the child nodes of this object.
        /// 
        /// ContentHierarchy object of children.
        public IHierarchicalEnumerable GetChildren()
        {
            ContentHierarchy children = new ContentHierarchy();
            List contentList = Content.GetChildren(_content.Id);
            foreach (Content content in contentList)
            {
                ContentHierarchyData data = new ContentHierarchyData(content);
                children.Add(data);
            }            
            return children;
        }

        /// 
        /// Returns the parent node of this HierarchyData object.
        /// 
        /// 
        public IHierarchyData GetParent()
        {
            return new ContentHierarchyData(Content.GetById(_content.ParentId));
        }

        #region IComparable Members
        /// 
        /// Default compare method for our Content object.
        /// 
        /// HierarchyData object to compare./// Integer.
        public int CompareTo(object obj)
        {
            ContentHierarchyData content = (ContentHierarchyData)obj;
            return _content.Subject.CompareTo(content._content.Subject);
        }

        #endregion
    }

    /// 
    /// The DataSourceView that will be used to return the TreeView data.
    /// 
    public class ContentDataSourceView : HierarchicalDataSourceView
    {
        private int parentId;

        /// 
        /// Instantiate a new DataSourceView
        /// 
        /// Integer - Id of the Content to return, 0 represents the root of the TreeView.public ContentDataSourceView(string viewPath)
        {
            if (viewPath == string.Empty)
            {
                parentId = 0;
            }
            else
            {
                parentId = int.Parse(viewPath);
                Content content = Content.GetById(parentId);
            }
        }

        /// 
        /// Gets the list of Content objects for the given node.
        /// 
        /// ContentHierarchy collection of Content.
        public override IHierarchicalEnumerable Select()
        {
            ContentHierarchy contentHierarchy = new ContentHierarchy();
            IList children = Content.GetChildren(parentId);

            foreach (Content child in children)
            {
                int currentNode = contentHierarchy.Add(new ContentHierarchyData(child));
            }
            return contentHierarchy;
        }
    }
}
Here's the contents of the default.aspx page, I haven't added anything to the code behind. 
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TreeviewHierarchicalDataSource._Default" %>
<%@ Register Namespace="TreeviewHierarchicalDataSource" Assembly="TreeviewHierarchicalDataSource" TagPrefix="cds" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <cds:ContentDataSource ID="cdsContent" runat="server" />
        <asp:TreeView ID="tvContent" runat="server" DataSourceID="cdsContent" ExpandDepth="0"
         EnableClientScript="true" PopulateNodesFromClient="true" />
    </div>
    </form>
</body>
</html>

0 comments: