前言 :
ASP.NET 3.5提供了,网页上常使用到的树状选单TreeView。
可是TreeView的数据系结,并没有支持ObjectDataSource。
这篇文章分享如何实做ObjectDataSource,来支持TreeView的数据系结功能。
分析设计 :
TreeView的DataSource只支持实做IHierarchicalDataSource接口的对象。
IHierarchicalDataSource这个看似简单的接口,里面却包含了很多子界面。
将整个IHierarchicalDataSource拆解开来,并且经过分析设计之后的对象图如下 :
每个对象的职责就不赘述了,大多为符合IHierarchicalDataSource所规范的功能而实做。
整个设计比较特别的只有IHierarchicalObjectDataFactory接口。
这个接口其实是多余的,只是为了不让HierarchicalObjectDataEnumerable及HierarchicalObjectData有循环相依。
执行结果 :
使用范例 :
<%@ 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"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:TreeView ID="TreeView1" runat="server"> <DataBindings> <asp:TreeNodeBinding TextField="Name" ValueField="Id" /> </DataBindings> </asp:TreeView> </div> </form> </body> </html>
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; } } } }
实作 :
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; } } }