3
votes

Problème de glisser-déposer ListBox MultiSelect

J'essaie de faire un glisser-déposer de plusieurs éléments entre ListBox dans les formulaires Windows. Le problème que je rencontre est que si je sélectionne plusieurs éléments en maintenant la touche Maj et que j'essaie de le faire glisser et de le déposer sans relâcher la touche, j'obtiens une erreur. En fait, les SelectedIndices et SelectedItems affichent juste 1 élément, celui sur lequel j'ai cliqué en premier, même si plusieurs éléments sont mis en surbrillance dans la zone de liste.

J'utilise SelectionMode = MultiExtended

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; }
        }
    }

Il semble que si je ne relâche pas le bouton gauche de la souris avant de faire "DoDragDrop", les éléments ne sont pas sélectionnés et aussi si j'essaie d'obtenir les SelectedIndices de l'autre ListBox, le Count est le nombre "d'éléments sélectionnés ", mais lorsque j'essaye de naviguer dans la liste, j'obtiens une IndexOutOfRangeException.

entrez la description de l'image ici

Y a-t-il une solution?

Exemple de code pour reproduire le problème: (Reproduire: 1- Sélectionnez un article 2- Maintenez la touche Maj enfoncée et cliquez sur un autre élément, puis sans relâcher Maj et bouton de la souris, faites glisser cet élément (si vous avez un point d'arrêt à l'intérieur du 'if', vous ne verrez qu'un seul élément sur SelectedItems))

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);
        }
    }
}


8 commentaires

Où obtenez-vous les valeurs SelectedIndex et SelectedItems? Quelle ligne jette l'erreur? Documentez tout cela.


@LarsTech Lorsque j'obtiens SelectedItems sur MouseMove, il n'y a qu'un seul élément. Quand je suis dans l'autre ListBox et que j'essaye d'obtenir ListBox1.SelectedItems j'obtiens l'erreur


le problème est qu'avant même de commencer le glisser-déposer, les SelectedItems sont faux, il ne sélectionne jamais les éléments si je ne relâche pas le bouton de la souris


J'ai ajouté une image


J'ai édité le code qui était déjà là


C'est dans la propre ListBox, celle où j'ai sélectionné les éléments, à ce stade, je n'ai pas encore commencé le glisser-déposer


vous pouvez remplacer le composant, vous pouvez utiliser Listbox1 devant, le résultat est le même .. J'ai ajouté un exemple de code


Pour reproduire: - Sélectionnez un élément - Maintenez la touche Maj enfoncée et cliquez sur un autre élément, puis sans relâcher le décalage et le bouton de la souris, faites glisser cet élément (si vous avez un point d'arrêt à l'intérieur du 'si', vous ne verrez qu'un seul élément sur SelectedItems)


3 Réponses :


2
votes

Je vois le problème maintenant. Le plus drôle est que le fait d'appuyer sur la touche Ctrl pour sélectionner des éléments dans la liste fonctionne normalement, mais pas la touche Maj. Ma solution serait de recréer votre propre collection 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);
    }
  }
}


0 commentaires

3
votes

Autre exemple, au cas où vous auriez besoin de savoir quels éléments ont été sélectionnés dans un ListBox, utilisez la touche SHIFT pour créer une sélection étendue, même si vous n'avez pas besoin de lancer une opération Draq & Drop :

En utilisant l'exemple de données que vous avez fourni dans la question, une List .

Dans l'exemple, une List ( lbSelectedIndexes ) est utilisée pour garder une trace des éléments actuellement sélectionné dans la ListBox. Cette liste n'est remplie que lorsque la sélection est effectuée à l'aide de la touche SHIFT , ou après qu'une opération de glisser-déposer est lancée. Cela peut être utile pour déterminer le type de sélection.

Dans tous les autres cas, la List est vide et les collections SelectedItems et SelectedIndices peuvent être utilisées pour déterminer les éléments actuellement sélectionnés.

Le La valeur SystemInformation.DragSize est également utilisée pour déterminer si l'opération de glissement doit être lancée lorsque le pointeur de la souris est déplacé alors que le bouton gauche est pressé.
Lorsque l’opération de glisser-déposer est lancée, un nouveau DataObject est rempli d'éléments ListBox correspondant à la sélection actuelle, quelle que soit la manière dont la sélection a été effectuée.
Les DragDropEffects est défini sur DragDropEffects.Copy .

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;
    }
}

Pour tester les résultats, déposez une autre ListBox ( listBox2 , ici) sur le formulaire, définissez sa propriété AlloDrop sur true et abonnez-vous à DragEnter et DragDrop événements.

Lorsque le pointeur de la souris entre dans la deuxième zone du client ListBox, l'effet DragDropEffects.Copy est déclenché si e.Data.GetDataPresent () détecte que l'objet glissé contient une List .

Si le format de données est accepté, l'objet de données est transformé en une List - en utilisant le


0 commentaires

1
votes

Juste pour vous dire que j'ai trouvé une autre solution. Si je règle Capture = false dans l'événement MouseDown, les éléments fonctionneront comme prévu et nous n'avons pas besoin de faire la sélection manuelle.

Par exemple:

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

J'espère aide!


0 commentaires