什么是IHierarchicalDataSource接口呢?该接口是Asp.Net2.0中心定义的一个专用于描述一类有继承体系关系的数据的数据源,它可以作为TreeView,SiteMap等这些能够描述继承体系结构的控件的数据源。因为很多情况下,我们需要在TreeView这样的控件中显示我们的自定义数据,不适用数据邦定的情况下,我们当然也能手工来编码,坦率地说,代码也不太难写,几个递归函数而已,但是如果很多页面都用treeview的话是不是该抽象一下呢?如果使用IHierarchicalDataSource数据源,不写一条代码,就能将数据邦定到控件。本文给出的就是一个IHierarchicalDataSource接口的通用简单实现。
什么是IHierarchicalDataSource接口呢?该接口是Asp.Net2.0中心定义的一个专用于描述一类有继承体系关系的数据的数据源,它可以作为TreeView,SiteMap等这些能够描述继承体系结构的控件的数据源。因为很多情况下,我们需要在TreeView这样的控件中显示我们的自定义数据,不适用数据邦定的情况下,我们当然也能手工来编码,坦率地说,代码也不太难写,几个递归函数而已,但是如果很多页面都用treeview的话是不是该抽象一下呢?如果使用IHierarchicalDataSource数据源,不写一条代码,就能将数据邦定到控件。本文给出的就是一个IHierarchicalDataSource接口的通用简单实现。
设计目标
我们的设计目标很明确,就是一个IHierarchicalDataSource实现,该实现类可以接受一组用户获取继承体系中的数据的函数的delegate作为构造函数的参数。
基本的delegate定义和构造函数定义如下:
//delegates
public delegate object[] GetRootItemsHandler(); //获取顶层元素
public delegate object[] GetNonRootItemsHandler(string parentId); //获取非顶层元素
public delegate object GetItemHandler(string id); //通过id获取元素
public delegate string GetItemIdHandler(object obj); //通过元素获取id
//constructor
public SimpleHierarchicalDataSource(GetItemHandler itemHandler, GetItemIdHandler itemIdHandler, GetRootItemsHandler rootHandler, GetNonRootItemsHandler nonRootHandler);
简单说明一下为什么定义了这四个delegate。
首先出于通用型,元素的id使用了string类型,因为不是所有元素的id都是数值。其次,GetRootItemsHandler用于获取继承体系顶层的元素,也就是没有父元素的元素。GetNonRootItemsHandler和GetRootItemsHandler返回元素类型可以是不一样的。例如,Root Item可以是Project,而Nonroot Item可以是和project相关的requirement;当然,两者返回的元素也可以一样,比如多级产品分类。
而后两个delegate则分别用来获得元素及其id。这里的id除了作为获取元素的标识,还有一些特殊的用处。以TreeView为例,它的FindNode函数可以通过一个查询字符串来查询继承体系中的元素。例如:/1/12/45标识继承体系中id为1的元素下的id为12的元素下的id为45的元素。
代码实现
好了,闲话少说,看看实现代码吧,还是要实现好几个接口和抽象类的~~
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Ilungasoft.Framework.Web.UI
{
public delegate object[] GetRootItemsHandler();
public delegate object[] GetNonRootItemsHandler(string parentId);
public delegate object GetItemHandler(string id);
public delegate string GetItemIdHandler(object obj);
public class HierarchyData : System.Web.UI.IHierarchyData
{
Private Members#region Private Members
private object obj;
private GetItemIdHandler itemIdHandler;
private GetNonRootItemsHandler handler;
private object parentObj;
private string parentPath;
private object[] childObjs;
#endregion
Constructors#region Constructors
public HierarchyData(object parentObj, string parentPath, object obj, GetItemIdHandler itemIdHandler, GetNonRootItemsHandler handler)
{
this.parentObj = parentObj;
this.parentPath = parentPath;
this.obj = obj;
this.itemIdHandler = itemIdHandler;
this.handler = handler;
childObjs = handler(itemIdHandler(obj));
}
#endregion
IHierarchyData Members#region IHierarchyData Members
public System.Web.UI.IHierarchicalEnumerable GetChildren()
{
return new HierarchicalCollection(parentObj, parentPath, handler(itemIdHandler(obj)), itemIdHandler, handler);
}
public System.Web.UI.IHierarchyData GetParent()
{
string[] pathSplits = parentPath.Trim(' ', '/').Split('/');
if (pathSplits != null && pathSplits.Length > 0)
{
if (pathSplits.Length > 1)
{
string parentParentPath = "";
for (int i = 0; i < pathSplits.Length - 1; i++)
{
parentParentPath = parentParentPath + "/" + pathSplits[i];
}
return new HierarchyData(itemIdHandler(pathSplits[pathSplits.Length - 2]), parentParentPath, parentObj, itemIdHandler, handler);
}
}
return null;
}
public bool HasChildren
{
get
{
return childObjs != null && childObjs.Length > 0;
}
}
public object Item
{
get
{
return obj;
}
}
public string Path
{
get
{
return string.Format("{0}/{1}", parentPath, itemIdHandler(obj));
}
}
public string Type
{
get
{
return obj.GetType().ToString();
}
}
#endregion
}
public class HierarchicalCollection : System.Web.UI.IHierarchicalEnumerable
{
Private Members#region Private Members
private object parentObj;
private string parentPath;
private object[] objs;
private GetItemIdHandler itemIdHandler;
private GetNonRootItemsHandler nonRootHandler;
#endregion
Constructors#region Constructors
public HierarchicalCollection(object parentObj, string parentPath, object[] objs, GetItemIdHandler itemIdHandler, GetNonRootItemsHandler nonRootHandler)
{
this.parentObj = parentObj;
this.parentPath = parentPath;
this.objs = objs;
this.itemIdHandler = itemIdHandler;
this.nonRootHandler = nonRootHandler;
}
#endregion
IHierarchicalEnumerable Members#region IHierarchicalEnumerable Members
public System.Web.UI.IHierarchyData GetHierarchyData(object enumeratedItem)
{
return new HierarchyData(parentObj, parentPath, enumeratedItem, itemIdHandler, nonRootHandler);
}
#endregion
IEnumerable Members#region IEnumerable Members
public System.Collections.IEnumerator GetEnumerator()
{
return objs.GetEnumerator();
}
#endregion
}
public class RootHierarchicalDataSourceView : System.Web.UI.HierarchicalDataSourceView
{
Private Members#region Private Members
private GetItemIdHandler itemIdHandler;
private GetRootItemsHandler rootHandler;
private GetNonRootItemsHandler nonRootHandler;
#endregion
Constructors#region Constructors
public RootHierarchicalDataSourceView(GetItemIdHandler itemIdHandler, GetRootItemsHandler rootHandler, GetNonRootItemsHandler nonRootHandler)
{
this.itemIdHandler = itemIdHandler;
this.rootHandler = rootHandler;
this.nonRootHandler = nonRootHandler;
}
#endregion
Overriden Members#region Overriden Members
public override System.Web.UI.IHierarchicalEnumerable Select()
{
return new HierarchicalCollection(null, null, rootHandler(), itemIdHandler, nonRootHandler);
}
#endregion
}
public class NonRootHierarchicalDataSourceView : System.Web.UI.HierarchicalDataSourceView
{
Private Members#region Private Members
private string parentPath;
private GetItemHandler itemHandler;
private GetItemIdHandler itemIdHandler;
private GetNonRootItemsHandler nonRootHandler;
private object parentObj;
#endregion
Constructors#region Constructors
public NonRootHierarchicalDataSourceView(string parentPath, GetItemHandler itemHandler, GetItemIdHandler itemIdHandler, GetNonRootItemsHandler nonRootHandler)
{
this.parentPath = parentPath;
this.itemHandler = itemHandler;
this.itemIdHandler = itemIdHandler;
this.nonRootHandler = nonRootHandler;
string[] pathSplits = parentPath.Trim(' ', '/').Split('/');
if (pathSplits != null && pathSplits.Length > 0)
{
parentObj = itemHandler(pathSplits[pathSplits.Length - 1]);
}
}
#endregion
Overriden Members#region Overriden Members
public override System.Web.UI.IHierarchicalEnumerable Select()
{
return new HierarchicalCollection(parentObj, parentPath, nonRootHandler(itemIdHandler(parentObj)), itemIdHandler, nonRootHandler);
}
#endregion
}
public class SimpleHierarchicalDataSource : System.Web.UI.IHierarchicalDataSource
{
Private Members#region Private Members
private GetItemHandler itemHandler;
private GetItemIdHandler itemIdHandler;
private GetRootItemsHandler rootHandler;
private GetNonRootItemsHandler nonRootHandler;
#endregion
Constructors#region Constructors
public SimpleHierarchicalDataSource(GetItemHandler itemHandler, GetItemIdHandler itemIdHandler, GetRootItemsHandler rootHandler, GetNonRootItemsHandler nonRootHandler)
{
this.itemHandler = itemHandler;
this.itemIdHandler = itemIdHandler;
this.rootHandler = rootHandler;
this.nonRootHandler = nonRootHandler;
if (DataSourceChanged != null)
{
//null is ok
}
}
#endregion
IHierarchicalDataSource Members#region IHierarchicalDataSource Members
public event EventHandler DataSourceChanged;
public System.Web.UI.HierarchicalDataSourceView GetHierarchicalView(string viewPath)
{
if (viewPath == null || (viewPath != null && (viewPath.Trim().Equals("/") || viewPath.Trim().Equals(""))))
{
return new RootHierarchicalDataSourceView(itemIdHandler, rootHandler, nonRootHandler);
}
else
{
return new NonRootHierarchicalDataSourceView(viewPath, itemHandler, itemIdHandler, nonRootHandler);
}
}
#endregion
Sample Client Handlers#region Sample Client Handlers
//public class EntityWrapper<T>
//{
// public T Target;
// public EntityWrapper(T obj)
// {
// Target = obj;
// }
// public override string ToString()
// {
// if (typeof(T) == typeof(Entity.Project))
// {
// return StrongTyped<Entity.Project>(Target).Title;
// }
// else
// {
// return StrongTyped<Entity.Requirement>(Target).Title;
// }
// }
//}
//private object[] GetProjects()
//{
// Entity.Project[] projects = Facade.SelectAll<Entity.Project>();
// if (projects != null && projects.Length > 0)
// {
// object[] objs = new object[projects.Length];
// for (int i = 0; i < objs.Length; i++)
// {
// objs[i] = new EntityWrapper<Entity.Project>(projects[i]);
// }
// return objs;
// }
// return null;
//}
//private object[] GetRequirements(string parentId)
//{
// Entity.Requirement[] reqs;
// if (parentId.StartsWith("p"))
// {
// reqs = Facade.Select<Entity.Requirement>("ParentID = 0 and ProjectID = @ProjectID", int.Parse(parentId.TrimStart('p')));
// }
// else
// {
// reqs = Facade.Select<Entity.Requirement>("ParentID = @ParentID", int.Parse(parentId.TrimStart('r')));
// }
// if (reqs != null && reqs.Length > 0)
// {
// object[] objs = new object[reqs.Length];
// for (int i = 0; i < objs.Length; i++)
// {
// objs[i] = new EntityWrapper<Entity.Requirement>(reqs[i]);
// }
// return objs;
// }
// return null;
//}
//private object GetProjectOrRequirement(string id)
//{
// if (id.StartsWith("p"))
// {
// return new EntityWrapper<Entity.Project>(Facade.Get<Entity.Project>(int.Parse(id.TrimStart('p'))));
// }
// else
// {
// return new EntityWrapper<Entity.Requirement>(Facade.Get<Entity.Requirement>(int.Parse(id.TrimStart('p'))));
// }
//}
//private string GetProjectOrRequirementId(object obj)
//{
// if (obj is EntityWrapper<Entity.Project>)
// {
// return "p" + StrongTyped<EntityWrapper<Entity.Project>>(obj).Target.ID.ToString();
// }
// else
// {
// return "r" + StrongTyped<EntityWrapper<Entity.Requirement>>(obj).Target.ID.ToString();
// }
//}
#endregion
}
} 注意代码中最后一个#region SampleClientHandler是一组project-requirement两种元素的继承体系的delegate实现范例。
使用的时候,只要将这几个delegate实现函数引用作为构造函数的参数,将实例化的datasource赋给treeview的datasource,就能轻松显示整个继承体系的元素了。
源码下载
HierarchicalDataSource.zip--
以上代码虽然具有一定的通用性,但是本身毕竟是为了一定的特殊用途而写,因此功能并不完备,如果要大量使用,请酌情重构和改写。