我的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的函数,或者其他一些解决问题的方法吗?
答案 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行为增量滚动
感谢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);
}
}