ListBox MultiSelect Drag and Drop проблема - PullRequest
0 голосов
/ 30 января 2019

Я пытаюсь сделать перетаскивание нескольких элементов между ListBox в формах Windows.Проблема, с которой я столкнулся, заключается в том, что если я выбрал несколько элементов, удерживая клавишу Shift, и попытался перетащить их, не отпуская клавишу, я получил ошибку.На самом деле SelectedIndices и SelectedItems просто показывают 1 элемент, который я нажал первым, хотя в ListBox выделены несколько элементов.

Я использую SelectionMode = MultiExtended

void ZListBox_MouseMove(object sender, MouseEventArgs e)
{
    if (isDraggingPoint.HasValue && e.Button == MouseButtons.Left && SelectedIndex >= 0)
    {
        var pointToClient = PointToClient(MousePosition);

        if (isDraggingPoint.Value.Y != pointToClient.Y)
        {
            lastIndexItemOver = -1;
            isDraggingPoint = null;

            var dropResult = DoDragDrop(SelectedItems, DragDropEffects.Copy);
        }
    }
}

Кажетсячто если я не отпущу левую кнопку мыши до того, как сделаю «DoDragDrop», элементы не будут выбраны, а также, если я попытаюсь получить SelectedIndices из другого ListBox, Count будет количеством «выбранных элементов», нокогда я пытаюсь перемещаться по списку, я получаю исключение IndexOutOfRangeException.

enter image description here

Есть ли какое-нибудь решение для этого?

Пример кодавоспроизвести проблему: (Воспроизвести: 1- Выберите элемент 2 - Удерживайте клавишу Shift и щелкните в другом элементе, затем, не отпуская Shift и кнопку мыши, перетащите этот элемент (если у вас есть точка останова внутри 'if', выувидеть только 1 элемент на SelectedItems))

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Load += Form1_Load;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var someList = new List<ListItemsTest>();
            someList.Add(new ListItemsTest() { ID = 1, Name = "Name 1" });
            someList.Add(new ListItemsTest() { ID = 2, Name = "Name 2" });
            someList.Add(new ListItemsTest() { ID = 3, Name = "Name 3" });
            someList.Add(new ListItemsTest() { ID = 4, Name = "Name 4" });
            someList.Add(new ListItemsTest() { ID = 5, Name = "Name 5" });
            listBox1.DisplayMember = "Name";
            listBox1.ValueMember = "ID";
            listBox1.DataSource = someList;
            listBox1.SelectionMode = SelectionMode.MultiExtended;
            listBox1.MouseMove += ListBox1_MouseMove;
            listBox1.AllowDrop = true;
        }

        void ListBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && listBox1.SelectedIndex >= 0)
            {
                var dropResult = DoDragDrop(listBox1.SelectedItems, DragDropEffects.Copy);
            }
        }

        public class ListItemsTest
        {
            public int ID { get; set; }
            public string Name { get; set; }
        }
    }

Ответы [ 3 ]

0 голосов
/ 30 января 2019

Другой пример, если вам нужно узнать, какие элементы были выбраны в ListBox, используйте клавишу SHIFT для создания расширенного выделения, даже если вам не нужно инициировать операцию Draq&Drop:

Используя образец данных, который вы предоставили в вопросе, a List<ListItemsTest>.

В этом примере List<int> (lbSelectedIndexes) используется для отслеживания того, какие элементы в настоящий момент выбраны в ListBox.Этот список заполняется только тогда, когда выбор выполняется с помощью клавиши SHIFT или после запуска операции перетаскивания.Это может быть полезно для определения типа выбора.

Во всех остальных случаях List<int> пуст, а коллекции SelectedItems и SelectedIndices могут использоваться для определения элементов, выбранных в данный момент.

Значение SystemInformation.DragSize также используется для определения необходимости запуска операции перетаскивания при перемещении указателя мыши при нажатой левой кнопке.
При запуске операции перетаскивания новый DataObject заполняется элементами ListBox, соответствующими текущему выбору, независимо от того, как был выполнен выбор.
DragDropEffects устанавливается вDragDropEffects.Copy.

