Preface:
ASP. NET 3.5 provides a tree menu Treeview that is commonly used on webpages.
However, the data of the Treeview is tied and objectdatasource is not supported.
This article shares how to implement objectdatasource to support the data binding function of Treeview.
Analysis Design:
The datasource of the Treeview only supports objects that are actually the ihierarchicaldatasource interface.
Ihierarchicaldatasource, a seemingly simple interface, contains many subinterfaces.
The entire ihierarchicaldatasource is disassembled, and the object diagram after analysis and design is as follows:
The responsibilities of each object are not described in detail. Most of them are implemented in compliance with ihierarchicaldatasource specifications.
Only the ihierarchicalobjectdatafactory interface is particularly designed.
This interface is actually redundant, just to prevent hierarchicalobjectdataenumerable and hierarchicalobjectdata from being cyclically dependent.
Execution result:
Example:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CLK.Web.UI.WebApplication._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
namespace CLK.Web.UI.WebApplication{ public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (this.IsPostBack == false) { List<IHierarchicalObject> demoDataList = new List<IHierarchicalObject>(); demoDataList.Add(new DemoData(null, "AAA", "Id_AAA", "Name_AAA")); demoDataList.Add(new DemoData(null, "BBB", "Id_BBB", "Name_BBB")); demoDataList.Add(new DemoData(null, "CCC", "Id_CCC", "Name_CCC")); demoDataList.Add(new DemoData("BBB", "DDD", "Id_DDD", "Name_DDD")); demoDataList.Add(new DemoData("DDD", "EEE", "Id_EEE", "Name_EEE")); demoDataList.Add(new DemoData(null, "FFF", "Id_FFF", "Name_FFF")); HierarchicalObjectDataSource dataSource = new HierarchicalObjectDataSource(demoDataList); TreeView1.DataSource = dataSource; TreeView1.DataBind(); } } } public class DemoData : IHierarchicalObject { // Properties private readonly string _parentPath; private readonly string _path; private readonly string _id; private readonly string _name; // Construction public DemoData(string parentPath, string path, string id, string name) { #region Require if (path == null) throw new ArgumentNullException(); if (string.IsNullOrEmpty(id) == true) throw new ArgumentNullException(); if (string.IsNullOrEmpty(name) == true) throw new ArgumentNullException(); #endregion _parentPath = parentPath; _path = path; _id = id; _name = name; } // Member public string ParentPath { get { return _parentPath; } } public string Path { get { return _path; } } public string Id { get { return _id; } } public string Name { get { return _name; } } }}
Implementation:
namespace CLK.Web.UI{ public class HierarchicalObjectDataSource : IHierarchicalDataSource { // Properties private readonly IEnumerable<IHierarchicalObject> _objectEnumerable; // Construction public HierarchicalObjectDataSource(IEnumerable<IHierarchicalObject> objectEnumerable) { #region Require if (objectEnumerable == null) throw new ArgumentNullException(); #endregion _objectEnumerable = objectEnumerable; } // Member public HierarchicalDataSourceView GetHierarchicalView(string viewPath) { return new HierarchicalObjectDataSourceView(_objectEnumerable, viewPath); } // Event public event EventHandler DataSourceChanged; protected void OnDataSourceChanged(object sender, EventArgs e) { if (this.DataSourceChanged != null) { this.DataSourceChanged(sender, e); } } } public class HierarchicalObjectDataSourceView : HierarchicalDataSourceView { // Properties private readonly IEnumerable<IHierarchicalObject> _objectEnumerable; private readonly IHierarchicalObject _currentObject; private readonly IHierarchicalObjectDataFactory _objectFactory; // Construction public HierarchicalObjectDataSourceView(IEnumerable<IHierarchicalObject> objectEnumerable, string path) { #region Require if (objectEnumerable == null) throw new ArgumentNullException(); if (path == null) throw new ArgumentNullException(); #endregion _objectFactory = new HierarchicalObjectDataFactory(); _objectEnumerable = objectEnumerable; _currentObject = null; foreach (IHierarchicalObject currentObject in _objectEnumerable) { if (currentObject.Path == path) { _currentObject = currentObject; break; } } } // Member public override IHierarchicalEnumerable Select() { return _objectFactory.CreateHierarchicalEnumerable(_objectEnumerable, _currentObject); } } public class HierarchicalObjectDataFactory : IHierarchicalObjectDataFactory { // Member public IHierarchicalEnumerable CreateHierarchicalEnumerable(IEnumerable<IHierarchicalObject> objectEnumerable, IHierarchicalObject currentObject) { return new HierarchicalObjectDataEnumerable(objectEnumerable, currentObject, this); } public IHierarchyData CreateHierarchyData(IEnumerable<IHierarchicalObject> objectEnumerable, IHierarchicalObject currentObject) { return new HierarchicalObjectData(objectEnumerable, currentObject, this); } } public interface IHierarchicalObjectDataFactory { IHierarchicalEnumerable CreateHierarchicalEnumerable(IEnumerable<IHierarchicalObject> objectEnumerable, IHierarchicalObject currentObject); IHierarchyData CreateHierarchyData(IEnumerable<IHierarchicalObject> objectEnumerable, IHierarchicalObject currentObject); } public class HierarchicalObjectDataEnumerable : IHierarchicalEnumerable { // Properties private readonly IEnumerable<IHierarchicalObject> _objectEnumerable; private readonly IHierarchicalObject _currentObject; private readonly IHierarchicalObjectDataFactory _objectFactory; // Construction public HierarchicalObjectDataEnumerable(IEnumerable<IHierarchicalObject> objectEnumerable, IHierarchicalObject currentObject, IHierarchicalObjectDataFactory objectFactory) { #region Require if (objectEnumerable == null) throw new ArgumentNullException(); //if (currentObject == null) throw new ArgumentNullException(); if (objectFactory == null) throw new ArgumentNullException(); #endregion _objectEnumerable = objectEnumerable; _currentObject = currentObject; _objectFactory = objectFactory; } // Member public IHierarchyData GetHierarchyData(object enumeratedItem) { #region Require if (enumeratedItem == null) throw new ArgumentNullException(); #endregion if (enumeratedItem is IHierarchicalObject) { return _objectFactory.CreateHierarchyData(_objectEnumerable, enumeratedItem as IHierarchicalObject); } return null; } public System.Collections.IEnumerator GetEnumerator() { return new HierarchicalObjectDataEnumerator(_objectEnumerable.GetEnumerator(), _currentObject); } } public class HierarchicalObjectDataEnumerator : IEnumerator<IHierarchicalObject> { // Properties private readonly IEnumerator<IHierarchicalObject> _objectEnumerator; private readonly IHierarchicalObject _currentObject; private IHierarchicalObject _currentChildrenObject = null; // Construction public HierarchicalObjectDataEnumerator(IEnumerator<IHierarchicalObject> objectEnumerator, IHierarchicalObject currentObject) { #region Require if (objectEnumerator == null) throw new ArgumentNullException(); //if (currentObject == null) throw new ArgumentNullException(); #endregion _objectEnumerator = objectEnumerator; _currentObject = currentObject; } public void Dispose() { _objectEnumerator.Dispose(); } // Member public IHierarchicalObject Current { get { return _currentChildrenObject; } } object System.Collections.IEnumerator.Current { get { return this.Current; } } public bool MoveNext() { while (_objectEnumerator.MoveNext() == true) { if (_currentObject != null) { if (_objectEnumerator.Current.ParentPath == _currentObject.Path) { _currentChildrenObject = _objectEnumerator.Current; return true; } } else { if (_objectEnumerator.Current.ParentPath == null) { _currentChildrenObject = _objectEnumerator.Current; return true; } } } _currentChildrenObject = null; return false; } public void Reset() { _objectEnumerator.Reset(); _currentChildrenObject = null; } } public class HierarchicalObjectData : IHierarchyData { // Properties private readonly IEnumerable<IHierarchicalObject> _objectEnumerable; private readonly IHierarchicalObject _currentObject; private readonly IHierarchicalObjectDataFactory _objectFactory; // Construction public HierarchicalObjectData(IEnumerable<IHierarchicalObject> objectEnumerable, IHierarchicalObject currentObject, IHierarchicalObjectDataFactory objectFactory) { #region Require if (objectEnumerable == null) throw new ArgumentNullException(); if (currentObject == null) throw new ArgumentNullException(); if (objectFactory == null) throw new ArgumentNullException(); #endregion _objectEnumerable = objectEnumerable; _currentObject = currentObject; _objectFactory = objectFactory; } // Member public IHierarchicalEnumerable GetChildren() { return _objectFactory.CreateHierarchicalEnumerable(_objectEnumerable, _currentObject); } public IHierarchyData GetParent() { if (_currentObject.ParentPath != null) { foreach (IHierarchicalObject parentObject in _objectEnumerable) { if (parentObject.Path == _currentObject.ParentPath) { return _objectFactory.CreateHierarchyData(_objectEnumerable, parentObject); } } } return null; } public bool HasChildren { get { foreach (object childrenObject in this.GetChildren()) { return true; } return false; } } public object Item { get { return _objectFactory.CreateHierarchyData(_objectEnumerable, _currentObject); } } public string Path { get { return _currentObject.Path; } } public string Type { get { return _currentObject.GetType().ToString(); } } } public interface IHierarchicalObject { string Path { get; } string ParentPath { get; } }}