【问题标题】:ListBox MultiSelect Drag and Drop problemListBox MultiSelect 拖放问题
【发布时间】:2019-06-23 02:47:00
【问题描述】:

我正在尝试在 Windows 窗体中的 ListBox 之间拖放多个项目。我遇到的问题是,如果我选择多个按住 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”之前不释放鼠标左键,则不会选择项目,并且如果我尝试从另一个 ListBox 获取 SelectedIndices,则计数是“选定的项目”,但是当我尝试浏览列表时,我得到一个 IndexOutOfRangeException。

有什么解决办法吗?

重现问题的示例代码: (重现: 1- 选择一个项目 2- 按住 shift 并单击另一个项目,而不是释放 shift 和鼠标按钮,拖动此项目(如果您在“if”中有断点,您将在 SelectedItems 上看到只有 1 个项目))

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

【问题讨论】:

  • 您从哪里获得 SelectedIndex 和 SelectedItems 值?哪一行抛出错误?记录所有这些。
  • @LarsTech 当我在 MouseMove 上获得 SelectedItems 时,只有 1 个项目。当我在另一个 ListBox 中并尝试获取 ListBox1.SelectedItems 时出现错误
  • 问题是,在我开始拖放之前,SelectedItems 是错误的,如果我不释放鼠标按钮,它永远不会选择项目
  • 我添加了一张图片
  • 我编辑了已经存在的代码

标签: c# winforms drag-and-drop listbox


【解决方案1】:

另一个例子,如果您需要知道在 ListBox 中选择了哪些项目,请使用 SHIFT 键创建扩展选择,即使您不需要启动 Draq&amp;Drop 操作:

使用您在问题中提供的数据样本,一个列表

在示例中,List&lt;int&gt; (lbSelectedIndexes) 用于跟踪当前在 ListBox 中选择了哪些项目。仅当使用SHIFT 键执行选择或启动拖放操作后,才会填充此列表。这对于确定选择的类型很有用。

在所有其他情况下,List&lt;int&gt; 为空,SelectedItemsSelectedIndices 集合可用于确定当前选择的项目。

SystemInformation.DragSize 值还用于确定在按下左键的同时移动鼠标指针时是否应启动拖动操作。
启动拖放操作时,无论选择如何执行,都会用与当前选择对应的 ListBox Items 填充新的DataObject
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 并订阅 DragEnterDragDrop 事件。

当鼠标指针进入第二个ListBox客户区时,如果e.Data.GetDataPresent()方法检测到被拖动对象包含List&lt;ListItemsTest&gt;,则触发DragDropEffects.Copy效果。

如果数据格式被接受,则数据对象转换List&lt;ListItemsTest&gt; - 使用IDataObject.GetData() 方法 - 并设置为listBox2DataSource

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

【讨论】:

    【解决方案2】:

    我现在看到了这个问题。有趣的是,按 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);
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      只是为了让您知道我找到了另一个解决方案。如果我在 MouseDown 事件中设置 Capture = false,Items 将按预期工作,我们不需要手动选择。

      例如:

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

      希望对你有帮助!

      【讨论】:

        【解决方案4】:

        我有一个列表框,我希望它可以拖放排序,并且还具有扩展的多选功能。我遇到了与 OP 相同的问题,当使用 shift+click 选择项目时,所选项目被炸毁。如果我在启动 DoDragDrop 之前添加检查以确保在鼠标按下时没有按下修饰键,它可以解决问题。

        (这是一个 VB.net 代码 sn-p,但你明白了)

        Private Sub lbClasses_MouseDown(sender As Object, e As MouseEventArgs) Handles lbClasses.MouseDown
            If e.Button = MouseButtons.Left AndAlso Control.ModifierKeys = 0 Then
                If Not IsNothing(lbClasses.SelectedItem) Then
                    lbClasses.DoDragDrop(lbClasses.SelectedItem, DragDropEffects.Move)
                End If
            End If
        End Sub
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-03-09
          • 1970-01-01
          • 2019-05-28
          • 2011-09-26
          • 2011-06-27
          • 2023-04-10
          相关资源
          最近更新 更多