zoukankan      html  css  js  c++  java
  • 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;
            }
        }
    }
  • 相关阅读:
    com.android.ide.common.process.PrecessException:org.gradle.process....finished with non-zero exit value 1
    android 学习笔记(1)
    C#遍历指定文件夹中的所有文件(转)
    让TextView里面的文字逐个显示的动画效果实现(1)
    This Handler class should be static or leaks might occur(null) 解决办法 (转)
    Android开发 旋转屏幕导致Activity重建解决方法(转)
    SQLite 数据库
    【Android】error opening trace file: No such file or directory (2)
    WPF中的RichTextBox
    android中的 Toast 和 AlertDialog
  • 原文地址:https://www.cnblogs.com/ValiancyHe/p/2454552.html
Copyright © 2011-2022 走看看