博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C#添加可以拖拽的panel

Posted on 2014-12-22 10:12  first_start  阅读(2542)  评论(0)    收藏  举报

原文地址:http://blog.sina.com.cn/s/blog_621e24e20101501s.html

最近写的项目中,发现用户特喜欢拖拽,继上次写的listviw内外部拖拽后,这回用户又来了,他们想点中一块区域并移动它,比如他最关心某一个区块,就把它拖拽到最上面,以后看着方便。顿时俺就懵了,哈哈,写了好久才写出来,期间遇到了不少的问题,脑袋都想疼了,最困扰的问题其实不是挪动排序,而是虚拟区域的坐标,一句话的事儿,就是没考虑到。这个拖拽会了,我相信大多数拖拽都可以自己写了吧~现在来看一下效果图:

C# <wbr>任意拖拽Panel,自动排序,支持滚动条和鼠标滚动

C# <wbr>任意拖拽Panel,自动排序,支持滚动条和鼠标滚动

主要思路:挪动panel到哪里,主要是分两个大情况来考虑,先考虑它是向下拖拽还是向上拖拽;然后再根据两种不同的情况再去分析,这样才不会乱!具体细节不想说,因为不好说,要说的东东太多了,建议自己敲代码的时候,要先写一个panel,让它先能够拖动,然后再创建3个panel来拖拽着写下面的代码。所有的判断、排序都是根据panel的location的Y坐标来判断的。向下拖拽时,判断它是否小于别的panel的Y坐标,只有小于才是向下拖,不能用大于来考虑向下拖;同理,向上拖拽时,判断它是否大于别的panel的Y坐标,只有大于才是向上拖,不能用小于来考虑向上拖拽!

arrayPanelSorted:这个最重要,它主要是用于panel怎么排序。排序的话,无非就是数组的项覆盖来覆盖去,后一个覆盖前一个,前一个覆盖后一个,都是根据不同情况来定的,这里最好自己画个图仔细研究一下。

 

dicPanelXY:存放的是每个panel的坐标值,注意,只要拖动滚动条,它的值就都会发生改变,这就是当时卡住我的虚拟区域DisplayRectangle的问题,panel的工作区始终是(0,0),当滚动条往下拽,最上面的panel就滚动到工作区上面去了,它坐标就是负值,越来越小,这就意味着排序完了以后,如果你给最上面的panel的坐标变为(0,0),它只是显示在工作区里排第一个,但是刚才滚动到上面的区域就会变成空白,全都空着!!所以排序后重新赋值坐标得是虚拟坐标!!

 

dicPanelSort:这个就是存放排序后的顺序,方便下一次排序的时候,按顺序把Panel添加到对应下标的arrayPanelSorted里面。

 

最后还要强调一点,就是一个Panel创建好了,它放在dictionary里面,放在list里面,不管它放在哪里了,它的location变了,对一个的所有dictionary<int,Panel>里的啊、list<Panel>里的啊,就全都会变,因为它是引用类型!记住了!!别出现list[0]>(Panel)sender这种低级错误,这就体现了引用类型和值类型在内存上是怎么存储的!

 

好吧,本来想截图贴上去的,为的是自己动手敲一遍,别复制粘贴,但是好多的方法太长了,没法截图,只能把代码贴上来了,最好自己敲一遍代码,俺觉得至少有90%的都会粘贴,但是只有那10%的会提高的更快~俺就是这么敲出来的~代码注释是必要的!

