使用拖放重新排序winforms列表框?
Posted
技术标签:
【中文标题】使用拖放重新排序winforms列表框?【英文标题】:Reorder a winforms listbox using drag and drop? 【发布时间】:2010-10-22 18:05:49 【问题描述】:这是一个简单的过程吗?
我只是为内部工具编写一个快速的 hacky 用户界面。
我不想花太多时间在上面。
【问题讨论】:
【参考方案1】:这是一个快速而肮脏的应用程序。基本上,我创建了一个带有按钮和 ListBox 的表单。单击按钮时,ListBox 将填充下一个 20 天的日期(必须使用某些东西来进行测试)。然后,它允许在 ListBox 中拖放以进行重新排序:
public partial class Form1 : Form
public Form1()
InitializeComponent();
this.listBox1.AllowDrop = true;
private void button1_Click(object sender, EventArgs e)
for (int i = 0; i <= 20; i++)
this.listBox1.Items.Add(DateTime.Now.AddDays(i));
private void listBox1_MouseDown(object sender, MouseEventArgs e)
if (this.listBox1.SelectedItem == null) return;
this.listBox1.DoDragDrop(this.listBox1.SelectedItem, DragDropEffects.Move);
private void listBox1_DragOver(object sender, DragEventArgs e)
e.Effect = DragDropEffects.Move;
private void listBox1_DragDrop(object sender, DragEventArgs e)
Point point = listBox1.PointToClient(new Point(e.X, e.Y));
int index = this.listBox1.IndexFromPoint(point);
if (index < 0) index = this.listBox1.Items.Count-1;
object data = e.Data.GetData(typeof(DateTime));
this.listBox1.Items.Remove(data);
this.listBox1.Items.Insert(index, data);
【讨论】:
这太好了,谢谢。有两个(非常小的)陷阱。在 _MouseDown 中,选择不会在事件触发之前切换,因此您需要调用 IndexFromPoint 来获取当前选择(并检查列表底部的 -1 含义)。但是,在这里,X 和 Y 已经是客户端坐标,因此您无需调用 PointToClient 在 _DragDrop 中,您还需要检查索引是否为 -1,表示从列表底部删除,然后忽略该放置或将项目移动到底部您认为合适的列表。这两件事不同,这正是我所追求的简单解决方案。 也喜欢这个小代码示例。发现与 Gareth 相同的错误并编辑答案以删除它们,希望如此。 效果很好! “Datatme”需要改成对应的类型 我认为 DragOver 事件还应该包括对数据类型的检查,如果我们不想为 eerything 和任何东西显示放置光标。 ... 如果 e.Data.GetDataPresent(GetType(DateTime)) 那么 ... 请注意,由于处理 MouseDown,这样做会弄乱 SelectedIndexChanged 和 DoubleClick 事件(可能还有其他事件); SelectedIndexChanged 不再因鼠标点击而触发(但仍响应键盘),并且 DoubleClick 变得非常挑剔且难以触发。【参考方案2】:晚了 7 年。但是对于任何新人,这里是代码。
private void listBox1_MouseDown(object sender, MouseEventArgs e)
if (this.listBox1.SelectedItem == null) return;
this.listBox1.DoDragDrop(this.listBox1.SelectedItem, DragDropEffects.Move);
private void listBox1_DragOver(object sender, DragEventArgs e)
e.Effect = DragDropEffects.Move;
private void listBox1_DragDrop(object sender, DragEventArgs e)
Point point = listBox1.PointToClient(new Point(e.X, e.Y));
int index = this.listBox1.IndexFromPoint(point);
if (index < 0) index = this.listBox1.Items.Count - 1;
object data = listBox1.SelectedItem;
this.listBox1.Items.Remove(data);
this.listBox1.Items.Insert(index, data);
private void itemcreator_Load(object sender, EventArgs e)
this.listBox1.AllowDrop = true;
【讨论】:
“itemcreator_Load”函数是否相关?还是只是一个错字?【参考方案3】:如果您从未实施拖放操作,第一次需要几个小时,想要正确完成它并且必须通读文档。尤其是用户取消操作时的即时反馈和恢复列表需要一些思考。将行为封装到可重用的用户控件中也需要一些时间。
如果您从未做过拖放操作,请查看来自 MSDN 的 drag and drop example。这将是一个很好的起点,您可能需要半天时间才能完成工作。
【讨论】:
【参考方案4】:这依赖于上面@BFree 的回答 - 感谢它提供了很多帮助。 我在尝试使用该解决方案时遇到了一个错误,因为我正在为我的列表框使用数据源。出于完整性考虑,如果您尝试直接将项目删除或添加到列表框,则会出现此错误:
// Causes error
this.listBox1.Items.Remove(data);
错误: System.ArgumentException: '设置 DataSource 属性时无法修改项目集合。'
解决方案:更新数据源本身,然后重新绑定到您的列表框。 Program.SelectedReports 是一个 BindingList。
代码:
private void listboxSelectedReports_DragDrop(object sender, DragEventArgs e)
// Get the point where item was dropped.
Point point = listboxSelectedReports.PointToClient(new Point(e.X, e.Y));
// Get the index of the item where the point was dropped
int index = this.listboxSelectedReports.IndexFromPoint(point);
// if index is invalid, put item at the end of the list.
if (index < 0) index = this.listboxSelectedReports.Items.Count - 1;
// Get the item's data.
ReportModel data = (ReportModel)e.Data.GetData(typeof(ReportModel));
// Update the property we use to control sorting within the original datasource
int newSortOrder = 0;
foreach (ReportModel report in Program.SelectedReports)
// match sorted item on unique property
if (data.Id == report.Id)
report.SortOrder = index;
if (index == 0)
// only increment our new sort order if index is 0
newSortOrder += 1;
else
// skip our dropped item's index
if (newSortOrder == index)
newSortOrder += 1;
report.SortOrder = newSortOrder;
newSortOrder += 1;
// Sort original list and reset the list box datasource.
// Note: Tried other things, Reset(), Invalidate(). Updating DataSource was only way I found that worked??
Program.SelectedReports = new BindingList<ReportModel>(Program.SelectedReports.OrderBy(x => x.SortOrder).ToList());
listboxSelectedReports.DataSource = Program.SelectedReports;
listboxSelectedReports.DisplayMember = "Name";
listboxSelectedReports.ValueMember = "ID";
其他说明: BindingList 在这个命名空间下:
using System.ComponentModel;
在向列表动态添加项目时,请确保填充排序属性。我使用了一个整数字段“SortOrder”。
当您删除一个项目时,我不必担心更新 Sorting 属性,因为它只会产生一个数字间隙,这在我的情况下是可以的,YMMV。
说实话,除了 foreach 循环之外,可能还有更好的排序算法,但在我的情况下,我处理的项目数量非常有限。
【讨论】:
【参考方案5】:另一种方法是使用the list-view 控件,这是资源管理器用来显示文件夹内容的控件。它更复杂,但为您实现了项目拖动。
【讨论】:
...并且不支持简单的事情,例如数据绑定列表项:( ...在列表或详细视图中显示时拖动也不起作用。以上是关于使用拖放重新排序winforms列表框?的主要内容,如果未能解决你的问题,请参考以下文章