winform三态树的构建和使用

  哇咔咔,好长时间没来博客园写点东西了,主要是感觉水平有限没啥可写的,而最近做一个项目也感觉没多少自己原创的东西,想想最近这个项目中唯一值得一写的东西就是研究了个winform的三态树,遂想把源码贴上来分享一下。三态树就是子节点全部选中、部分选中、全部未选中时父节点有三种选中状态,windows系统的好多功能都用这种三态树,但是让人郁闷的是winform却没有直接提供这种控件,只提供了一个很普通的treeview。严格说来这个也不算我的原创,我只是从codeproject上面找到一个源码,并修复了一些bug以及根据自己的需求修改了部分代码,但是考虑到当时我中文搜索并没有搜索到自己想要的,而是通过google英文搜索才找到的,所以觉得还是有必要来贴一下这个三态树。顺便赞一下google的英文搜索,解决了我的好多技术问题,而百度一般是搜出来一堆垃圾。细节实现就不说了,大家直接看代码吧,看懂应该是没问题的,当初我也是看着代码琢磨的,完全忽略了人家好心写的一些英文注释。不过还是大体说一下,这个就是重写了treeview,成了一个自定义控件,并且自己定义了一个属性,就是可以选择是否要作为三态树使用,所以这个是完全可以当初普通树用的,使用的话就是修改一下命名空间将代码文件加到项目,编译一下便生成了自定义控件了,拖动即可使用。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Data;
using DevExpress.XtraEditors;

namespace NXADMSLIB.Lib_GIS
{
    public delegate void EventCheckHandler(EventCheckArg e);
    
    /// <summary>
    /// Provides a tree view
    /// control supporting
    /// tri-state checkboxes.
    /// </summary>
    public class TriStateTreeView : TreeView
    {
        public event EventCheckHandler NodeChecked;
        // ~~~ fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        ImageList _ilStateImages;
        bool _bUseTriState;
        bool _bCheckBoxesVisible;
        bool _bPreventCheckEvent;
        public bool PreventCheckEvent
        {
            set { _bPreventCheckEvent = value; }
            get { return _bPreventCheckEvent; }
        }

        // ~~~ constructor ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /// <summary>
        /// Creates a new instance
        /// of this control.
        /// </summary>
        public TriStateTreeView()
            : base()
        {
         CheckBoxState cbsState;
         Graphics gfxCheckBox;
         Bitmap bmpCheckBox;

            _ilStateImages = new ImageList();                                            // first we create our state image
            cbsState = CheckBoxState.UncheckedNormal;                                    // list and pre-init check state.

            for (int i = 0; i <= 2; i++) {                                                // let's iterate each tri-state
                bmpCheckBox = new Bitmap(16, 16);                                        // creating a new checkbox bitmap
                gfxCheckBox = Graphics.FromImage(bmpCheckBox);                            // and getting graphics object from
                switch (i) {                                                            // it...
                    case 0: cbsState = CheckBoxState.UncheckedNormal; break;
                    case 1: cbsState = CheckBoxState.CheckedNormal; break;
                    case 2: cbsState = CheckBoxState.MixedNormal; break;
                }
                CheckBoxRenderer.DrawCheckBox(gfxCheckBox, new Point(2, 2), cbsState);    // ...rendering the checkbox and...
                gfxCheckBox.Save();
                _ilStateImages.Images.Add(bmpCheckBox);                                    // ...adding to sate image list.
                
                _bUseTriState = true;
            }
        }

        // ~~~ properties ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /// <summary>
        /// Gets or sets to display
        /// checkboxes in the tree
        /// view.
        /// </summary>
        [Category("Appearance")]
        [Description("Sets tree view to display checkboxes or not.")]
        [DefaultValue(false)]
        public new bool CheckBoxes
        {
            get { return _bCheckBoxesVisible; }
            set
            {
                _bCheckBoxesVisible = value;
                base.CheckBoxes = _bCheckBoxesVisible;
                this.StateImageList = _bCheckBoxesVisible ? _ilStateImages : null;
            }
        }
        
        [Browsable(false)]
        public new ImageList StateImageList
        {
            get { return base.StateImageList; }
            set { base.StateImageList = value; }
        }

        /// <summary>
        /// Gets or sets to support
        /// tri-state in the checkboxes
        /// or not.
        /// </summary>
        [Category("Appearance")]
        [Description("Sets tree view to use tri-state checkboxes or not.")]
        [DefaultValue(true)]
        public bool CheckBoxesTriState
        {
            get { return _bUseTriState; }
            set { _bUseTriState = value; }
        }

