How do I mimic Windows Explorer multi-select/drag-n-drop behavior in a DataGridView?

孤街醉人 提交于 2019-12-20 02:45:10

问题


I'm trying to mimic the way Windows Explorer handles multiple selection. In a default DataGridView, you can select multiple items using Ctrl-click. But if you release the Ctrl key and then try and drag/drop the selected items, it clears the selected items and only selects the "hit" row. I found the following solution somewhere online.

protected override OnMouseDown(MouseEventArgs e)
{
  int hitRowIndex = HitTest(e.X, e.Y).RowIndex;
  if(!SelectedRows.Contains(Rows[hitRowIndex]))
  {
    base.OnMouseDown();
  }
}

However, this causes other side effects. With the CTRL key pressed and mousing down on a selected item, the item remains selected. This makes sense because the mousedown event is bypassed if the row clicked on is selected. From looking at the behavior of Windows Explorer, it looks like the deselection of an item with the CTRL key held is not handled until the MouseUp event. Has anyone tried to do this?


回答1:


I have created a custom component to fix this, and some other annoying issues I had with multi selection in datagridview. This is the code, hope it helps anyone:

public partial class CustomDataGridView : DataGridView
{
    public CustomDataGridView()
    {
        InitializeComponent();
    }
    public CustomDataGridView(IContainer container)
    {
        container.Add(this);

        InitializeComponent();
    }

    private bool _delayedMouseDown = false;
    private Rectangle _dragBoxFromMouseDown = Rectangle.Empty;

    private Func<object> _getDragData = null;
    public void EnableDragDrop(Func<object> getDragData)
    {
        _getDragData = getDragData;
    }

    protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
    {
        base.OnCellMouseDown(e);

        if (e.RowIndex >= 0 && e.Button == MouseButtons.Right)
        {
            var currentRow = this.CurrentRow.Index;
            var selectedRows = this.SelectedRows.OfType<DataGridViewRow>().ToList();
            var clickedRowSelected = this.Rows[e.RowIndex].Selected;

            this.CurrentCell = this.Rows[e.RowIndex].Cells[e.ColumnIndex];

            // Select previously selected rows, if control is down or the clicked row was already selected
            if ((Control.ModifierKeys & Keys.Control) != 0 || clickedRowSelected)
                selectedRows.ForEach(row => row.Selected = true);

            // Select a range of new rows, if shift key is down
            if ((Control.ModifierKeys & Keys.Shift) != 0)
                for (int i = currentRow; i != e.RowIndex; i += Math.Sign(e.RowIndex - currentRow))
                    this.Rows[i].Selected = true;
        }
    }
    protected override void OnMouseDown(MouseEventArgs e)
    {
        var rowIndex = base.HitTest(e.X, e.Y).RowIndex;
        _delayedMouseDown = (rowIndex >= 0 &&
            (SelectedRows.Contains(Rows[rowIndex]) || (ModifierKeys & Keys.Control) > 0));

        if (!_delayedMouseDown)
        {
            base.OnMouseDown(e);

            if (rowIndex >= 0)
            {
                // Remember the point where the mouse down occurred. 
                // The DragSize indicates the size that the mouse can move 
                // before a drag event should be started.                
                Size dragSize = SystemInformation.DragSize;

                // Create a rectangle using the DragSize, with the mouse position being
                // at the center of the rectangle.
                _dragBoxFromMouseDown = new Rectangle(
                    new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize);
            }
            else
                // Reset the rectangle if the mouse is not over an item in the datagridview.
                _dragBoxFromMouseDown = Rectangle.Empty;
        }
    }
    protected override void OnMouseUp(MouseEventArgs e)
    {
        // Perform the delayed mouse down before the mouse up
        if (_delayedMouseDown)
        {
            _delayedMouseDown = false;
            base.OnMouseDown(e);
        }

        base.OnMouseUp(e);
    }
    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);

        // If the mouse moves outside the rectangle, start the drag.
        if (_getDragData != null && (e.Button & MouseButtons.Left) > 0 &&
            _dragBoxFromMouseDown != Rectangle.Empty && !_dragBoxFromMouseDown.Contains(e.X, e.Y))
        {
            if (_delayedMouseDown)
            {
                _delayedMouseDown = false;
                if ((ModifierKeys & Keys.Control) > 0)
                    base.OnMouseDown(e);
            }

            // Proceed with the drag and drop, passing in the drag data
            var dragData = _getDragData();
            if (dragData != null)
                this.DoDragDrop(dragData, DragDropEffects.Move);
        }
    }
}



回答2:


May be this help you:

    protected override void OnMouseDown(MouseEventArgs e)
    {
        int hitRowIndex = HitTest(e.X, e.Y).RowIndex;
        if ((!SelectedRows.Contains(Rows[hitRowIndex])) || ((ModifierKeys & Keys.Control) != Keys.None))
        {
            base.OnMouseDown(e);
        }
    }


来源:https://stackoverflow.com/questions/1942636/how-do-i-mimic-windows-explorer-multi-select-drag-n-drop-behavior-in-a-datagridv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!