用户可分类的列表框

介绍 此控件允许用户使用拖放方法对列表框项重新排序。它支持标准的Items属性(带有字符串和自定义对象) 和DataSource属性,如果数据源实现了IList接口(即。BindingList)。 背景 web上有几个允许用户对列表框进行排序的代码片段,但大多数代码片段只支持字符串列表(或任何其他硬编码的列表)。 我的方法基于BFree在StackOverflow上的回答:http://stackoverflow.com/a/805267/540761。 为什么本地的方法不正确? 只要我们只对字符串(或者,更一般地,对一种特定类型)使用Items属性,上面的链接中的代码就很简单也很好。 有时,对字符串以外的项使用数据源(例如,对许多控件使用相同的项列表)是很好的。这也是一个好主意 独立于项目类型的控件。为了实现这一点,我们将创建一个UserSortableListbox类,它继承自System.Windows.Forms.ListBox。 让事情变得简单 我决定假设两件重要的事情来保持代码的简单: UserSortableListbox始终是用户可排序的(没有可能禁用拖放),唯一支持的选择模式是SelectionMode. one。 这两种功能都可以很容易地实现,稍后将对此进行描述。 拖拽 为了允许用户使用拖放重新排序项,我们必须处理三个事件:重新排序的开始(MouseDown事件), 移动元素(拖放)和拖放项目(拖放)。 我们在MouseDown事件之后开始拖放机制: 隐藏,复制Code

protected override void OnMouseDown(MouseEventArgs e)
{
    base.OnMouseDown(e);
    if (SelectedItem == null)
    {
        return;
    }
    sourceIndex = SelectedIndex;
    OnSelectedIndexChanged(e); //(*)
    DoDragDrop(SelectedItem, DragDropEffects.Move);
}

sourceIndex简单地定义在类的某个地方: 隐藏,复制Code

private int sourceIndex = -1;

这里唯一需要解释的是onselectedindexchange(e)。我们需要这个是因为 当处理MouseDown时,selectedindexchange不会启动。 处理物品的移动很简单: 隐藏,复制Code

protected override void OnDragOver(DragEventArgs e)
{
    base.OnDragOver(e);
    e.Effect = DragDropEffects.Move | DragDropEffects.Scroll;
}

现在,最有趣的部分。在发生拖放事件之后,我们就可以完成这项工作,并将拖放的项目移动到适当的位置。 下面是OnDragDrop方法的第一个版本: 隐藏,复制Code

protected override void OnDragDrop(DragEventArgs e)
{
    base.OnDragDrop(e);
    //(1)
    Point point = PointToClient(new Point(e.X, e.Y));
    int index = IndexFromPoint(point); //destination index
    //(2)
    if (index < 0) index = Items.Count - 1;
    
    //(3a)
    if (index > sourceIndex)
    {
        Items.Insert(index + 1, Items[sourceIndex]);
        Items.RemoveAt(sourceIndex);
    }
    //(3b)
    else
    {
        Items.Insert(index, Items[sourceIndex]);
        Items.RemoveAt(sourceIndex + 1);
    }
    //(4)
    SelectedIndex = index;
}

本代码的一些注释: 我们没有像OnMouseDown方法那样的简单方法来指示被删除项的新索引。但是,我们可以使用继承的IndexFromPoint方法, 这就得到了我们想要的东西。唯一要记住的是对e进行变换。X和e。Y坐标到客户端坐标。在这一行中,我们必须决定如何处理将项目拖放到列表框中最后一个元素下面的情况(因为您不能将项目拖出列表框,这是唯一的情况 当用户将一个条目放到最后一个条目下面时,IndexFromPoint将返回-1)。处理这种情况最直观的方法是将目标索引设置为 列表中的最后一个索引。当我们有一个源和目标索引时,我们可以移动项。首先,我们通过在item中再次插入item [sourceIndex]来复制一个项, 然后我们去掉原来的那个。如果目标索引大于(低于)源,从sourceIndex删除将影响目标索引, 所以我们插入的是index + 1。同样,当目标索引小于(上面)源时,在位置索引处插入也会产生影响 sourceIndex,所以我们必须移除sourceIndex + 1。我们删除了先前选中的项,所以是时候在它的新位置重新选择它了。 我们重新创建了基本解决方案。惟一的优点是代码中不再有e.Data.GetData()。幸运的是,添加了数据源支持 现在真的很简单。我们只需要为DataSource和Items字段找到一个通用的类(或接口),这样我们就可以操作它的元素, 特别是提供Count、Insert和RemoveAt方法。Items具有ObjectCollection类型, 它实现了IList、ICollection和IEnumerable。因为IList接口是精确的 我们要搜索的内容,我们可以假设数据源将实现它,我们将创建一个名为items和replace的变量 OnDragDrop方法中的所有条目,它将完成任务并允许我们在UserSortableListbox中使用数据源。 隐藏,复制Code