        // ~~~ functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /// <summary>
        /// Refreshes this
        /// control.
        /// </summary>
        public override void Refresh()
        {
            Stack<TreeNode> stNodes;
            TreeNode tnStacked;

            base.Refresh();

            if (!CheckBoxes)                                                // nothing to do here if
                return;                                                        // checkboxes are hidden.

            base.CheckBoxes = false;                                        // hide normal checkboxes...

            stNodes = new Stack<TreeNode>(this.Nodes.Count);                // create a new stack and
            foreach (TreeNode tnCurrent in this.Nodes)                        // push each root node.
                stNodes.Push(tnCurrent);

            while (stNodes.Count > 0)
            {                                        // let's pop node from stack,
                tnStacked = stNodes.Pop();                                    // set correct state image
                if (tnStacked.StateImageIndex == -1)                        // index if not already done
                    tnStacked.StateImageIndex = tnStacked.Checked ? 1 : 0;    // and push each child to stack
                for (int i = 0; i < tnStacked.Nodes.Count; i++)                // too until there are no
                    stNodes.Push(tnStacked.Nodes[i]);                        // nodes left on stack.
            }
        }

        protected override void OnLayout(LayoutEventArgs levent)
        {
            base.OnLayout(levent);

            Refresh();
        }

        protected override void OnAfterExpand(TreeViewEventArgs e)
        {
            base.OnAfterExpand(e);

            foreach (TreeNode tnCurrent in e.Node.Nodes)                    // set tree state image
                if (tnCurrent.StateImageIndex == -1)                        // to each child node...
                    tnCurrent.StateImageIndex = tnCurrent.Checked ? 1 : 0;
        }

        protected override void OnAfterCheck(TreeViewEventArgs e)
        {
            base.OnAfterCheck(e);

            if (_bPreventCheckEvent)
                return;

            OnNodeMouseClick(new TreeNodeMouseClickEventArgs(e.Node, MouseButtons.None, 0, 0, 0));
        }

        protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
        {
         Stack<TreeNode> stNodes;
         TreeNode tnBuffer;
         bool bMixedState;
         int iSpacing;
         int iIndex;

            base.OnNodeMouseClick(e);

            _bPreventCheckEvent = true;

            iSpacing = ImageList == null ? 0 : 18;                            // if user clicked area
            if ((e.X > e.Node.Bounds.Left - iSpacing ||                        // *not* used by the state
                 e.X < e.Node.Bounds.Left - (iSpacing + 16)) &&                // image we can leave here.
                 e.Button != MouseButtons.None)
            { return; }

            tnBuffer = e.Node;                                                // buffer clicked node and
            if (e.Button == MouseButtons.Left)                                // flip its check state.
                tnBuffer.Checked = !tnBuffer.Checked;

            tnBuffer.StateImageIndex =  tnBuffer.Checked ?                    // set state image index
                                        1 : tnBuffer.StateImageIndex;// tnBuffer.StateImageIndex;        // correctly.

            OnAfterCheck(new TreeViewEventArgs(tnBuffer, TreeViewAction.ByMouse));

            stNodes = new Stack<TreeNode>(tnBuffer.Nodes.Count);            // create a new stack and
            stNodes.Push(tnBuffer);                                            // push buffered node first.
            do {                                                            // let's pop node from stack,
                tnBuffer = stNodes.Pop();                                    // inherit buffered node's
                tnBuffer.Checked = e.Node.Checked;
                tnBuffer.StateImageIndex = tnBuffer.Checked ?                    // set state image index
                                        1 : 0;// tnBuffer.StateImageIndex;        // correctly.// check state and push
                for (int i = 0; i < tnBuffer.Nodes.Count; i++)                // each child on the stack
                    stNodes.Push(tnBuffer.Nodes[i]);                        // until there is no node
            } while (stNodes.Count > 0);                                    // left.

            bMixedState = false;
            tnBuffer = e.Node;                                                // re-buffer clicked node.
            while (tnBuffer.Parent != null) {                                // while we get a parent we
                foreach (TreeNode tnChild in tnBuffer.Parent.Nodes)            // determine mixed check states
                    bMixedState |= (tnChild.Checked != tnBuffer.Checked |    // and convert current check
                                    tnChild.StateImageIndex == 2);            // state to state image index.
                iIndex = (int)Convert.ToUInt32(tnBuffer.Checked);            // set parent's check state and
                tnBuffer.Parent.Checked = bMixedState || (iIndex > 0);        // state image in dependency
                if (bMixedState)                                            // of mixed state.
                    tnBuffer.Parent.StateImageIndex = CheckBoxesTriState ? 2 : 1;
                else
                    tnBuffer.Parent.StateImageIndex = iIndex;
                tnBuffer = tnBuffer.Parent;                                    // finally buffer parent and
                bMixedState = false;
            }                                                                // loop here.

            _bPreventCheckEvent = false;
            //将点击事件传达以对地图做进一步操作
            //this.NodeChecked(new EventCheckArg(e.Node));
            if (this.NodeChecked != null)
            {
                this.NodeChecked(new EventCheckArg(e.Node));
            }
        }