程序创建里面就一个form,然后form上有一个叫panelMonitor的panel,然后就没了,开始敲代码吧~

 

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

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {
        //所有panel的顺序,value值就是该panel的顺序
        public static Dictionary<Panel, int> dicPanelSort = new Dictionary<Panel, int>();
        //所有panel的Y坐标的值
        public static Dictionary<Panel, Point> dicPanelXY = new Dictionary<Panel, Point>();
        //按顺序放的panel数组,下标就是顺序
        public static Panel[] arrayPanelSorted;
        //所有的panel
        public static List<Panel> listPanels = new List<Panel>();
        //panel的高度
        public static int ssheight = 150;
        //每个panel的间隔
        public static int ssmarginTop = 10;
       

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //显示所有的panel
            ShowPanels();
            //滚轮事件
            this.MouseWheel += new MouseEventHandler(Form1_MouseWheel);
            //panelMonitor允许滚动
            this.panelMonitor.AutoScroll = true;
            this.AutoScroll = true;
            //窗体的大小固定为宽1269,高522
            this.Size = new Size(1269, 522);
            this.MinimumSize = new Size(1269, 522);
            this.MaximumSize = new Size(1269, 522);
            //添加滚动条的滚动事件
            panelMonitor.Scroll += new ScrollEventHandler(panelMonitor_Scroll);
        }

        #region  显示所有的panel
        /// <summary>
        /// 显示所有的panel
        /// </summary>
        public void ShowPanels()
        {
            //按顺序初始panel的顺序值
            int sort = 0;
            //panel的纵坐标,起始位10
            int panelX = 10;
            //创建10个panel
            for (int z = 0; z < 10; z++)
            {
                //panel
                Panel panel = new Panel();
                panel.BackColor = Color.White;
                panel.BorderStyle = BorderStyle.FixedSingle;
                panel.Location = new Point(15, panelX);
                panel.Width = panelMonitor.Width - 50;
                panel.Height = 150;
                panel.Parent = panelMonitor;

                //标号
                Label label = new Label();
                label.Text = z.ToString();
                label.Location = new Point(10, 10);
                label.ForeColor = Color.Black;
                label.Font = new System.Drawing.Font("宋体", 20.75f);
                label.Parent = panel;

                //左间距
                int pLeft = 0;
                //右间距
                int pTop = 0;

                //panel  鼠标按下键时的事件
                panel.MouseDown += (sMouseDown, eMouseDown) =>
                {
                    foreach (Panel p in listPanels)
                    {
                        if (p == (Panel)sMouseDown)
                        {
                            ((Panel)sMouseDown).BackColor = Color.Azure;
                        }
                        else
                        {
                            p.BackColor = Color.White;
                        }
                    }
                    //把本panel置为最前
                    ((Panel)sMouseDown).BringToFront();
                    pLeft = eMouseDown.X;
                    pTop = eMouseDown.Y;
                };

                //panel  鼠标在panel上移动时的事件
                panel.MouseMove += (sMouseMove, eMouseMove) =>
                {
                    //把鼠标指针改变为移动的指针
                    this.Cursor = Cursors.SizeAll;
                    if (eMouseMove.Button.ToString().Equals("Left"))
                    {
                        ((Panel)sMouseMove).BackColor = Color.Azure;
                        panel.Left = panel.Location.X + eMouseMove.X - pLeft;
                        panel.Top = panel.Location.Y + eMouseMove.Y - pTop;
                    }
                };

                //panel  鼠标移出panel时的事件
                panel.MouseLeave += (sMouseLeave, eMouseLeave) =>
                {
                    //恢复鼠标为默认的指针
                    this.Cursor = Cursors.Default;
                };

                //panel  鼠标键释放时的事件
                panel.MouseUp += new MouseEventHandler(panel_MouseUp);
                //添加panel
                listPanels.Add(panel);
                //Panel的顺序
                dicPanelSort.Add(panel, sort);
                //Panel的Y坐标
                dicPanelXY.Add(panel, panel.Location);
                //下一个panel的纵坐标位置
                panelX += panel.Height + ssmarginTop;
                //顺序值自加1
                sort++;
            }
        }
        #endregion

        #region  panel  释放鼠标键时的事件
        void panel_MouseUp(object sender, MouseEventArgs e)
        {
            Point p = panelMonitor.DisplayRectangle.Location;
            Point a = listPanels[0].Location;

            //创建排序数组
            arrayPanelSorted = new Panel[dicPanelSort.Count + 1];
            //根据最新的顺序,对应数组下标进行添加
            foreach (Panel panel in dicPanelSort.Keys)
            {
                arrayPanelSorted[dicPanelSort[panel]] = panel;
            }
            //有多个panel时,进行判断
            if (listPanels.Count != 1)
            {
                //panel往下拖(和原来自己的Y坐标比较)
                if (((Panel)sender).Location.Y >= dicPanelXY[((Panel)sender)].Y)
                {
                    //遍历当前顺序的panel数组
                    for (int i = dicPanelSort[(Panel)sender] + 1; i < arrayPanelSorted.Length - 1; i++)
                    {
                        //情况1:小于别的panel
                        if (((Panel)sender).Location.Y <= arrayPanelSorted[i].Location.Y)
                        {
                            //把要挪动的放到数组的最后一个位置
                            arrayPanelSorted[arrayPanelSorted.Length - 1] = (Panel)sender;
                            //从被拖拽的对象的下一个开始,依次覆盖前一个值,直到到目标位置结束
                            for (int j = dicPanelSort[(Panel)sender] + 1; j < i; j++)
                            {
                                //覆盖前一个
                                arrayPanelSorted[j - 1] = arrayPanelSorted[j];
                            }
                            arrayPanelSorted[i - 1] = arrayPanelSorted[arrayPanelSorted.Length - 1];
                            //更新所有集合、数组的位置
                            SortedPanel(arrayPanelSorted);
                            return;
                        }

                        //情况2:伪拖动:没有拖到目标位置,在自己的位置原地拖动。注意!和上边的if位置不可对调,因为当Panel多的时候,后面不止一个的Panel的Y肯定会比当前拖动的Panel的Y要大
                        if (arrayPanelSorted[i].Location.Y - ((Panel)sender).Location.Y >= 0)
                        {
                            //回归位置
                            ((Panel)sender).Location = dicPanelXY[(Panel)sender];
                            return;
                        }
                    }
                    //情况:3:没有小于别的Panel,如果是自己本身就在最下面,还往下拖,位置回归最下面
                    if (arrayPanelSorted[arrayPanelSorted.Length - 1] == (Panel)sender)
                    {
                        //回归位置
                        ((Panel)sender).Location = dicPanelXY[(Panel)sender];
                    }
                    else//情况4:没有小于别的Panel,但自己是被拖拽到最下端,而原来不是在最下端,进行挪动
                    {
                        //把要挪动的放到数组的最后一个位置
                        arrayPanelSorted[arrayPanelSorted.Length - 1] = (Panel)sender;
                        //从被挪动的下一个位置开始一直到数组的最后一个位置,逐个覆盖前一个位置上的数据
                        for (int i = dicPanelSort[((Panel)sender)] + 1; i <= arrayPanelSorted.Length - 1; i++)
                        {
                            //覆盖前一个
                            arrayPanelSorted[i - 1] = arrayPanelSorted[i];
                        }
                        //更新所有集合、数组的位置
                        SortedPanel(arrayPanelSorted);
                    }
                }
                else//panel往上拖
                {
                    for (int i = dicPanelSort[(Panel)sender] - 1; i >= 0; i--)
                    {
                        //情况5:大于别的panel
                        if (((Panel)sender).Location.Y >= arrayPanelSorted[i].Location.Y)
                        {
                            //把要挪动的放到数组的最后一个位置
                            arrayPanelSorted[arrayPanelSorted.Length - 1] = (Panel)sender;
                            //从被拖拽的对象的上一个开始,依次覆盖下一个值,直到到目标位置结束
                            for (int j = dicPanelSort[(Panel)sender] - 1; j > i; j--)
                            {
                                //覆盖后一个
                                arrayPanelSorted[j + 1] = arrayPanelSorted[j];
                            }
                            arrayPanelSorted[i + 1] = arrayPanelSorted[arrayPanelSorted.Length - 1];
                            //更新所有集合、数组的位置
                            SortedPanel(arrayPanelSorted);
                            return;
                        }

                        //情况6:伪拖动:没有拖到目标位置,而是在自己的位置上原地拖动。注意!和上边的if位置不可对调,因为当Panel多的时候,前面不止一个Panem的Y肯定会比当前拖动的Panel的Y要小
                        if (((Panel)sender).Location.Y - arrayPanelSorted[i].Location.Y >= 0)
                        {
                            //回归位置
                            ((Panel)sender).Location = dicPanelXY[(Panel)sender];
                            return;
                        }
                    }
                    //情况7:没有大于别的Panel,如果是自己本身就在最上面,还往上拖,位置回归最上面
                    if (arrayPanelSorted[0] == (Panel)sender)
                    {
                        //回归位置
                        ((Panel)sender).Location = dicPanelXY[(Panel)sender];
                    }
                    else//情况8:没有大于别的Panel,但自己是被拖拽到最上端,而原来不是在最上端,进行挪动
                    {
                        //把要挪动的放到数组的最后一个位置
                        arrayPanelSorted[arrayPanelSorted.Length - 1] = (Panel)sender;
                        //从被挪动的上一个位置开始一直到数据的第一个位置,逐个覆盖自己的数据
                        for (int i = dicPanelSort[(Panel)sender] - 1; i >= 0; i--)
                        {
                            //覆盖后一个
                            arrayPanelSorted[i + 1] = arrayPanelSorted[i];
                        }
                        arrayPanelSorted[0] = arrayPanelSorted[arrayPanelSorted.Length - 1];
                        //更新所有集合、数组的位置
                        SortedPanel(arrayPanelSorted);
                    }
                }
            }
            else//如果是一个,再怎么拖拽,还是原来的位置
            {
                //回归位置
                ((Panel)sender).Location = dicPanelXY[(Panel)sender];
            }
        }
        #endregion

        #region  更新Panel的所有集合、数组的位置
        /// <summary>
        /// 更新Panel的所有集合、数组的位置
        /// </summary>
        /// <param name="arrayPanelSorted"></param>
        public void SortedPanel(Panel[] arrayPanelSorted)
        {
            //排序,i为数组下标,j为排序后的Panel的新的坐标Y值。
            //注意一!!在第三个条件语句j的值是根据i-1得来的,因为i++在前面先加完值了,得找到上一个Panel的高,再计算坐标Y值
            //注意二!!panelMonitor.DisplayRectangle.Location.Y是虚拟区域的Y坐标,所为虚拟区域,就是滚动条滚到下面是,滚出去的区域就是虚拟区域
            for (int i = 0, j = panelMonitor.DisplayRectangle.Location.Y + 10; i < arrayPanelSorted.Length - 1; i++, j += arrayPanelSorted[i - 1].Height + ssmarginTop)
            {
                //对Panel进行排序
                arrayPanelSorted[i].Location = new Point(15, j);
                //更新XY坐标值
                dicPanelXY[arrayPanelSorted[i]] = arrayPanelSorted[i].Location;
                //更新顺序值
                dicPanelSort[arrayPanelSorted[i]] = i;
            }
        }
        #endregion

        #region  鼠标滚轮事件
        /// <summary>
        /// 鼠标滚轮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Form1_MouseWheel(object sender, MouseEventArgs e)
        {
            //获取鼠标的位置
            Point mousePoint = new Point(e.X, e.Y);
            //换算成相对本窗体的位置
            mousePoint.Offset(this.Location.X, this.Location.Y);
            //判断是否在panel内
            if (panelMonitor.RectangleToClient(panelMonitor.DisplayRectangle).Contains(mousePoint))
            {
                //执行滚动
                panelMonitor.AutoScrollPosition = new Point(0, panelMonitor.VerticalScroll.Value - e.Delta);
            }
        }
        #endregion

        #region  添加滚动条的滚动事件
        /// <summary>
        /// 添加滚动条的滚动事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void panelMonitor_Scroll(object sender, ScrollEventArgs e)
        {
            //更新坐标
            foreach (Panel p in listPanels)
            {
                dicPanelXY[p] = new Point(panelMonitor.DisplayRectangle.Location.X + dicPanelXY[p].X, panelMonitor.DisplayRectangle.Location.Y + dicPanelXY[p].Y);
            }
        }
        #endregion
    }
}