如何使用ScrollableControl并将AutoScroll设置为false

我有一个自定义控件,可以放大自定义绘制的文档canvas。

我尝试使用AutoScroll但它没有给出满意的结果。 当我将AutoScrollPosition和AutoScrollMinSize背靠背(以任何顺序)设置时,它会强制绘画并在每次变焦时引起抖动。 我假设这是因为它修改了两个属性时调用了Update而不是Invalidate。

我现在手动设置Horizo​​ntalScroll和VerticalScroll属性, AutoScroll设置为false ,因此每次缩放级别或客户端大小更改时:

int canvasWidth = (int)Math.Ceiling(Image.Width * Zoom) + PageMargins.Horizontal; int canvasHeight = (int)Math.Ceiling(Image.Height * Zoom) + PageMargins.Vertical; HorizontalScroll.Maximum = canvasWidth; HorizontalScroll.LargeChange = ClientSize.Width; VerticalScroll.Maximum = canvasHeight; VerticalScroll.LargeChange = ClientSize.Height; if (canvasWidth > ClientSize.Width) { HorizontalScroll.Visible = true; } else { HorizontalScroll.Visible = false; HorizontalScroll.Value = 0; } if (canvasHeight > ClientSize.Height) { VerticalScroll.Visible = true; } else { VerticalScroll.Visible = false; VerticalScroll.Value = 0; } int focusX = (int)Math.Floor((FocusPoint.X * Zoom) + PageMargins.Left); int focusY = (int)Math.Floor((FocusPoint.Y * Zoom) + PageMargins.Top); focusX = focusX - ClientSize.Width / 2; focusY = focusY - ClientSize.Height / 2; if (focusX  canvasWidth - ClientSize.Width) focusX = canvasWidth - ClientSize.Width; if (focusY  canvasHeight - ClientSize.Height) focusY = canvasHeight - ClientSize.Height; if (HorizontalScroll.Visible) HorizontalScroll.Value = focusX; if (VerticalScroll.Visible) VerticalScroll.Value = focusY; 

在这种情况下, FocusPoint是一个PointF结构,它保存用户所关注的位图中的坐标(例如,当他们将鼠标滚轮放大以放大时,他们将焦点放在当时的当前鼠标位置上)。 此function在很大程度上起作用。

什么不起作用是滚动条。 如果用户尝试通过单击任一滚动条手动滚动,它们都会继续返回0.我不会在代码中的任何其他位置设置它们。 我尝试在OnScroll()方法中编写以下内容:

 if (se.ScrollOrientation == ScrollOrientation.VerticalScroll) { VerticalScroll.Value = se.NewValue; } else { HorizontalScroll.Value = se.NewValue; } Invalidate(); 

但这会导致一些非常不稳定的行为,包括轻弹和滚动越界。

我该怎么写OnScroll的代码? 我已经尝试了base.OnScroll但是当AutoScroll设置为false时它没有做任何事情。

我最终通过创建3个子控件来实现我自己的自定义滚动:HScrollBar,VScrollBar和Panel。

我像这样隐藏ClientSize和ClientRectangle:

 public new Rectangle ClientRectangle { get { return new Rectangle(new Point(0, 0), ClientSize); } } public new Size ClientSize { get { return new Size( base.ClientSize.Width - VScrollBar.Width, base.ClientSize.Height - HScrollBar.Height ); } } 

布局在OnClientSizeChanged中完成:

 protected override void OnClientSizeChanged(EventArgs e) { base.OnClientSizeChanged(e); HScrollBar.Location = new Point(0, base.ClientSize.Height - HScrollBar.Height); HScrollBar.Width = base.ClientSize.Width - VScrollBar.Width; VScrollBar.Location = new Point(base.ClientSize.Width - VScrollBar.Width, 0); VScrollBar.Height = base.ClientSize.Height - HScrollBar.Height; cornerPanel.Size = new Size(VScrollBar.Width, HScrollBar.Height); cornerPanel.Location = new Point(base.ClientSize.Width - cornerPanel.Width, base.ClientSize.Height - cornerPanel.Height); } 

每个ScrollBar的Scroll事件都订阅了以下内容:

 private void ScrollBar_Scroll(object sender, ScrollEventArgs e) { OnScroll(e); } 

最后,我们可以允许MouseWheel事件使用以下内容滚动:

 protected override void OnMouseWheel(MouseEventArgs e) { int xOldValue = VScrollBar.Value; if (e.Delta > 0) { VScrollBar.Value = (int)Math.Max(VScrollBar.Value - (VScrollBar.SmallChange * e.Delta), 0); OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, xOldValue, VScrollBar.Value, ScrollOrientation.VerticalScroll)); } else { VScrollBar.Value = (int)Math.Min(VScrollBar.Value - (VScrollBar.SmallChange * e.Delta), VScrollBar.Maximum - (VScrollBar.LargeChange - 1)); OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, xOldValue, VScrollBar.Value, ScrollOrientation.VerticalScroll)); } } 

对于自定义绘制,您将使用以下语句:

 e.Graphics.TranslateTransform(-HScrollBar.Value, -VScrollBar.Value); 

使用AutoScroll时,这样可以完美地避免出现故障。