Point lbMouseDownPosition = Point.Empty;
List<int> lbSelectedIndexes = new List<int>();

private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
    var lb = sender as ListBox;
    lbMouseDownPosition = e.Location;
    lbSelectedIndexes = new List<int>();
    int idx = lb.IndexFromPoint(e.Location);
    if (ModifierKeys == Keys.Shift && idx != lb.SelectedIndex) {
        lbSelectedIndexes.AddRange(Enumerable.Range(
            Math.Min(idx, lb.SelectedIndex),
            Math.Abs((idx - lb.SelectedIndex)) + 1).ToArray());
    }
}

private void listBox1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left && 
        ((Math.Abs(e.X - lbMouseDownPosition.X) > SystemInformation.DragSize.Width) || 
         (Math.Abs(e.Y - lbMouseDownPosition.Y) > SystemInformation.DragSize.Height)))
    {
        var lb = sender as ListBox;
        DataObject obj = new DataObject();
        if (lbSelectedIndexes.Count == 0) {
            lbSelectedIndexes = lb.SelectedIndices.OfType<int>().ToList();
        }
        List<object> selection = lb.Items.OfType<object>().Where((item, idx) =>
            lbSelectedIndexes.IndexOf(idx) >= 0).ToList();
        obj.SetData(typeof(IList<ListItemsTest>), selection);

        lb.DoDragDrop(obj, DragDropEffects.Copy);
    }
}

Чтобы проверить результаты, перетащите другой ListBox (listBox2, здесь) в форму,установите для его свойства AlloDrop значение true и подпишитесь на события DragEnter и DragDrop.

Когда указатель мыши входит во вторую клиентскую область ListBox, эффект DragDropEffects.Copy срабатывает, если метод e.Data.GetDataPresent () обнаруживает, что перетаскиваемый объект содержит List<ListItemsTest>.

Если формат данных принят, объект данных преобразуется обратно в List<ListItemsTest> - с использованием метода IDataObject.GetData () - и устанавливается какDataSource из listBox2.

private void listBox2_DragDrop(object sender, DragEventArgs e)
{
    ListBox lb = sender as ListBox;
    if (e.Data != null && e.Data.GetDataPresent(typeof(IList<ListItemsTest>))) {
        lb.DisplayMember = "Name";
        lb.ValueMember = "ID";
        lb.DataSource = e.Data.GetData(typeof(IList<ListItemsTest>));
    }
}

private void listBox2_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(typeof(IList<ListItemsTest>))) {
        e.Effect = DragDropEffects.Copy;
    }
}
0 голосов
/ 27 февраля 2019

Просто чтобы сообщить, что я нашел другое решение.Если я установлю Capture = false в событии MouseDown, элементы будут работать должным образом, и нам не нужно делать ручной выбор.

Например:

void ZListBox_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        Capture = false;
    }
}

Надеюсь, это поможет!

0 голосов
/ 30 января 2019

Я вижу проблему сейчас.Самое смешное, что нажатие клавиши Ctrl для выбора элементов в списке работает нормально, а клавиша Shift - нет.Моим решением было бы воссоздать вашу собственную коллекцию SelectedItems:

void listBox1_MouseMove(object sender, MouseEventArgs e) {
  if (e.Button == MouseButtons.Left && listBox1.SelectedItems.Count > 0) {
    int mouseIndex = listBox1.IndexFromPoint(e.Location);
    if (mouseIndex > -1) {
      ListBox.SelectedObjectCollection x = new ListBox.SelectedObjectCollection(listBox1);
      if (Control.ModifierKeys == Keys.Shift) {
        int i1 = Math.Min(listBox1.SelectedIndex, mouseIndex);
        int i2 = Math.Max(listBox1.SelectedIndex, mouseIndex);
        for (int i = i1; i <= i2; ++i) {
          x.Add(listBox1.Items[i]);
        }
      } else {
        x = listBox1.SelectedItems;
      }
      var dropResult = DoDragDrop(x, DragDropEffects.Move);
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...