Переупорядочить / переместить / перетащить ListViewItems в том же элементе управления ListView в C# Windows Forms

У меня есть ListView в LargeIcon View в C# 2008 Система Windows Forms. Теперь я хотел бы переместить элемент этого ListView в том же ListView в другую позицию - назовем его "перетаскивание" или "переупорядочение элементов" или что-то еще. VB 6 способен на это и делает это автоматически в любом listView.

В C#, похоже, нет этой функции, или ее нужно было сначала кодировать. Для кодирования этого у меня нет опыта, и я не нашел ответа в своем исследовании в Интернете. Я нашел только "процедуру переопределения", которая не сработала.

Мне не нужны никакие другие элементы управления ListView (например, ObjectListView или что-то еще), и мне не нужно переопределять процедуры или создавать новый элемент управления ListView. Я хочу обработать это в элементе управления ListView, предоставленном Microsoft, как есть. Любые идеи по этому поводу. Код был бы высоко оценен, я полагаю, что я не смогу сделать это самостоятельно, если это не будет очень простой однострочник.

PS: если необходимо переместить элемент, мне нужно переместить все свойства элемента (текст, тег, ключ изображения, цвет фона, цвет переднего плана, имя, текст подсказки и т. Д.). Я понятия не имею, как это можно сделать. Я нашел один намек на это: он существует для удаления элемента (называемого .Remove()) и вставки с именем .Insert(). Но с этой информацией я все еще не могу сделать "перемещение" элементов мышью. Я думаю, что все DragEvents listView играют здесь роль, но я не знаю, как их использовать и как скопировать выбранные элементы (listView1.SelectedItems) в правильную позицию и необходимость сначала получить эту позицию.

3 ответа

Решение

Фактически, функция, о которой вы говорите, не поддерживается Winforms, а не C#. C# не имеет ничего общего с такой функцией; это функция технологии пользовательского интерфейса, а не языковая функция. Однако, чтобы решить это, у нас мало кода здесь. Поддерживает Position свойство для каждого ListViewItem использовать для этой цели (в 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;         
}

ПРИМЕЧАНИЕ: как вы можете видеть, я оставил 2 строки кода с комментариями listView1.AutoArrange там, если вы хотите reorder вместо изменения ListViewItem Положение вы можете раскомментировать эти строки. Я могу заметить некоторое мерцание здесь (это нормально, когда вы работаете с winforms ListView), поэтому вы должны использовать этот код (может быть помещен в конструктор формы), чтобы включить DoubleBuffered:

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

Рабочий пример переупорядочивания перетаскиванием есть в этой статье или здесь . Предоставленный код также поддерживает метку вставки. Ниже код из статьи:

      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();, у меня были проблемы с производительностью при работе с большим списком. Чтобы решить эту проблему, добавьте Comparer после добавления элементов ListView.

Мы можем использовать следующий код, чтобы получить элемент, упорядоченный по позиции

    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
Другие вопросы по тегам