IList items = DataSource != null ? DataSource as IList : Items;

更多的功能 为了让控件更有用,我们可以添加一个Reorder事件,当用户移动一个项目时触发该事件: 隐藏,复制Code

public class ReorderEventArgs : EventArgs
{
    public int index1, index2;
}
public delegate void ReorderHandler(object sender, ReorderEventArgs e);
public event ReorderHandler Reorder;

index1和index2是移动项的源和目标索引。这是完整的OnDragDrop方法, 包括数据源支持和重新排序事件: 隐藏,复制Code

protected override void OnDragDrop(DragEventArgs e)
{
    base.OnDragDrop(e);
    Point point = PointToClient(new Point(e.X, e.Y));
    int index = IndexFromPoint(point);
    IList items = DataSource != null ? DataSource as IList : Items;
    if (index < 0) index = items.Count - 1;
    if (index != sourceIndex)
    {
        if (index > sourceIndex)
        {
            items.Insert(index + 1, items[sourceIndex]);
            items.RemoveAt(sourceIndex);
        }
        else
        {
            items.Insert(index, items[sourceIndex]);
            items.RemoveAt(sourceIndex + 1);
        }
        if (null != Reorder)
            Reorder(this, new ReorderEventArgs() { index1 = sourceIndex, index2 = index });
    }
    SelectedIndex = index;
}

保持实现简单 正如我上面提到的,我们假设不能禁用拖放,只能禁用SelectionMode。在使用控件时,其中一个是可用的,因此我们应该隐藏AllowDrop 和SelectionMode,并在构造函数中设置正确的值: 隐藏,复制Code

[Browsable(false)]
new public bool AllowDrop
{
    get { return true; }
    set { }
}
[Browsable(false)]
new public SelectionMode SelectionMode
{
    get { return SelectionMode.One; }
    set { }
}
public UserSortableListBox() //this is the constructor
{
    base.AllowDrop = true;
    base.SelectionMode = SelectionMode.One;
}

当然,我们可以添加对那些刚刚禁用的属性的支持。如果您想允许禁用移动项,您只需要进行检查 OnMouseMove和do或don () DoDragDrop()开头的AllowDrop(或另一个新属性)。 支持其他选择模式比较复杂,但仍然很简单。而不是移动一个项并拥有一个sourceIndex,我们将拥有 添加一个sourceIndex[]数组,该数组将从OnMouseDown中的SelectedIndices复制,而primarySourceIndex将包含 单击的项目(也是在OnMouseDown中,它可以从IndexFromPoint获得,而不需要转换坐标)。 然后,在OnDragDrop方法中,我们使用(primarySourceIndex - index)位置:item移动所有项目 在sourceIndex[i]将被移动到sourceIndex[i] + primarySourceIndex -索引位置。 使用的代码 使用这个控件就像使用标准列表框一样简单。Reorder事件在设计器中可用,可以轻松处理。 隐藏,复制Code

userSortableListBox1.Reorder += 
  new synek317.Controls.UserSortableListBox.ReorderHandler(this.optionsListBox_Reorder);
void optionsListBox_Reorder(object sender, UserSortableListBox.ReorderEventArgs e)
{
  //moved index is at e.index2 position
  //or simply at userSortableListBox1.SelectedIndex
  //previously it was at e.index1 position
}

在下载部分,我包含了一个带有已编译控件的.dll文件,因此您可以将其添加到您的项目引用中并开箱即用。 的兴趣点 我不确定为什么selectedindexchange不启动时MouseDown事件被用于列表框。但是,我使用了一个变通方法,我的控件只是简单地启动 从鼠标向下的selectedindexchangeevent。 本文转载于:http://www.diyabc.com/frontweb/news343.html

原文地址:https://www.cnblogs.com/Dincat/p/13437939.html