ListBox MultiSelect拖放问题

时间:2019-01-29 21:49:01

标签: c# winforms

我正在尝试在Windows窗体的ListBox之间拖放多个项目。我遇到的问题是,如果我选择多个按住Shift键的项目并尝试将其拖放而不释放键,则会出现错误。实际上,即使ListBox中突出显示了多个项目,SelectedIndices和SelectedItems只是显示了一个项目,我首先单击了该项目。

我正在使用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,则Count是“选定的项目”,但是当我尝试浏览列表时,出现了IndexOutOfRangeException。

enter image description here

有什么解决方法吗?

重现此问题的示例代码: (复制: 1-选择一个项目 2-按住Shift键并单击另一个项目,然后按住Shift键并单击鼠标,然后拖动该项目(如果'if'内有断点,则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 :(得分:3)

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

使用您在问题中提供的数据样本 List<ListItemsTest>

在示例中,使用List<int> lbSelectedIndexes )跟踪当前在列表框中选择的项目。仅当使用SHIFT键执行选择时或启动拖放操作后,才会填充此列表。这对于确定选择类型很有用。

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

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,然后订阅< strong> DragEnter DragDrop 事件。

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

如果接受了数据格式,则使用IDataObject.GetData()方法将数据对象转换为List<ListItemsTest>并将其设置为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;
    }
}

答案 1 :(得分:2)

我现在看到了这个问题。有趣的是,按Ctrl键可以选择列表中的项目,但可以正常使用,而Shift键则不能。我的解决方案是重新创建自己的SelectedItems集合:

ele     dim1    dim2    dim3
123     DM1     *       *

答案 2 :(得分:0)

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

例如:

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

希望有帮助!