在 C# Windows 窗体中的同一列表视图控件中重新排序/移动/拖放列表视图项

本文关键字:列表 视图 拖放 排序 新排序 移动 控件 窗体 Windows | 更新日期: 2023-09-27 18:35:33

我在 C# 2008 System Windows 窗体的 LargeIcon 视图中有一个列表视图。现在我想在另一个位置的同一列表视图中移动此 ListView 中的项目 - 我们称之为"拖放"或"项目重新排序"或其他什么。VB 6 能够做到这一点,并在任何列表视图中自动执行此操作。

C# 似乎没有此功能,或者需要先对其进行编码。对于编码,我没有经验,我在互联网上的研究中也没有找到答案。我只发现了一个不起作用的"覆盖程序"。

我不需要任何其他 ListView 控件(如 ObjectListView 或其他控件),也不需要重写过程或创建新的 ListView 控件。我想按原样在Microsoft给出的 ListView 控件中处理它。对此有任何想法。我相信代码将不胜感激我不能自己做,除非它是非常简单的单行。

PS:如果需要移动项目,我需要移动项目的所有属性(文本,标签,图像键,背景色,前景色,名称,工具提示文本等)。我不知道如何做到这一点。我发现的一个提示:它的存在是为了删除一个项目(称为.删除()) 并插入调用的 。插入()。但是有了这些信息,我仍然无法"移动"鼠标完成的项目。我认为列表视图的所有 DragEvents 在这里都起作用,但我不知道如何使用它们以及如何将所选项目(listView1.SelectedItems)复制到正确的位置,并且需要首先获得这个位置。

在 C# Windows 窗体中的同一列表视图控件中重新排序/移动/拖放列表视图项

事实上,Winforms不支持您谈论的功能,而不是C#。C# 与此类功能无关;这是一项 UI 技术功能,而不是语言功能。但是,为了解决这个问题,我们这里几乎没有代码。它支持用于该目的的每个ListViewItemPosition 属性(在LargeIcon视图中)。另一个重要的属性是 AutoArrange ,这应该设置为 false 以使Position生效。这是代码:

ListViewItem heldDownItem;
Point heldDownPoint;
//MouseDown event handler for your listView1
private void listView1_MouseDown(object sender, MouseEventArgs e)
{            
    //listView1.AutoArrange = false;
    heldDownItem = listView1.GetItemAt(e.X,e.Y);
    if (heldDownItem != null) {
      heldDownPoint = new Point(e.X - heldDownItem.Position.X, 
                                e.Y - heldDownItem.Position.Y);
    }
}
//MouseMove event handler for your listView1
private void listView1_MouseMove(object sender, MouseEventArgs e)
{
    if (heldDownItem != null){
        heldDownItem.Position = new Point(e.Location.X - heldDownPoint.X, 
                                          e.Location.Y - heldDownPoint.Y);
    }
}
//MouseUp event handler for your listView1
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
    heldDownItem = null;
    //listView1.AutoArrange = true;         
}

注意:如您所见,我在那里listView1.AutoArrange了 2 个注释的代码行,如果您想reorder而不是更改ListViewItem位置,您可以取消注释这些行。我可以注意到这里有一些闪烁(当您处理 winforms ListView 时这是正常的),因此您应该使用此代码(可以放置在表单构造函数中)来启用DoubleBuffered

typeof(Control).GetProperty("DoubleBuffered", 
                             System.Reflection.BindingFlags.NonPublic |
                             System.Reflection.BindingFlags.Instance)
               .SetValue(listView1, true, null);

我们可以使用以下代码来获取按位置排序的项目

    SortedDictionary<Tuple<int, int>, string> points = new SortedDictionary<Tuple<int, int>, string>();
    string debug1 = "", debug2 = "";
    foreach (ListViewItem item in listView1.Items)
    {
        Tuple<int, int> tp = new Tuple<int,int>(item.Position.Y, item.Position.X);
        points.Add(tp, item.Text);
        debug1 += item.Text;
    }
    foreach (KeyValuePair<Tuple<int, int>, string> kvp in points)
    {
        debug2 += kvp.Value;
    }
    MessageBox.Show(debug1); //orignal order
    MessageBox.Show(debug2); //sort by position

本文中有拖放重新排序的工作示例,或者此处相同。提供的代码还支持插入标记。在文章中的代码下方:

using System;
using System.Drawing;
using System.Windows.Forms;
public class ListViewInsertionMarkExample : Form
{
    private ListView myListView; 
    public ListViewInsertionMarkExample()
    {
        // Initialize myListView.
        myListView = new ListView();
        myListView.Dock = DockStyle.Fill;
        myListView.View = View.LargeIcon;
        myListView.MultiSelect = false;
        myListView.ListViewItemSorter = new ListViewIndexComparer();
        // Initialize the insertion mark.
        myListView.InsertionMark.Color = Color.Green;
        // Add items to myListView.
        myListView.Items.Add("zero");
        myListView.Items.Add("one");
        myListView.Items.Add("two");
        myListView.Items.Add("three");
        myListView.Items.Add("four");
        myListView.Items.Add("five");
        
        // Initialize the drag-and-drop operation when running
        // under Windows XP or a later operating system.
        if (OSFeature.Feature.IsPresent(OSFeature.Themes))
        {
            myListView.AllowDrop = true;
            myListView.ItemDrag += new ItemDragEventHandler(myListView_ItemDrag);
            myListView.DragEnter += new DragEventHandler(myListView_DragEnter);
            myListView.DragOver += new DragEventHandler(myListView_DragOver);
            myListView.DragLeave += new EventHandler(myListView_DragLeave);
            myListView.DragDrop += new DragEventHandler(myListView_DragDrop);
        }
        // Initialize the form.
        this.Text = "ListView Insertion Mark Example";
        this.Controls.Add(myListView);
    }
    [STAThread]
    static void Main() 
    {
        Application.EnableVisualStyles();
        Application.Run(new ListViewInsertionMarkExample());
    }
    // Starts the drag-and-drop operation when an item is dragged.
    private void myListView_ItemDrag(object sender, ItemDragEventArgs e)
    {
        myListView.DoDragDrop(e.Item, DragDropEffects.Move);
    }
    // Sets the target drop effect.
    private void myListView_DragEnter(object sender, DragEventArgs e)
    {
        e.Effect = e.AllowedEffect;
    }
    // Moves the insertion mark as the item is dragged.
    private void myListView_DragOver(object sender, DragEventArgs e)
    {
        // Retrieve the client coordinates of the mouse pointer.
        Point targetPoint = 
            myListView.PointToClient(new Point(e.X, e.Y));
        // Retrieve the index of the item closest to the mouse pointer.
        int targetIndex = myListView.InsertionMark.NearestIndex(targetPoint);
        // Confirm that the mouse pointer is not over the dragged item.
        if (targetIndex > -1) 
        {
            // Determine whether the mouse pointer is to the left or
            // the right of the midpoint of the closest item and set
            // the InsertionMark.AppearsAfterItem property accordingly.
            Rectangle itemBounds = myListView.GetItemRect(targetIndex);
            if ( targetPoint.X > itemBounds.Left + (itemBounds.Width / 2) )
            {
                myListView.InsertionMark.AppearsAfterItem = true;
            }
            else
            {
                myListView.InsertionMark.AppearsAfterItem = false;
            }
        }
        // Set the location of the insertion mark. If the mouse is
        // over the dragged item, the targetIndex value is -1 and
        // the insertion mark disappears.
        myListView.InsertionMark.Index = targetIndex;
    }
    // Removes the insertion mark when the mouse leaves the control.
    private void myListView_DragLeave(object sender, EventArgs e)
    {
        myListView.InsertionMark.Index = -1;
    }
    // Moves the item to the location of the insertion mark.
    private void myListView_DragDrop(object sender, DragEventArgs e)
    {
        // Retrieve the index of the insertion mark;
        int targetIndex = myListView.InsertionMark.Index;
        // If the insertion mark is not visible, exit the method.
        if (targetIndex == -1) 
        {
            return;
        }
        // If the insertion mark is to the right of the item with
        // the corresponding index, increment the target index.
        if (myListView.InsertionMark.AppearsAfterItem) 
        {
            targetIndex++;
        }
        // Retrieve the dragged item.
        ListViewItem draggedItem = 
            (ListViewItem)e.Data.GetData(typeof(ListViewItem));
        // Insert a copy of the dragged item at the target index.
        // A copy must be inserted before the original item is removed
        // to preserve item index values. 
        myListView.Items.Insert(
            targetIndex, (ListViewItem)draggedItem.Clone());
        // Remove the original copy of the dragged item.
        myListView.Items.Remove(draggedItem);
    }
    // Sorts ListViewItem objects by index.
    private class ListViewIndexComparer : System.Collections.IComparer
    {
        public int Compare(object x, object y)
        {
            return ((ListViewItem)x).Index - ((ListViewItem)y).Index;
        }
    }
}

当心你在哪里添加myListView.ListViewItemSorter = new ListViewIndexComparer();,我在大列表中遇到了性能问题。要解决它,请在添加列表视图项后添加比较器。