TreeView实现级联选择

问题

      C#的TreeView能够实现大部分功能,
      但是1)类似Shift键进行批量选择,Ctrl进行选择性选择功能没有实现。
              2)不存在级联选择。父节点选择了,子节点没有根据父节点的状态进行状态设置(某些应用)

解决方案

    向 注册事件,在KeyDown中记录是否存在Shift或者Ctrl键的按下。在KeyUp完成选择。在NodeMouseClick事件中把中间选择的点高亮。
    通过注册AfterCheck事件实现级联选择。


完整代码

   
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace WorldWind.Aide
{
    /// <summary>
    /// 这个类提供树形视图的另类操作
    /// </summary>
    [CodeDescription("2013.07.24","封装多选的功能")]
    public class TreeViewAssist
    {
        #region 属性
        /// <summary>
        /// 批量选择的节点
        /// </summary>
        private List<TreeNode> m_selectedNodes = new List<TreeNode>();
        private bool m_controlKeyDown = false;
        private bool m_shiftKeyDown = false;
        private TreeView m_treeView;
        private Color m_defaultForeColor = Color.Black;
        private Color m_defaultBackColor = Color.White;
        private Color m_highlightBackColor = SystemColors.Highlight;
        private Color m_highlightForeColor = Color.White;
        private bool m_enableMultiSelect = true;
        private bool m_enebleCascadeSelect = true;
        private bool m_allowParentSingleCheck = true;
        #endregion

        #region 对外属性
         /// <summary>
        /// 获得批量选择的节点
        /// <para>注意,这个是通过Shift键或者Control键得到的节点,键没用按下去就没有</para>
        /// </summary>
        public List<TreeNode> SelectedNodes
        {
            get { return m_selectedNodes; }
        }
        public Color DefaultForeColor
        {
            get { return m_defaultForeColor; }
            set { m_defaultForeColor = value; }
        }
        public Color DefaultBackColor
        {
            get { return m_defaultBackColor; }
            set { m_defaultBackColor = value; }
        }
        public Color HighlightBackColor
        {
            get { return m_highlightBackColor; }
            set { m_highlightBackColor = value; }
        }
        public Color HighlightForeColor
        {
            get { return m_highlightForeColor; }
            set { m_highlightForeColor = value; }
        }
        /// <summary>
        /// 是否允许多选
        /// </summary>
        public bool EnableMultiSelect
        {
            get { return m_enableMultiSelect; }
            set {
                if (m_enableMultiSelect != value)
                {
                    m_enableMultiSelect = value;
                    SetEvent();
                }
            }
        }
        /// <summary>
        /// 是否允许级联选择
        /// </summary>
        public bool EnebleCascadeSelect
        {
            get { return m_enebleCascadeSelect; }
            set {
                if (m_enebleCascadeSelect != value)
                {
                    m_enebleCascadeSelect = value;
                    SetEvent();
                }
            }
        }
        /// <summary>
        /// 是否允许父节点单独进行选择,默认true,即子类没有选择那么父类也没得选择
        /// </summary>
        public bool AllowParentSingleCheck
        {
            get { return m_allowParentSingleCheck; }
            set { m_allowParentSingleCheck = value; }
        }
        #endregion

        #region 构造函数
        /// <summary>
        /// 根据一个树来构建一个辅助
        /// </summary>
        /// <param name="treeView"></param>
        public TreeViewAssist(TreeView treeView)
            : this(treeView, true, true)
        {

        }
        /// <summary>
        /// 建立图层节点的帮助
        /// </summary>
        /// <param name="treeView"></param>
        /// <param name="enableMultiSelect">是否允许多点选择</param>
        /// <param name="enableCascadeSelect">是否允许级联选择</param>
        public TreeViewAssist(TreeView treeView, bool enableMultiSelect,bool enableCascadeSelect)
        {
            m_enableMultiSelect = enableMultiSelect;
            m_enebleCascadeSelect = enableCascadeSelect;
            m_treeView = treeView;
            m_defaultForeColor = treeView.ForeColor;
            m_defaultBackColor = treeView.BackColor;
            SetEvent();
        }
        #endregion

        #region TreeView事件
        /// <summary>
        /// 根据设置配置事件
        /// </summary>
        private void SetEvent()
        {
            if (m_enableMultiSelect)
            {
                m_treeView.KeyDown += new KeyEventHandler(m_treeView_KeyDown);
                m_treeView.KeyUp += new KeyEventHandler(m_treeView_KeyUp);
                m_treeView.NodeMouseClick += new TreeNodeMouseClickEventHandler(m_treeView_NodeMouseClick);
            }
            else
            {
                m_treeView.KeyDown -= new KeyEventHandler(m_treeView_KeyDown);
                m_treeView.KeyUp -= new KeyEventHandler(m_treeView_KeyUp);
                m_treeView.NodeMouseClick -= new TreeNodeMouseClickEventHandler(m_treeView_NodeMouseClick);
            }

            if (m_enebleCascadeSelect)
            {
                m_treeView.AfterCheck += new TreeViewEventHandler(m_treeView_AfterCheck);
            }
            else
            {
                m_treeView.AfterCheck -= new TreeViewEventHandler(m_treeView_AfterCheck);
            }
        }

        void m_treeView_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Action != TreeViewAction.ByMouse)
                return;
            TriggerParentCheck(e.Node, e.Node.Checked);
            TriggerChildCheck(e.Node, e.Node.Checked);
        }
        void m_treeView_KeyDown(object sender, KeyEventArgs e)
        {
            m_controlKeyDown = e.Control;
            m_shiftKeyDown = e.Shift;
        }
        void m_treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                OnSelectingNodes(e.Node);
            }
        }

        void m_treeView_KeyUp(object sender, KeyEventArgs e)
        {
            m_controlKeyDown = false;
            m_shiftKeyDown = false;
        }
        #endregion

        #region 批量选择的代码
        /// <summary>
        /// 清除选择的节点
        /// </summary>
        public void ClearSelectedNodes()
        {
            if (m_selectedNodes.Count == 0)
                return;
            foreach (TreeNode tn in m_selectedNodes)
            {
                tn.ForeColor = DefaultForeColor;
                tn.BackColor = DefaultBackColor;
            }
            m_selectedNodes.Clear();
        }
        /// <summary>
        /// 清除选择的节点
        /// </summary>
        /// <param name="tn"></param>
        private void ClearSelectedNode(TreeNode tn)
        {
            tn.ForeColor = DefaultForeColor;
            tn.BackColor = DefaultBackColor;
            m_selectedNodes.Remove(tn);
        }
        /// <summary>
        /// 添加一个节点作为选择节点的一个
        /// </summary>
        /// <param name="tn"></param>
        /// <param name="checkExist"></param>
        private void SetNodeAsSelectedNodes(TreeNode tn, bool checkExist)
        {
            if (checkExist)
            {
                if (m_selectedNodes.Contains(tn))
                    return;
            }
            m_selectedNodes.Add(tn);
            tn.BackColor = HighlightBackColor;
            tn.ForeColor = HighlightForeColor;
        }
        /// <summary>
        /// 正在通过Shift或者Control的节点
        /// </summary>
        /// <param name="selectedNode"></param>
        private void OnSelectingNodes(TreeNode selectedNode)
        {
            if (m_controlKeyDown == false && m_shiftKeyDown == false)
            {
                ClearSelectedNodes();
                return;
            }
            if (m_controlKeyDown)
            {
                if (m_selectedNodes.Count == 0)
                {
                    if (this.m_treeView.SelectedNode != null)
                    {
                        SetNodeAsSelectedNodes(this.m_treeView.SelectedNode, true);
                    }
                }
                SetNodeAsSelectedNodes(selectedNode, true);
            }
            if (m_shiftKeyDown)
            {
                TreeNode firstTn = this.m_treeView.SelectedNode;
                if (m_selectedNodes.Count > 0)
                    firstTn = m_selectedNodes[0];
                TreeNode secondTn = selectedNode;

                AddShiftNodes(firstTn, secondTn);
            }
        }
        /// <summary>
        /// 添加经过Shift控制的节点
        /// </summary>
        /// <param name="firstTn"></param>
        /// <param name="secondTn"></param>
        private void AddShiftNodes(TreeNode firstTn, TreeNode secondTn)
        {
            TreeNode nextVisibleNode = firstTn;
            bool find = false;
            while (nextVisibleNode != null)
            {
                if (nextVisibleNode == secondTn)
                {
                    find = true;
                    break;
                }
                nextVisibleNode = nextVisibleNode.NextVisibleNode;
            }

            //清除原来的数据
            ClearSelectedNodes();

            if (firstTn != null && secondTn != null)
            { 
                TravelShiftSelectedNodes(firstTn, secondTn, find);
            }

        }
        /// <summary>
        /// 添加Shift控制选择的节点
        /// </summary>
        /// <param name="currentNode"></param>
        /// <param name="secondNode"></param>
        /// <param name="findNext"></param>
        private void TravelShiftSelectedNodes(TreeNode currentNode, TreeNode secondNode, bool findNext)
        {
            SetNodeAsSelectedNodes(currentNode, false);

            if (currentNode == secondNode)
                return;

            TreeNode nextNode;
            if (findNext)
                nextNode = currentNode.NextVisibleNode;
            else
                nextNode = currentNode.PrevVisibleNode;
            if (nextNode != null)
            {
                TravelShiftSelectedNodes(nextNode, secondNode, findNext);
            }
        }
        /// <summary>
        /// 组织选择的节点,包括
        /// <para>判断一个节点的选择了,但它的子节点只选择了一部分</para>
        /// <para>一个节点和它的子节点都选择了,那么只选择这个节点,子节点不作为选择</para>
        /// </summary>
        /// <returns></returns>
        public bool OrganizeSelectedNodes()
        {
            bool[] toBeRemove = new bool[m_selectedNodes.Count];
            for (int i = m_selectedNodes.Count - 1; i > -1; i--)
            {
                //存在一个组,这个组内的数据只有部分被选择
                if (m_selectedNodes[i].Nodes.Count > 0)
                {
                    int childSelCount = 0;
                    foreach (TreeNode tn in m_selectedNodes[i].Nodes)
                    {
                        if (m_selectedNodes.Contains(tn))
                        {
                            childSelCount++;
                        }
                    }
                    if (childSelCount != 0 && childSelCount < m_selectedNodes[i].Nodes.Count)
                    {
                        MessageBox.Show("【" + m_selectedNodes[i].Text + "】只选择了部分的数据,不能进行下一步操作!");
                        return false;
                    }
                }
            }
            for (int i = m_selectedNodes.Count - 1; i > -1; i--)
            {
                for (int j = i - 1; j > -1; j--)
                {
                    if (m_selectedNodes[j].Parent == m_selectedNodes[i])
                    {
                        toBeRemove[j] = true;
                    }
                }
            }
            for (int i = m_selectedNodes.Count - 1; i > -1; i--)
            {
                if (toBeRemove[i])
                    m_selectedNodes.RemoveAt(i);
            }

            return true;
        }
        #endregion

        #region 级联选择
        /// <summary>
        /// 级联到父类的选择
        /// </summary>
        /// <param name="tn"></param>
        /// <param name="tnChecked"></param>
        public void TriggerParentCheck(TreeNode tn, bool tnChecked)
        {
            if (tnChecked)
            {
                TreeNode parentTN = tn.Parent;
                //传递上层
                while (parentTN != null && parentTN.Checked == false)
                {
                    parentTN.Checked = true;
                    parentTN = parentTN.Parent;
                }
            }
            else
            {
                TreeNode parentTN = tn.Parent;
                if (parentTN == null)
                    return;
                bool childHasChecked = false;
                for (int i = 0; i < parentTN.Nodes.Count; i++)
                {
                    if (parentTN.Nodes[i].Checked)
                    {
                        childHasChecked = true;
                        break;
                    }
                }
                if (childHasChecked)
                {
                    TriggerParentCheck(tn, true);
                }
                else if (m_allowParentSingleCheck == false)
                {
                    if (parentTN.Checked == true)
                    {
                        parentTN.Checked = false;
                        TriggerParentCheck(parentTN, false);
                    }
                }
            }
        }
        /// <summary>
        /// 级联到子类的选择
        /// </summary>
        /// <param name="tn"></param>
        /// <param name="tnChecked"></param>
        public void TriggerChildCheck(TreeNode tn, bool tnChecked)
        {
            TriggerChildCheck(tn, tnChecked, true);
        }
        /// <summary>
        /// 级联到子类的选择
        /// </summary>
        /// <param name="tn"></param>
        /// <param name="tnChecked"></param>
        /// <param name="checkNodeCount"></param>
        private void TriggerChildCheck(TreeNode tn, bool tnChecked, bool checkNodeCount)
        {
            if (checkNodeCount)
            {
                if (tn.Nodes == null && tn.Nodes.Count == 0)
                {
                    return;
                }
            }
            foreach (TreeNode childTN in tn.Nodes)
            {
                if (childTN.Checked != tnChecked)
                {
                    childTN.Checked = tnChecked;
                    if (childTN.Nodes != null && childTN.Nodes.Count != 0)
                    {
                        TriggerChildCheck(childTN, tnChecked, false);
                    }
                }
            }
        }
        #endregion

        #region 卸载
        /// <summary>
        /// 卸载
        /// </summary>
        public void Dispose()
        {
            EnableMultiSelect = false;
            this.EnebleCascadeSelect = false;
        }
        #endregion
    }
}

猜你喜欢

转载自blog.csdn.net/htsitr2/article/details/39050567