如何在拖放过程中自动滚动DataGridView

时间:2010-04-02 16:32:07

标签: c# datagridview scroll

我的C#.NET应用程序中的一个表单有多个DataGridViews,它们实现拖放以移动行。拖放大多数工作正常,但我一直很难将DataGridViews转换为AutoScroll - 当在行的顶部或底部附近拖动一行时,将其向该方向滚动。

到目前为止,我已尝试实施this解决方案的版本。我有一个继承自DataGridView的ScrollingGridView类,它实现了所描述的计时器,并且根据调试器,计时器正在适当地触发,但是计时器代码:

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);
}
据我所知,

没有做任何事情,可能是因为我在表单中有多个DataGridViews。我也尝试修改AutoScrollOffset属性,但这也没有做任何事情。对DataGridView和ScrollBar类的研究似乎没有建议实际使DataGridView滚动的任何其他命令或函数。任何人都可以帮我一个实际滚动DataGridView的函数,或者其他一些解决问题的方法吗?

3 个答案:

答案 0 :(得分:2)

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

在这里有很多有用的答案,只是想为这个问题添加一个不太复杂的解决方案。当在DataGridView中拖动行时,将调用上面的代码。我的被​​命名为“ TargetReasonGrid”。

我添加了一些注释,这些注释解释了我在做什么,但以下是详细说明的步骤:

  1. 转换鼠标位置,使其相对于网格位置(在表单/控件中)

  2. 在显示的网格边缘上设置虚拟区域,鼠标移动将触发滚动

  3. 检查以确保您实际上有不同的行可滚动到

  4. 以1行为增量滚动

感谢C4u(在上面评论),给了我“虚构区域”的想法。

答案 1 :(得分:1)

感谢您对我的问题的帮助,也许我可以帮助您解决问题。

来自this页面:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

//winuser.h constants
private const int WM_VSCROLL = 277; // Vertical scroll
private const int SB_LINEUP = 0;    // Scrolls one line up
private const int SB_LINEDOWN = 1;  // Scrolls one line down
private const int SB_ENDSCROLL = 8; // Ends the scrolling

//Call this when you want to scroll
private void ScrollGridview(int direction)
{
    SendMessage(Handle, WM_VSCROLL, (IntPtr)direction, VerticalScrollBar.Handle);
    SendMessage(Handle, WM_VSCROLL, (IntPtr)SB_ENDSCROLL, VerticalScrollBar.Handle);
}

(第二个SendMessage似乎没有必要,但我把它包括在内以便进行测量)

我编写了一个DataGridView控件,其中包含 this gbogumil的自动滚动解决方案和正确运行的OnPaint突出显示 - 您可以找到它here

我还想指出this control,我刚刚从另一个帖子中找到了它。它看起来非常好,但不幸的是它是GPL,所以你只能将它用于GPL项目。不过,它完成了我们需要的所有事情以及更多。

答案 2 :(得分:1)

我有一段时间没看过这段代码。但不久前我实现了一个支持这个的DataGridView。

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