很久没有写过WinForm的程序了,这几天整理了下以前写过的几个控件,发现一些比较有用的扩展。今天介绍一个自定义控件DragListView,可以拖拽其中的行,移动它们的位置,从而重新排序。
这个控件适用于使用Details视图显示项的情况。
原理:继承ListView,当点击选中一个列表项时,拖动鼠标时,释放鼠标时,重写相关拖拽方法,实现拖动效果,将选中的列表项插入到拖拽位置。拖动鼠标的过程中,通过判断拖动的数据和位置来设置拖拽的效果。
1、重写如下几个关于拖拽的方法:
OnItemDrag:启动拖拽,设置拖拽的数据和效果。
OnDragEnter:拖拽进入ListView,判断拖拽的数据格式,并设置拖拽的效果。
OnDragOver:拖动经过ListView时,设置拖动的效果,显示拖放位置线。
OnDragDrop:拖拽释放,移动行。
为了判断拖动的方向,还需要重写OnMouseDown,获取鼠标按下时的坐标,和拖拽释放时的坐标进行比较,判断出向上或向下。
2、为了直接拖出来控件就能使用,这里重写了几个属性,设置了默认值。
AllowDrop:指示控件是否可以接受用户拖到它上面的数据。默认为True。
FullRowSelect:指示当项被选中时,其所有子项是否同该项一起突出显示。默认为True。
GridLines:指示是否在项和子项周围显示网格线,仅在“详细信息”视图中显示。默认为True。
View:选择可以显示项的五种不同视图中的一种。默认为Details。
Sorting:指示对项进行排序的方式,默认为None才不会自动排序。
看看效果:
(1)拖拽中:
(2)拖拽释放:
下边看看代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Collections; namespace VeryCodes.Windows.Forms { public class DragAndDropListView : ListView { #region 私有变量 /// <summary> /// 上一个拖动悬停的项 /// </summary> private ListViewItem lastHoverItem; /// <summary> /// 指示是否允许拖动行重新排序 /// </summary> private bool allowDragRowReorder; /// <summary> /// 上一次鼠标按下时Y轴坐标,用于判断拖动上下方向 /// </summary> private int lastMouseDownY = -1; /// <summary> /// 上一次鼠标按下时X轴坐标,用于判断拖动左右方向 /// </summary> private int lastMouseDownX = -1; /// <summary> /// 拖拽位置线的颜色 /// </summary> private Color lineColor; #endregion #region 公有属性 /// <summary> /// 获取或设置一个值,该值指示是否允许拖动行重新排序。 /// </summary> [Description("指示是否允许拖动行重新排序。")] [Category("DragListView")] [DefaultValue(true)] public bool AllowDragRowReorder { get { return this.allowDragRowReorder; } set { this.allowDragRowReorder = value; } } /// <summary> /// 获取或设置一个值,该值指示控件是否可以接受用户拖到它上面的数据。 /// </summary> [Description("指示控件是否可以接受用户拖到它上面的数据。")] [Category("DragListView")] [DefaultValue(true)] public new bool AllowDrop { get { return base.AllowDrop; } set { base.AllowDrop = value; } } /// <summary> /// 获取或设置一个值,该值指示当项被选中时,其所有子项是否同该项一起突出显示。 /// </summary> [Description("指示当项被选中时,其所有子项是否同该项一起突出显示。")] [Category("DragListView")] [DefaultValue(true)] public new bool FullRowSelect { get { return base.FullRowSelect; } set { base.FullRowSelect = value; } } /// <summary> /// 获取或设置一个值,该值指示是否在项和子项周围显示网格线,仅在“详细信息”视图中显示。 /// </summary> [Description("指示是否在项和子项周围显示网格线,仅在“详细信息”视图中显示")] [Category("DragListView")] [DefaultValue(true)] public new bool GridLines { get { return base.GridLines; } set { base.GridLines = value; } } /// <summary> /// 获取或设置项在控件中的显示方式。 /// </summary> [Description("选择可以显示项的五种不同视图中的一种。")] [Category("DragListView")] [DefaultValue(View.Details)] public new View View { get { return base.View; } set { base.View = value; } } /// <summary> /// 重新设计Sorting属性,原有的属性如果不为None,则不能拖动行变化位置。 /// </summary> [Description("指示对项进行排序的方式。")] [Category("DragListView")] [DefaultValue(View.Details)] public new SortOrder Sorting { get { return SortOrder.None; } set { base.Sorting = SortOrder.None; } } /// <summary> /// 获取或设置拖拽位置线的颜色。 /// </summary> [Description("获取或设置拖拽位置线的颜色。")] [Category("DragListView")] [DefaultValue(View.Details)] public Color LineColor { get { return lineColor; } set { lineColor = value; } } #endregion /// <summary> /// 初始化DragAndDropListView类的一个新实例 /// </summary> public DragAndDropListView() : base() { this.AllowDragRowReorder = true; this.AllowDrop = true; this.FullRowSelect = true; this.GridLines = true; this.View = View.Details; lineColor = Color.Red; this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); } #region 重写方法 /// <summary> /// 当拖拽释放时的处理 /// </summary> /// <param name="e"></param> protected override void OnDragDrop(DragEventArgs e) { // 不允许拖拽排序,则直接调用重写的基类方法,然后返回。 if (!allowDragRowReorder) { base.OnDragDrop(e); return; } //判断拖拽的数据是否正确 if (!e.Data.GetDataPresent(typeof(DragItemData).ToString()) || ((DragItemData)e.Data.GetData(typeof(DragItemData).ToString())).ListView == null || ((DragItemData)e.Data.GetData(typeof(DragItemData).ToString())).DragItems.Count == 0) { return; } // 获取拖拽的数据 DragItemData data = (DragItemData)e.Data.GetData(typeof(DragItemData).ToString()); // 获取最终悬停的项 Point cp = base.PointToClient(new Point(e.X, e.Y)); ListViewItem dragToItem = base.GetItemAt(cp.X, cp.Y); // 如果没有悬停项,则添加到最后 if (dragToItem == null) { for (int i = 0; i < data.DragItems.Count; i++) { ListViewItem newItem = (ListViewItem)data.DragItems[i]; base.Items.Add(newItem); } } else { // 如果是向下拖拽,则设置拖动的行的Index为拖动到的行的Index+1 int dropIndex = dragToItem.Index; if (this.View == View.Details || this.View == View.List) { if (lastMouseDownY > cp.Y) // 向上 { } else // 向下 { dropIndex++; } } else { if (lastMouseDownX > cp.X) // 向左 { } else //向右 { dropIndex++; } } // 插入拖动的数据到指定的位置 for (int i = data.DragItems.Count - 1; i >= 0; i--) { ListViewItem newItem = (ListViewItem)data.DragItems[i]; base.Items.Insert(dropIndex, newItem); } } // 从拖出ListView中移除数据 if (data.ListView != null) { foreach (ListViewItem itemToRemove in data.ListView.SelectedItems) { data.ListView.Items.Remove(itemToRemove); } } // set the back color of the previous item, then nullify it if (lastHoverItem != null) { lastHoverItem = null; } this.Invalidate(); // call the base on drag drop to raise the event base.OnDragDrop(e); } /// <summary> /// 当拖拽经过项时的处理 /// </summary> /// <param name="e"></param> protected override void OnDragOver(DragEventArgs e) { // 不允许拖拽排序,则直接调用重写的基类方法,然后返回。 if (!allowDragRowReorder) { base.OnDragOver(e); return; } // 如果被拖拽的数据和预期的数据类型无关联,则不处理,否则设置拖拽效果。 if (!e.Data.GetDataPresent(typeof(DragItemData).ToString())) { e.Effect = DragDropEffects.None; base.OnDragOver(e); return; } if (this.Items.Count > 0) { // 获取拖拽当前悬停的项 Point cp = this.PointToClient(new Point(e.X, e.Y)); ListViewItem hoverItem = this.GetItemAt(cp.X, cp.Y); // 如果鼠标移动到项的外边,但是在ListView区域内,X轴移动 if (hoverItem == null) { if (cp.X < this.Location.X + this.Width && cp.X > this.Location.X) { hoverItem = base.GetItemAt(this.Location.X + 20, cp.Y); } } // 获取当前控件 GDI+ 绘图图面 Graphics g = this.CreateGraphics(); // 如果没有就拖到最后一个 if (hoverItem == null) { e.Effect = DragDropEffects.Move; if (lastHoverItem != null) { lastHoverItem = null; this.Invalidate(); } hoverItem = this.Items[this.Items.Count - 1]; DrawHoverLine(cp, hoverItem, g); base.OnDragOver(e); return; } // 判断拖拽当前是否悬停在一个新的项上,如果是则重绘控件 if (lastHoverItem == null || lastHoverItem != hoverItem) { this.Invalidate(); } // 如果是同一行,则不执行返回 foreach (ListViewItem itemToMove in this.SelectedItems) { if (itemToMove.Index == hoverItem.Index) { e.Effect = DragDropEffects.None; //hoverItem.EnsureVisible(); this.Invalidate(); base.OnDragOver(e); return; } } // 绘制拖动放置线 DrawHoverLine(cp, hoverItem, g); lastHoverItem = hoverItem; // 确保悬浮行是可见的 hoverItem.EnsureVisible(); } // 设置拖动效果 e.Effect = DragDropEffects.Move; // 调用重写的基类方法 base.OnDragOver(e); } /// <summary> /// 当拖拽操作进入控件时的处理 /// </summary> /// <param name="e"></param> protected override void OnDragEnter(DragEventArgs e) { // 不允许拖拽排序,则直接调用重写的基类方法,然后返回。 if (!allowDragRowReorder) { base.OnDragEnter(e); return; } // 如果被拖拽的数据和预期的数据类型无关联,则不处理,否则设置拖拽效果。 if (!e.Data.GetDataPresent(typeof(DragItemData).ToString())) { e.Effect = DragDropEffects.None; return; } else { e.Effect = DragDropEffects.Move; } // 调用被重写的基类方法。 base.OnDragEnter(e); } /// <summary> /// 当项被拖拽时的处理 /// </summary> /// <param name="e"></param> protected override void OnItemDrag(ItemDragEventArgs e) { // 不允许拖拽排序,则直接调用重写的基类方法,然后返回。 if (!allowDragRowReorder) { base.OnItemDrag(e); return; } // 触发拖放操作。 base.DoDragDrop(GetDataForDragDrop(), DragDropEffects.Move); // 调用被重写的基类方法。 base.OnItemDrag(e); } /// <summary> /// 当控件失去焦点时的处理 /// </summary> /// <param name="e"></param> protected override void OnLostFocus(EventArgs e) { // 重新设置选中项的背景,移除“上一个悬停项”的值 this.ResetOutOfRange(); // 重绘 this.Invalidate(); // 调用重写的基类方法 base.OnLostFocus(e); } /// <summary> /// 当拖拽操作离开控件时的处理 /// </summary> /// <param name="e"></param> protected override void OnDragLeave(EventArgs e) { // 重新设置选中项的背景,移除“上一个悬停项”的值 ResetOutOfRange(); // 重绘 this.Invalidate(); // 调用重写的基类方法 base.OnDragLeave(e); } /// <summary> /// 重写OnMouseDown,获取鼠标按下时的坐标 /// </summary> /// <param name="e"></param> protected override void OnMouseDown(MouseEventArgs e) { lastMouseDownY = e.Y; lastMouseDownX = e.X; base.OnMouseDown(e); } #endregion #region 私有方法 /// <summary> /// 获取拖拽项 /// </summary> /// <returns></returns> private DragItemData GetDataForDragDrop() { // 创建一个DragItemData类的实例,用于在拖拽过程中进行处理,如项位置变化等 DragItemData data = new DragItemData(this); // 将选中项的副本保存到拖拽项集合中 foreach (ListViewItem item in this.SelectedItems) { data.DragItems.Add(item.Clone()); } return data; } /// <summary> /// /// </summary> private void ResetOutOfRange() { // determine if the previous item exists, // if it does, reset the background and release // the previous item if (lastHoverItem != null) { lastHoverItem = null; } } /// <summary> /// 绘画拖动位置线 /// </summary> /// <param name="cp"></param> /// <param name="hoverItem"></param> /// <param name="g"></param> private void DrawHoverLine(Point cp, ListViewItem hoverItem, Graphics g) { if (this.View == View.Details || this.View == View.List) { if (lastMouseDownY > cp.Y) // 向上 { g.DrawLine(new Pen(lineColor, 2), new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X + this.Bounds.Width, hoverItem.Bounds.Y)); g.FillPolygon(new SolidBrush(lineColor), new Point[] { new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y - 5), new Point(hoverItem.Bounds.X + 5, hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + 5) }); g.FillPolygon(new SolidBrush(lineColor), new Point[] { new Point(this.Bounds.Width - 4, hoverItem.Bounds.Y - 5), new Point(this.Bounds.Width - 9, hoverItem.Bounds.Y), new Point(this.Bounds.Width - 4, hoverItem.Bounds.Y + 5) }); } else // 向下 { g.DrawLine(new Pen(Color.Red, 2), new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + hoverItem.Bounds.Height), new Point(hoverItem.Bounds.X + this.Bounds.Width, hoverItem.Bounds.Y + hoverItem.Bounds.Height)); g.FillPolygon(new SolidBrush(Color.Red), new Point[] { new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5), new Point(hoverItem.Bounds.X + 5, hoverItem.Bounds.Y + hoverItem.Bounds.Height), new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + hoverItem.Bounds.Height + 5) }); g.FillPolygon(new SolidBrush(Color.Red), new Point[] { new Point(this.Bounds.Width - 4, hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5), new Point(this.Bounds.Width - 9, hoverItem.Bounds.Y + hoverItem.Bounds.Height), new Point(this.Bounds.Width - 4, hoverItem.Bounds.Y + hoverItem.Bounds.Height + 5) }); } } else { if (lastMouseDownX > cp.X) // 向左 { g.DrawLine(new Pen(lineColor, 2), new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + hoverItem.Bounds.Height)); g.FillPolygon(new SolidBrush(lineColor), new Point[] { new Point(hoverItem.Bounds.X - 5, hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X + 5, hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + 5) }); g.FillPolygon(new SolidBrush(lineColor), new Point[] { new Point(hoverItem.Bounds.X - 5, hoverItem.Bounds.Y + hoverItem.Bounds.Height), new Point(hoverItem.Bounds.X + 5, hoverItem.Bounds.Y + hoverItem.Bounds.Height), new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5) }); } else // 向右 { g.DrawLine(new Pen(lineColor, 2), new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width, hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width, hoverItem.Bounds.Y + hoverItem.Bounds.Height)); g.FillPolygon(new SolidBrush(lineColor), new Point[] { new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width - 5, hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width + 5, hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width, hoverItem.Bounds.Y + 5) }); g.FillPolygon(new SolidBrush(lineColor), new Point[] { new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width - 5, hoverItem.Bounds.Y + hoverItem.Bounds.Height), new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width + 5, hoverItem.Bounds.Y + hoverItem.Bounds.Height), new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width, hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5) }); } } } #endregion #region DragItemData Class /// <summary> /// 用于封装拖拽项 /// </summary> private class DragItemData { #region Private Members /// <summary> /// 所属的DragAndDropListView /// </summary> private DragAndDropListView m_listView; /// <summary> /// 拖拽项集合 /// </summary> private ArrayList m_dragItems; #endregion #region Public Properties /// <summary> /// 获取所属的DragAndDropListView /// </summary> public DragAndDropListView ListView { get { return m_listView; } } /// <summary> /// 获取拖拽项集合 /// </summary> public ArrayList DragItems { get { return m_dragItems; } } #endregion #region Public Methods and Implementation /// <summary> /// 初始化DragAndDropListView类的一个新实例 /// </summary> /// <param name="listView"></param> public DragItemData(DragAndDropListView listView) { m_listView = listView; m_dragItems = new ArrayList(); } #endregion } #endregion } }
里边注释很详细了,如果还有不清楚的请留言。
发表评论
相关文章
国内AI资源汇总,AI聊天、AI绘画、AI写作、AI视频、AI设计、AI编程、AI音乐等,国内顺畅访问,无需科学上网。
扫码或点击进入:萤火AI大全
文章分类
最新评论