zoukankan      html  css  js  c++  java
  • TreeView控件实现按Ctrl和Shift键多选功能

    继上次定制TreeView控件,实现节点样式自定义及节点级别的单选、复选后继续对TreeView控件,这次实现的功能是树节点可以按Ctrl/Shift键多选和树节点的拖动。相对于上次的代码,这次主要新增加也以下几个属性和方法:

    • IsMultiSelect属性:树是否需要使用多选功能
    • SelectedNodeList属性:多选时被选中的节点的集合
    • TreeNodeCanAcceptDragedHandler事件:拖动时用来判断目标节点是否接受拖动
    • IsNodeCanAcceptDragHandler事件:拖动成功后要执行的事件,一般用来将新的关系持久化到数据库或其它地方。

    实现比较简单,各位直接看代码吧。

    扩展树的代码
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Windows.Forms;
    using GADPlatSystem.Navigate;

    namespace GADPlatSystem.Tools
    {
    ///<summary>
    /// 导航树控件
    ///</summary>
    [DesignTimeVisible(true)]
    [Serializable]
    public class GTreeView : TreeView
    {
    #region 成员变量

    ///<summary>
    /// 存储多选时选择的节点
    ///</summary>
    private IList<TreeNode> selectedNodeList = new List<TreeNode>();

    ///<summary>
    /// 当前节点
    ///</summary>
    private TreeNode currentNode = null;

    #endregion

    #region 属性

    ///<summary>
    /// 是否是多选
    ///</summary>
    public bool IsMultiSelect { get; set; }

    ///<summary>
    /// 选择的节点的集合
    ///</summary>
    public IList<TreeNode> SelectedNodeList
    {
    get { return selectedNodeList; }
    }

    #endregion

    #region Delegate & Event

    ///<summary>
    /// 节点被拖动后要处理事件的Delegate
    ///</summary>
    ///<param name="sourceNode">被拖动的节点</param>
    ///<param name="targetNode">目标节点</param>
    public delegate void OnDragNodeSucceed(TreeNode sourceNode, TreeNode targetNode);

    ///<summary>
    /// 判断目标节点是否接受拖动的Delegate
    ///</summary>
    ///<param name="targetNode"></param>
    ///<returns></returns>
    public delegate bool IsNodeCanAcceptDrag(TreeNode targetNode);

    ///<summary>
    /// 节点被拖动后要处理事件
    ///</summary>
    public event OnDragNodeSucceed TreeNodeCanAcceptDragedHandler;

    ///<summary>
    /// 判断目标节点是否接受拖动的事件处理
    ///</summary>
    public event IsNodeCanAcceptDrag IsNodeCanAcceptDragHandler;

    #endregion

    #region 类函数

    ///<summary>
    /// 构造函数
    ///</summary>
    public TreeViewExt()
    : base()
    {
    this.DrawMode = TreeViewDrawMode.OwnerDrawText;
    }

    ///<summary>
    /// 鼠标单击事件
    ///</summary>
    ///<param name="e">TreeNodeMouseClickEventArgs对象类</param>
    protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
    {
    this.SelectedNode = e.Node;
    var isSingleSelected = false;

    // 如果是多选,则根据按钮情况设置节点的选择状态
    if (IsMultiSelect)
    {
    if (!(SelectedNodeList.Count == 1 && SelectedNodeList[0] == SelectedNode))
    {
    if ((Control.ModifierKeys & Keys.Control) != 0 || e.Button == MouseButtons.Right)
    {
    ctrlMultiSelectNodes(SelectedNode, e.Button == MouseButtons.Right);
    }
    else if ((Control.ModifierKeys & Keys.Shift) != 0)
    {
    shiftMultiSelectNodes(SelectedNode, e.Button == MouseButtons.Right);
    }
    else
    {
    isSingleSelected = true;
    singleSelectNode(SelectedNode);
    }
    }
    isSingleSelected = true;
    }
    else
    {
    singleSelectNode(SelectedNode);
    setCurrentNode(SelectedNode);
    }

    this.Invalidate();
    }


    ///<summary>
    /// 重绘,主要是在Checkbox/RadioButton前面有图片
    ///</summary>
    ///<param name="e">DrawTreeNodeEventArgs对象类</param>
    protected override void OnDrawNode(DrawTreeNodeEventArgs e)
    {
    if (e.Bounds.X == -1)
    return;

    e.DrawDefault = false;

    Font font = this.Font;
    if (e.Node.NodeFont != null) font = e.Node.NodeFont;

    Color color = this.ForeColor;
    if (SelectedNodeList.Contains(e.Node))
    {
    color = SystemColors.HighlightText;
    }
    else if (e.Node.ForeColor != Color.Empty)
    {
    color = e.Node.ForeColor;
    }

    Graphics g = e.Graphics;
    Rectangle textBounds = new Rectangle();

    GTreeNode extNode = e.Node as GTreeNode;

    textBounds.X = e.Bounds.X;
    textBounds.Y = e.Bounds.Y;
    textBounds.Width = e.Bounds.Width;
    textBounds.Height = e.Bounds.Height;

    // 绘制节点的文本
    if (SelectedNodeList.Contains(e.Node))
    {
    g.FillRectangle(SystemBrushes.Highlight, textBounds);
    ControlPaint.DrawFocusRectangle(g, textBounds, color, SystemColors.Highlight);
    TextRenderer.DrawText(g, e.Node.Text, font, textBounds, color, TextFormatFlags.Default);
    }
    else
    {
    g.FillRectangle(SystemBrushes.Window, textBounds);
    TextRenderer.DrawText(g, e.Node.Text, font, textBounds, color, TextFormatFlags.Default);
    }
    }


    //控件初始化
    private void InitializeComponent()
    {
    this.SuspendLayout();
    //
    // GTreeViewExt
    //
    this.Indent = 25;
    this.ItemHeight = 25;
    this.ResumeLayout(false);

    }

    #endregion

    #region 鼠标拖动节点移动

    ///<summary>
    /// 拖动节点移动,在鼠标拖放操作结束时发生
    ///</summary>
    ///<param name="drgevent">DragEventArgs对象</param>
    protected override void OnDragDrop(DragEventArgs drgevent)
    {
    base.OnDragDrop(drgevent);

    var moveNode = (TreeNode)drgevent.Data.GetData(typeof(TreeNode));

    //根据鼠标坐标确定要移动到的目标节点
    Point point = this.PointToClient(new Point(drgevent.X, drgevent.Y));
    var targetNode = this.GetNodeAt(point);

    // 如果目标节点不接受拖动,则返回
    if (IsNodeCanAcceptDragHandler != null)
    {
    if (!IsNodeCanAcceptDragHandler(targetNode))
    {
    return;
    }
    }

    // 确定落下的节点不是被拖拽节点本身或者被拖拽节点的子节点
    if (!moveNode.Equals(targetNode) && !containsNode(moveNode, targetNode))
    {
    var newMoveNode = (TreeNode)moveNode.Clone();
    targetNode.Nodes.Insert(targetNode.Index, newMoveNode);

    //更新当前拖动的节点选择
    this.SelectedNode = newMoveNode;

    //移除拖放的节点
    moveNode.Remove();
    moveNode = newMoveNode;
    newMoveNode.Expand();

    if (TreeNodeCanAcceptDragedHandler != null)
    {
    TreeNodeCanAcceptDragedHandler(moveNode, targetNode);
    }
    }
    }

    ///<summary>
    /// 拖动节点移动,在用鼠标将某项拖动到该控件的工作区时发生
    ///</summary>
    ///<param name="drgevent">DragEventArgs对象</param>
    protected override void OnDragEnter(DragEventArgs drgevent)
    {
    base.OnDragEnter(drgevent);

    if (drgevent.Data.GetDataPresent(typeof(TreeNode)))
    {
    drgevent.Effect = DragDropEffects.Move;
    }
    else
    {
    drgevent.Effect = DragDropEffects.None;
    }
    }

    ///<summary>
    /// 拖动节点移动,在用户开始拖动项时发生
    ///</summary>
    ///<param name="e">ItemDragEventArgs对象</param>
    protected override void OnItemDrag(ItemDragEventArgs e)
    {
    base.OnItemDrag(e);

    if (e.Button == MouseButtons.Left)
    {
    DoDragDrop(e.Item, DragDropEffects.Move);
    }
    }

    #endregion 鼠标拖动节点移动

    #region Private Methods

    ///<summary>
    /// 按ctrl键多选的方法
    ///</summary>
    ///<param name="node"></param>
    ///<param name="mustSelect"></param>
    private void ctrlMultiSelectNodes(TreeNode node, bool mustSelect)
    {
    if (SelectedNodeList.Contains(node) && !mustSelect)
    {
    SelectedNodeList.Remove(node);
    setCurrentNode((TreeNode)SelectedNodeList[SelectedNodeList.Count - 1]);
    }
    else if (!mustSelect)
    {
    SelectedNodeList.Add(node);
    setCurrentNode(node);
    }
    }

    ///<summary>
    /// 按shift键多选的方法
    ///</summary>
    ///<param name="node"></param>
    ///<param name="mustSelect"></param>
    private void shiftMultiSelectNodes(TreeNode node, bool mustSelect)
    {
    if (mustSelect)
    {
    return;
    }
    if (SelectedNodeList.Contains(node))
    {
    SelectedNodeList.Remove(node);
    setCurrentNode((TreeNode)SelectedNodeList[SelectedNodeList.Count - 1]);
    }
    else
    {
    if (node.Parent == currentNode.Parent)
    {

    TreeNode addNode = node;
    for (int i = System.Math.Abs(currentNode.Index - node.Index); i > 0; i--)
    {
    if (!SelectedNodeList.Contains(addNode))
    {
    SelectedNodeList.Add(addNode);
    }

    addNode = currentNode.Index > node.Index ? addNode.NextNode : addNode.PrevNode;
    }

    setCurrentNode(node);
    }
    else
    {
    singleSelectNode(SelectedNode);
    }
    }


    }

    ///<summary>
    /// single select
    ///</summary>
    ///<param name="node"></param>
    private void singleSelectNode(TreeNode node)
    {
    SelectedNodeList.Clear();
    SelectedNodeList.Add(node);
    setCurrentNode(node);
    }

    ///<summary>
    /// Set current node
    ///</summary>
    ///<param name="node"></param>
    private void setCurrentNode(TreeNode node)
    {
    //if (isMulSelect)
    // SelectedNode = null;
    if (currentNode != node)
    {
    currentNode = node as TreeNode;
    }
    }


    ///<summary>
    /// 确定一个节点是否是另一个节点的祖先节点
    ///</summary>
    ///<param name="parentNode"></param>
    ///<param name="childNode"></param>
    ///<returns></returns>
    private bool containsNode(TreeNode parentNode, TreeNode childNode)
    {
    if (childNode.Parent == null) return false;
    if (childNode.Parent.Equals(parentNode)) return true;

    return containsNode(parentNode, childNode.Parent);
    }

    #endregion
    }
    }
    使用示例
           private void Form1_Load(object sender, EventArgs e)
    {
    var gTreeView1 = new GTreeViewExt();
    gTreeView1.AllowDrop = true;
    gTreeView1.IsMultiSelect = true;
    gTreeView1.TreeNodeDragedHandler += (source, target) =>
    { listBox1.Items.Add("" + source.Text + "拖动到了" + target.Text); };
    for (int i = 0; i < 10; i++)
    {
    var cNode = new TreeNode();
    cNode.Tag = i;
    cNode.Text = "Node" + i;
    cNode.ImageIndex = 0;
    cNode.SelectedImageIndex = 0;
    gTreeView1.Nodes.Add(cNode);

    for (int j = 0; j < i; j++)
    {
    var innerNode = new TreeNode();
    innerNode.Tag = i;
    innerNode.Text = "InnerNode" + i + "-" + j;
    innerNode.ImageIndex = 0;
    innerNode.SelectedImageIndex = 0;
    cNode.Nodes.Add(innerNode);
    }
    }

    gTreeView1.ExpandAll();
    gTreeView1.Width = 400;
    gTreeView1.Dock = DockStyle.Left;
    Controls.Add(gTreeView1);
    }




  • 相关阅读:
    shell脚本
    Dockerfile
    【剑灵攻略】003、XXXfragment that is not a fragment错误,fragment认不出来
    【剑灵攻略】002、android工程引用方式
    【剑灵攻略】001、底部菜单栏实现
    ViewPager动画切换
    android raw与assets区别
    android+eclipse+maven环境搭建
    解决在IE8中无法使用原生JSON的问题
    Windows Phone Toast通知
  • 原文地址:https://www.cnblogs.com/zhangronghua/p/CustomTreeViewToSupportDragAndMultiSelect.html
Copyright © 2011-2022 走看看