由一棵树绑定引发的遐想
作为一个开发人员, 总的写写什么树绑定之类的东东.
为了活的轻松点, 我们不得不想办法偷些懒.来看看树绑定偷懒办法.
1. 首先,声明一个接口ITreeAble<T>。
/// <summary>
/// 一个可绑定为树的对象接口
/// </summary>
/// <typeparam name="T">泛型的对象类型,ID的数据类型</typeparam>
public interface ITreeAble<T>
{
T ID { get; set; }
T ParentID { get; set; }
string Name { get; set; }
int OrderID { get; set; }
}
这里用泛型,因为主键的类型可能多样,可以是GUID, INT,或者String等等。
2. 实体类,可以绑定为树的,都需要继承这个接口。这里的主键就指定为整型。
public class ProjectTree : ITreeAble<int>
{
#region ITreeAble<int> Members
public int ID{get; set; }
public int ParentID{get;set;}
public string Name{get;set;}
public int OrderID{get;set;}
#endregion
public string LinkUrl { get; set; }
}
3. 实现绑定的逻辑。
/// <summary>
/// 绑定FlyTree
/// </summary>
/// <typeparam name="T">主键数据类型</typeparam>
public class FlyTreeBindHelper<T>
{
List<ITreeAble<T>> _dataList = null;
T _topNodeID;
FlyTreeView _treeControl = null;
//获得子节点数据的方法委托
public delegate List<ITreeAble<T>> GetChildNodes(string parentID);
public GetChildNodes _GetChildDelegate = null;
//获得是否保护子节点的方法委托,如果节点有子节点就可以展开,否则就不展开
public delegate bool CheckHasChildren(string parentID);
public CheckHasChildren _HasChildren = null;
//生成节点的方法委托
public delegate FlyTreeNode GetNewNode(ITreeAble<T> nodeData);
public GetNewNode _GetNewNode = null;
/// <summary>
///
/// </summary>
/// <param name="treeControl">树控件</param>
/// <param name="getChildNodeDelegate">获取子节点列表</param>
/// <param name="hasChildrenDelegate">是否有子节点</param>
public FlyTreeBindHelper(FlyTreeView treeControl, GetNewNode getNewNode, GetChildNodes getChildNodeDelegate, CheckHasChildren hasChildrenDelegate)
{
_treeControl = treeControl;
_GetNewNode = getNewNode;
if (getChildNodeDelegate != null)
{
_GetChildDelegate = getChildNodeDelegate;
_HasChildren = hasChildrenDelegate;
//节点展开的事件
_treeControl.PopulateNodes += new FlyTreeNodeEventHandler(_treeControl_PopulateNodes);
_treeControl.PostBackOnExpand = true;
}
}
public FlyTreeBindHelper(FlyTreeView treeControl, GetNewNode getNewNode)
: this(treeControl, getNewNode, null, null)
{
}
/// <summary>
/// 绑定树
/// </summary>
/// <param name="treeControl"></param>
/// <param name="dataList"></param>
public void BindTree(List<ITreeAble<T>> dataList, T topNodeID)
{
_dataList = dataList;
_topNodeID = topNodeID;
List<ITreeAble<T>> firstLevel = (from dl in _dataList
where dl.ParentID.Equals(_topNodeID)
orderby dl.OrderID
select dl).ToList<ITreeAble<T>>();
foreach (ITreeAble<T> mi in firstLevel)
{
FlyTreeNode node = GetNode(mi);
_treeControl.Nodes.Add(node);
BindChildNodes(node);
}
}
/// <summary>
/// 异步绑定树,要设定树的属性PostBackOnExpand="True"
/// </summary>
/// <param name="level">绑定的层级,从0开始计数</param>
/// <param name="topNodeID">顶节点ID</param>
public void BindTreeAsyn(List<ITreeAble<T>> dataList, T topNodeID, int level)
{
_dataList = dataList;
_topNodeID = topNodeID;
List<ITreeAble<T>> firstLevel = (from dl in _dataList
where dl.ParentID.Equals(_topNodeID)
orderby dl.OrderID
select dl).ToList<ITreeAble<T>>();
firstLevel.ForEach((a) => _treeControl.Nodes.Add(GetAsynNode(a)));
//设定初始展开级别
_treeControl.ExpandLevel = level;
}
private void BindChildNodes(FlyTreeNode node)
{
List<ITreeAble<T>> children = (from ml in _dataList
where ml.ParentID.Equals(int.Parse(node.Value))
select ml).ToList<ITreeAble<T>>();
foreach (ITreeAble<T> mi in children)
{
FlyTreeNode newNode = GetNode(mi);
node.ChildNodes.Add(newNode);
BindChildNodes(newNode);
}
}
protected FlyTreeNode GetNode(ITreeAble<T> mi)
{
FlyTreeNode node = null;
//如果没有外部实现,采用默认实现
if (_GetNewNode == null)
node = new FlyTreeNode(mi.Name, mi.ID.ToString());
else
node = _GetNewNode(mi);
return node;
}
protected FlyTreeNode GetAsynNode(ITreeAble<T> mi)
{
FlyTreeNode node = GetNode(mi);
node.PopulateNodesOnDemand = (_HasChildren == null) ? true : _HasChildren(node.Value);
return node;
}
/// <summary>
/// 节点展开时,绑定子节点
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void _treeControl_PopulateNodes(object sender, NineRays.WebControls.FlyTreeNodeEventArgs e)
{
if (_GetChildDelegate == null)
throw new Exception("GetChildNodes委托为空,请检测构造函数。");
List<ITreeAble<T>> childrenList = _GetChildDelegate(e.Node.Value);
childrenList.ForEach((a) => e.Node.ChildNodes.Add(GetAsynNode(a)));
}
}
4. 最后来用一下,看看足够简单不。
1. 全部绑定时
if (!IsPostBack)
{
//通过BLL,查询获得对象列表
List<ProjectTree> orList = TestTreeBLL.Instance.GetAll();
//将ProjectTree转成接口列表
List<ITreeAble<int>> datalist = orList.ConvertAll<ITreeAble<int>>((P) => P as ITreeAble<int>);
//使用Helper来绑定树,GetNewNode参数,构造一个新的树节点的方法,如果传入null,则使用默认的实现。
FlyTreeBindHelper<int> helper = new FlyTreeBindHelper<int>(flyTreeView, GetNewNode);
//全部绑定
helper.BindTree(datalist,0);
}
/// <summary>
/// 生成树节点的自定义实现
/// </summary>
/// <param name="mi"></param>
/// <returns></returns>
protected FlyTreeNode GetNewNode(ITreeAble<int> mi)
{
return new FlyTreeNode(mi.Name, mi.ID.ToString());
}
2. 点击树节点, 异步加载子节点.
FlyTreeBindHelper<int> helper2 = new FlyTreeBindHelper<int>(flyTreeView, null, GetChidren, HasChildren);
if (!IsPostBack)
{
//通过BLL,查询获得对象列表
List<ProjectTree> orList = TestTreeBLL.Instance.GetAll();
//将ProjectTree转成接口列表
List<ITreeAble<int>> datalist = orList.ConvertAll<ITreeAble<int>>((P) => P as ITreeAble<int>);
//异步绑定
helper2.BindTreeAsyn(datalist, 0, 0);
}
要注意的地方是,与全部绑定不同,new FlyTreeBindHelper必须写在if (!IsPostBack)外边,每次加载时都运行一次,以绑定树节点的展开事件。
并且要多传入两个参数:GetChidren, HasChildren。
protected List<ITreeAble<int>> GetChidren(string parentID)
{
List<ProjectTree> orList = TestTreeBLL.Instance.Find("ParentID=" + parentID);
//将ProjectTree转成接口列表
return orList.ConvertAll<ITreeAble<int>>((P) => P as ITreeAble<int>);
}
protected bool HasChildren(string parentID)
{
return true;
}
GetChidren方法,是作为一个委托传入构造函数的,用于展开节点时获取子节点的数据。
HasChildren方法,也是作为一个委托传入构造函数,这个参数可以为null,他获取当前节点是否有子节点,如果有就会在树节点上显示
可展开的符号,如果没有子节点,就不能展开。 当传入Null作为参数时,所有节点都会显示可展开的符号。
总结: 这里用的FlyTree控件, 要支持别的控件, 可进一步改造. 办法总比困难多.

浙公网安备 33010602011771号