        //三态树的初始构建
        public void CreateTree(DataTable table, string id, string name, string pid)
        {
            bool bMixedState = false;
            TreeNode tnBuffer;
            TreeNode firstChild;
            int iIndex;
            if (table == null)
            {
                XtraMessageBox.Show("空树");
                return;
            }
            this.Nodes.Clear();
            this.PreventCheckEvent = true;//设置节点checked状态时不触发点击事件
            foreach (DataRow row in table.Rows)
            {
                if (row[pid].ToString() == string.Empty)
                {
                    TreeNode root = new TreeNode();
                    root.Tag = row;
                    root.Text = row[name].ToString();
                    this.Nodes.Add(root);
                    CreateChildren(root, table, id, name, pid);
                    tnBuffer = root;
                    if (tnBuffer.Nodes.Count == 0)//没有子节点,直接确定状态
                    {
                        iIndex = (int)Convert.ToUInt32(tnBuffer.Checked);
                        tnBuffer.StateImageIndex = iIndex;
                    }
                    else//有子节点,根据子节点状态确定状态
                    {
                        firstChild = tnBuffer.Nodes[0];
                        bMixedState = false;
                        foreach (TreeNode tnChild in tnBuffer.Nodes)        // determine mixed check states
                        {
                            bMixedState = (tnChild.Checked != firstChild.Checked |    // and convert current check
                                            tnChild.StateImageIndex == 2);            // state to state image index.
                            if (bMixedState)
                                break;
                        }
                        iIndex = (int)Convert.ToUInt32(firstChild.Checked);            // set parent's check state and
                        tnBuffer.Checked = bMixedState || (iIndex > 0);        // state image in dependency
                        if (bMixedState)                                            // of mixed state.
                            tnBuffer.StateImageIndex = CheckBoxesTriState ? 2 : 1;
                        else
                            tnBuffer.StateImageIndex = iIndex;
                    }
                }
            }
        }
        //递归构建子树
        private void CreateChildren(TreeNode pNode, DataTable table, string id, string name, string pid)
        {
            bool bMixedState = false;
            TreeNode tnBuffer;
            TreeNode firstChild;
            int iIndex;
            foreach (DataRow row in table.Rows)
            {
                if (row[pid].ToString() == (pNode.Tag as DataRow)["id"].ToString())
                {
                    TreeNode node = new TreeNode();
                    node.Tag = row;
                    node.Text = row[name].ToString();
                    if (row["visible"] == DBNull.Value)//不是叶子节点
                    {
                        pNode.Nodes.Add(node);
                    }
                    else
                    {
                        node.Checked = row["visible"].ToString() == "1";
                        pNode.Nodes.Add(node);
                    }
                    CreateChildren(node, table, id, name, pid);
                    tnBuffer = node;
                    if (tnBuffer.Nodes.Count == 0)//没有子节点,直接确定状态
                    {
                        iIndex = (int)Convert.ToUInt32(tnBuffer.Checked);
                        tnBuffer.StateImageIndex = iIndex;
                    }
                    else//有子节点,根据子节点状态确定状态
                    {
                        firstChild = tnBuffer.Nodes[0];
                        bMixedState = false;
                        foreach (TreeNode tnChild in tnBuffer.Nodes)            // determine mixed check states
                        {
                            bMixedState = (tnChild.Checked != firstChild.Checked |    // and convert current check
                                            tnChild.StateImageIndex == 2);            // state to state image index.
                            if (bMixedState)
                                break;
                        }
                        iIndex = (int)Convert.ToUInt32(firstChild.Checked);            // set parent's check state and
                        tnBuffer.Checked = bMixedState || (iIndex > 0);        // state image in dependency
                        if (bMixedState)                                            // of mixed state.
                            tnBuffer.StateImageIndex = CheckBoxesTriState ? 2 : 1;
                        else
                            tnBuffer.StateImageIndex = iIndex;
                    }
                }
            }
        }

    }

    public class EventCheckArg : EventArgs
    {
        private TreeNode node;
        public TreeNode Node
        {
            set { node = value; }
            get { return node; }
        }
        public EventCheckArg(TreeNode node)
        {
            this.Node = node;
        }
    }
}
posted @ 2012-04-17 22:39  ValiancyHe  阅读(970)  评论(1编辑  收藏  举报