Customize Windows Form Scrollbar
Cody Gray's answer is 100% correct, though I wanted to add more reference material on the subject matter.
Background
The way that Windows Forms creates scrollbars is through using the window styles WS_HSCROLL
and WS_VSCROLL
. Respectively, these styles are responsible for enabling the horizontal and vertical scrollbars for a given HWND
. An HWND
is a native resource handle to a "window", which in .NET lingo corresponds to a Control
.
Thinking from a Windows API perspective, we must set window styles at the time we create the HWND
. This is done through a call to CreateWindow
, CreateWindowEx
, or SetWindowLong
. Naturally, we may begin to think about using P/Invoke to help us out, but this would be quite a burden as it means we would need to re-implement Windows Forms from scratch.
Fortunately, Windows Forms exposes a property, CreateParams
, that can be overridden to specify the exact window styles, among other Control
creation parameters. This property is in turn consumed by the .NET framework so that it can create the HWND
with the appropriate styles when the Control
is instantiated.
Customizing the Scrollbar
Replacing the Windows API functionality for a scrollbar is actually simpler than it may seem; however, this isn't obvious (well for me anyways, I had to sift through .NET source to find the answer). To do this, we must choose the appropriate Control
to inherit from to create our own custom ScrollableControl. If we observe the source code for System.Windows.Forms.ScrollableControl
, we see the following styles are used:
CreateParams cp = base.CreateParams;
if (HScroll || HorizontalScroll.Visible) {
cp.Style |= NativeMethods.WS_HSCROLL;
}
else {
cp.Style &= (~NativeMethods.WS_HSCROLL);
}
if (VScroll || VerticalScroll.Visible) {
cp.Style |= NativeMethods.WS_VSCROLL;
}
else {
cp.Style &= (~NativeMethods.WS_VSCROLL);
}
So, in short, when we extend from ScrollableControl
, the native horizontal and vertical scrollbars are enabled based on its internal logic. We could access the ScrollableControl
's window handle and then call SetWindowLong
to hide the scrollbars; however, we would need to keep track of all places that ScrollableControl
interacts with the Windows API. In fact, there is an internal function Control.UpdateStylesCore()
that is called based on whether or not the scrollbars should be shown. This function effectively reapplies the windows styles above, and it would likely be best not to fight with it. It would be a much cleaner approach to steer away from the Windows API and extend directly from Control
. We can then provide whatever API's we desire to have.
This means we would be looking at re-implementing:
- Updating scrollbars for mouse wheel events.
- Updating scrollbars based on clicking the custom scrollbar buttons and tracks/thumbs.
- Updating scrollbars based on adding/removing/moving/resizing child controls.
- Creating auto-scroll margins.
- Constraints on the client area that affects when scrollbars are visible.
- And so on...
Alternatively, a simple approach might be to create a new UserControl
. This would allow us to use the Visual Studio designer to simplify configuring our scrollbar buttons and tracks.
Whichever path is taken, it will be necessary to see how ScrollableControl
works internally in order to provide a comfortable user-experience.
No, there's no way to change the width of a scrollbar displayed on a single control (although there is a system-wide setting that will affect all scrollbars in all applications).
The ugly truth is that the lowly scrollbar control is far more complicated than it looks. Basically, the scrollbars on the FlowLayoutPanel
are drawn by Windows itself (rather than the .NET Framework) because of the WS_HSCROLL
and/or WS_VSCROLL
window styles that are set for the control behind the scenes. The FlowLayoutPanel
doesn't provide any facility to change or modify how these built-in scrollbars are drawn. Unlike other more advanced modifications in WinForms, there are no such messages we can send to the control's window procedure. And to make matters worse, the scrollbars are drawn in the non-client area of the FlowLayoutPanel
, which means we can't just override its Paint
event and handle drawing the scrollbars ourselves.
Unfortunately, if you really want to customize your scrollbars, you're going to have to hide the built-in scrollbars and roll your own. It's not quite as difficult as it sounds, though, if you're up for it. This article on CodeProject provides a good walk-through on creating your own skinnable scrollbar as a user control and use it as a replacement in the container control of your choice.
This if for C# and in relation to the Article Posted in Accepted Answer by Cody Gray https://stackoverflow.com/a/4326046/4342139 .I'd comment directly there but I'm missing reputation for that.
If you are like me and following the article from Cody Gray's answer, then to hide the scroll bar you'd be using inner and outer panels. This is not optimal solution and quite frankly 'weird and hacky'.I stumbled upon similar problem with horizontal scroll bar and managed to find a solution, and the same thing worked for vertical scroll. It is a bit less hacky way (but not by a lot) and can be achieved with the code below:
panel1.AutoScroll = false;
panel1.VerticalScroll.Maximum = 0;
panel1.VerticalScroll.Visible = false;
panel1.HorizontalScroll.Maximum = 0;
panel1.HorizontalScroll.Visible = false;
panel1.AutoScroll = true;
Notes: Pick Veritcal or Horizontal or both from the code above. It is critical to have AutoScroll Set to false and then to true as otherwise settings are not applied or AutoScroll is disabled. Setting Vertical/Horizontal Scroll Maximum to 0 seems to be only thing that is needed, but throwing Visible to false does not appear to hurt it(doesn't appear to work at all but maybe I am missing something).