[ASP.NET] : HierarchicalObjectDataSource
前言 :
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; }
}
}
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。