Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To AutoScroll a DataGridView during Drag and Drop

One of the forms in my C# .NET application has multiple DataGridViews that implement drag and drop to move the rows around. The drag and drop mostly works right, but I've been having a hard time getting the DataGridViews to AutoScroll - when a row is dragged near the top or bottom of the box, to scroll it in that direction.

So far, I've tried implementing a version of this solution. I have a ScrollingGridView class inheriting from DataGridView that implements the described timer, and according to the debugger, the timer is firing appropriately, but the timer code:

const int WM_VSCROLL = 277;
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

private void ScrollingGridViewTimerTick(object sender, EventArgs e)
{
    SendMessage(Handle, WM_VSCROLL, (IntPtr)scrollDirectionInt, IntPtr.Zero);
}

doesn't do anything as far as I can tell, possibly because I have multiple DataGridViews in the form. I also tried modifying the AutoScrollOffset property, but that didn't do anything either. Investigation of the DataGridView and ScrollBar classes doesn't seem to suggest any other commands or functions that will actually make the DataGridView scroll. Can anyone help me with a function that will actually scroll the DataGridView, or some other way to solve the problem?

like image 370
Mason Avatar asked Apr 02 '10 16:04

Mason


2 Answers

private void TargetReasonGrid_DragOver(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Move;

    //Converts window position to user control position (otherwise you can use MousePosition.Y)
    int mousepos = PointToClient(Cursor.Position).Y;

    //If the mouse is hovering over the bottom 5% of the grid
    if (mousepos > (TargetReasonGrid.Location.Y + (TargetReasonGrid.Height * 0.95)))
    {
        //If the first row displayed isn't the last row in the grid
        if (TargetReasonGrid.FirstDisplayedScrollingRowIndex < TargetReasonGrid.RowCount - 1)
        {
            //Increase the first row displayed index by 1 (scroll down 1 row)
            TargetReasonGrid.FirstDisplayedScrollingRowIndex = TargetReasonGrid.FirstDisplayedScrollingRowIndex + 1;
        }
    }

    //If the mouse is hovering over the top 5% of the grid
    if (mousepos < (TargetReasonGrid.Location.Y + (TargetReasonGrid.Height * 0.05)))
    {
        //If the first row displayed isn't the first row in the grid
        if (TargetReasonGrid.FirstDisplayedScrollingRowIndex > 0)
        {
            //Decrease the first row displayed index by 1 (scroll up 1 row)
            TargetReasonGrid.FirstDisplayedScrollingRowIndex = TargetReasonGrid.FirstDisplayedScrollingRowIndex - 1;
        }
    }
}

Lots of helpful answers here, just thought I'd add a less-complicated solution to this issue. The code above is called when rows are dragged within a DataGridView. Mine is named "TargetReasonGrid".

I added notes that explain what I'm doing, but here's the steps spelled out:

  1. Convert the mouse position so it's relative to grid locations (within your form/control)

  2. Set imaginary regions on the edge of the displayed grid where mouse travel will trigger a scroll

  3. Check to make sure you actually have different rows to scroll to

  4. Scroll in increments of 1 row

Thanks to C4u (commented above), gave me the "imaginary regions" idea.

like image 127
ryancopester Avatar answered Sep 27 '22 18:09

ryancopester


I haven't looked at this code in a while. But a while back I implemented a DataGridView that supported just this.

    class DragOrderedDataGridView : System.Windows.Forms.DataGridView
{
    public delegate void RowDroppedEventHangler(object source, DataGridViewRow sourceRow, DataGridViewRow destinationRow);
    public event RowDroppedEventHangler RowDropped;

    bool bDragging = false;
    System.Windows.Forms.DataGridView.HitTestInfo hti = null;
    System.Threading.Timer scrollTimer = null;
    delegate void SetScrollDelegate(int value);

    public bool AllowDragOrdering { get; set; }

    protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
    {
        if (AllowDragOrdering)
        {
            DataGridView.HitTestInfo hti = this.HitTest(e.X, e.Y);

            if (hti.RowIndex != -1
             && hti.RowIndex != this.NewRowIndex
             && e.Button == MouseButtons.Left)
            {
                bDragging = true;
            }
        }

        base.OnMouseDown(e);
    }

    protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
    {
        if (bDragging && e.Button == MouseButtons.Left)
        {
            DataGridView.HitTestInfo newhti = this.HitTest(e.X, e.Y);
            if (hti != null && hti.RowIndex != newhti.RowIndex)
            {
                System.Diagnostics.Debug.WriteLine("invalidating " + hti.RowIndex.ToString());
                Invalidate();
            }
            hti = newhti;
            System.Diagnostics.Debug.WriteLine(string.Format("{0:000} {1}  ", hti.RowIndex, e.Location));

            Point clientPoint = this.PointToClient(e.Location);


            System.Diagnostics.Debug.WriteLine(e.Location + "  " + this.Bounds.Size);
            if (scrollTimer == null
            && ShouldScrollDown(e.Location))
            {
                //
                // enable the timer to scroll the screen
                //
                scrollTimer = new System.Threading.Timer(new System.Threading.TimerCallback(TimerScroll), 1, 0, 250);
            }
            if (scrollTimer == null
            && ShouldScrollUp(e.Location))
            {
                scrollTimer = new System.Threading.Timer(new System.Threading.TimerCallback(TimerScroll), -1, 0, 250);
            }

        }
        else
        {
            bDragging = false;
        }

        if (!(ShouldScrollUp(e.Location) || ShouldScrollDown(e.Location)))
        {
            StopAutoScrolling();
        }
        base.OnMouseMove(e);
    }

    bool ShouldScrollUp(Point location)
    {
        return location.Y > this.ColumnHeadersHeight
            && location.Y < this.ColumnHeadersHeight + 15
            && location.X >= 0
            && location.X <= this.Bounds.Width;
    }

    bool ShouldScrollDown(Point location)
    {
        return location.Y > this.Bounds.Height - 15
            && location.Y < this.Bounds.Height
            && location.X >= 0
            && location.X <= this.Bounds.Width;
    }

    void StopAutoScrolling()
    {
        if (scrollTimer != null)
        {
            //
            // disable the timer to scroll the screen
            // 
            scrollTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            scrollTimer = null;
        }
    }

    void TimerScroll(object state)
    {
        SetScrollBar((int)state);
    }

    bool scrolling = false;

    void SetScrollBar(int direction)
    {
        if (scrolling)
        {
            return;
        }
        if (this.InvokeRequired)
        {
            this.Invoke(new Action<int>(SetScrollBar), new object[] {direction});
        }
        else
        {
            scrolling = true;

            if (0 < direction)
            {
                if (this.FirstDisplayedScrollingRowIndex < this.Rows.Count - 1)
                {
                    this.FirstDisplayedScrollingRowIndex++;
                }
            }
            else
            {
                if (this.FirstDisplayedScrollingRowIndex > 0)
                {
                    this.FirstDisplayedScrollingRowIndex--;
                }
            }

            scrolling = false;
        }

    }



    protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
    {
        bDragging = false;
        HitTestInfo livehti = hti;
        hti = null;

        if (RowDropped != null
         && livehti != null
         && livehti.RowIndex != -1
         && this.CurrentRow.Index != livehti.RowIndex)
        {
            RowDropped(this, this.CurrentRow, this.Rows[livehti.RowIndex]);
        }
        StopAutoScrolling();

        Invalidate();
        base.OnMouseUp(e);
    }

    protected override void OnCellPainting(System.Windows.Forms.DataGridViewCellPaintingEventArgs e)
    {
        if (bDragging && hti != null && hti.RowIndex != -1
         && e.RowIndex == hti.RowIndex)
        {
            //
            // draw the indicator
            //
            Pen p = new Pen(Color.FromArgb(0, 0, 215));
            p.Width = 4;
            e.Graphics.DrawLine(p, e.CellBounds.Left, e.CellBounds.Top, e.CellBounds.Right, e.CellBounds.Top);
        }

        base.OnCellPainting(e);
    }
}
like image 40
Greg Bogumil Avatar answered Sep 27 '22 16:09

Greg Bogumil