原文地址:http://blog.sina.com.cn/s/blog_621e24e20101501s.html
最近写的项目中,发现用户特喜欢拖拽,继上次写的listviw内外部拖拽后,这回用户又来了,他们想点中一块区域并移动它,比如他最关心某一个区块,就把它拖拽到最上面,以后看着方便。顿时俺就懵了,哈哈,写了好久才写出来,期间遇到了不少的问题,脑袋都想疼了,最困扰的问题其实不是挪动排序,而是虚拟区域的坐标,一句话的事儿,就是没考虑到。这个拖拽会了,我相信大多数拖拽都可以自己写了吧~现在来看一下效果图:
主要思路:挪动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
}
}
浙公网安备 33010602011771号