由一棵树绑定引发的遐想

作为一个开发人员, 总的写写什么树绑定之类的东东.

为了活的轻松点, 我们不得不想办法偷些懒.来看看树绑定偷懒办法.

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控件, 要支持别的控件, 可进一步改造. 办法总比困难多.

posted @ 2009-11-25 12:26  大熊(BigBear)  阅读(409)  评论(0)    收藏  举报