As a result, I created my own scrolling by creating 3 child controls: HScrollBar, VScrollBar and panel.
I hide ClientSize and ClientRectangle as follows:
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 ); } }
The layout is executed in 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); }
Each ScrollBar has its own Scroll event, subscribed to the following:
private void ScrollBar_Scroll(object sender, ScrollEventArgs e) { OnScroll(e); }
And finally, we can allow MouseWheel events to scroll with the following:
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)); } }
For custom painting you should use the following instruction:
e.Graphics.TranslateTransform(-HScrollBar.Value, -VScrollBar.Value);
This worked perfectly without crashing when using AutoScroll.