Cool Scrollbars is a library I have written to customize the standard scrollbars of a window. That library only supports standard window-scrollbars. Separate scrollbar controls are NOT supported.
New Features in Version 1.2
TreeView
problem fixed, thanks go to Diego Tártara - A few other minor problems also fixed
New Features in Version 1.1
- Supports ALL types of window (only in Windows NT, 2000 and XP)
- Simplified demo application
- MFC demo application also included
- Added support for Right-left reading windows
- Changed calling convention of APIs to WINAPI (
__stdcall
) - Completely standalone (no need for c-runtime)
Introduction
Cool Scrollbars is a library I have written to customize the standard scrollbars of a window. Please note that this library only supports standard window-scrollbars. Separate scrollbar controls are NOT supported. If you want to customize a scrollbar control, then you must devise your own method.
Rather than explain how this library works, I will describe how to use the library in your applications. Cool scrollbars behave exactly like normal scrollbars in a window. However, unlike standard scrollbars, cool scrollbars can be heavily customized. The library is written entirely in the C programming language, and can be compiled to a tiny 14kb! With all features enabled, this rises to around 20kb. This is a pretty small overhead for something which is so complex. Although this library does not require any external libraries such as MFC, ATL, WTL, etc., there is NO reason why you cannot use this library in those types of projects.
Features
- Change the size of a window's scrollbars
- Insert buttons into the scrollbar area
- Use standard bitmaps or metafiles on inserted buttons
- Enable tooltips for all inserted buttons
- Enable flat-style and hot-tracked scrollbars
- Custom-draw, so you can paint the scrollbars with your own bitmaps
Starting with version 1.1 of the Cool Scrollbar library, is a new feature which makes it possible to add cool scrollbars to any window in your program, even if you don't have access to the source-code. This includes standard Windows Controls (edit, list-boxes) and the Common Controls (ListView
, TreeView
, etc.).
Unfortunately, this feature is only available when a program is running under Windows NT, 2000 and XP. This is because of the different ways the Windows 9x and NT operating systems map DLLs into a process's address space. This difference prevents a process from hooking API calls when running under Windows 9x, and is the reason why this feature is only available under NT.
Perhaps a later version of the Cool Scrollbar library will address this problem. Until then however, a program which must run under Windows 95, 98 or ME is restricted to having cool-scrollbars only on windows for which the source-code is available.
Enable Cool Scrollbars for ANY Window (New in Version 1.1)
The Cool Scrollbar download now contains an additional library which provides a program with the ability to enable cool-scrollbars for ANY window in that program. This library (coolsb_detours
) uses a technique called binary-rewriting to hook and intercept all of the standard scrollbar API calls inside USER32.DLL. The coolsb_detours
library actually relies on the superb Detours
package available from research.microsoft.com.
In order to use coolsb_detours
, you must go to Microsoft's research site and download the Detours
package. There is only one file you actually need, but the Detours
license agreement prevents me from distributing this file myself. You can go to the Detours
page from the link below:
The only file you need from this package is lib\detours.lib. Simply copy this file into the coolsb_detours directory before you try to compile it. Please read Microsoft's licence agreement for this package before you start - it contains some important information.
Once you have downloaded the Detours
package, you must add the coolsb_detours
project to your workspace, as well as the standard coolsb
project. You do not have to include coolsb_detours
if you don't need the new feature it offers.
There are two new cool-scrollbar API calls which enable and disable support for all types of windows.
BOOL CoolSB_InitializeApp(void);
CoolSB_InitializeApp
allows your program to apply cool-scrollbars to any window created by your program. Note that CoolSB_InitializeApp
does not enable the cool-scrollbars themselves - this is still achieved with the InitializeCoolSB
API call (see below). You would typically call CoolSB_InitApp
at the start of your program.
BOOL CoolSB_UninitializeApp(void);
CoolSB_UninitializeApp
must be called before your program exits (assuming that you also called CoolSB_InitializeApp
)
These two functions will only return successfully when a program is running under Windows NT, 2000 or XP.
Adding Cool Scroll Bars to a Window
To add cool scrollbars to a window, call InitializeCoolSB
, passing the handle to the window. Unless you are using the coolsb_detours
package under Windows NT/2000/XP, you must replace ALL standard scrollbar functions, such as SetScrollInfo
or GetScrollInfo
, with the equivalent CoolSB_xxx
version. There is a cool scrollbar function call for every standard scrollbar API call. These functions behave in the exact same way as the standard API calls. If cool scrollbars haven't been enabled for a window, the cool scrollbar functions will default to the standard API calls automatically. This allows you to turn cool scroll bars on and off without having to write conditional code.
In addition to the standard scrollbar functions, cool scrollbars offer a number of extra features, and also a set of API calls to manipulate these features.
Note: You do NOT need to use the CoolSB_xxx
scrollbar functions if you are using the coolsb_detours
library.
Changing the Size of the Scrollbars
CoolSB_SetSize
allows altering the size of either the horizontal scrollbar, or the vertical scrollbar, or both. You should be very sure that you really want to change the size of a scrollbar, because it could annoy a user who has set their system scrollbar sizes to the exact size that they want.
BOOL CoolSB_SetSize(HWND hwnd, int wBar, int nLength, int nWidth);
By using this API call, you can alter the two dimensions of a scrollbar.
wBar
can be one of the following values: SB_HORZ
, SB_VERT
or SB_BOTH
. nLength
refers to either the width of a horizontal scrollbar arrow, or the height of a vertical scrollbar arrow. I chose the term "length" because you can take the length to mean the length of the actual arrow, in the direction it points. nWidth
refers to the height of a horizontal scrollbar arrow, or the width of a vertical scrollbar arrow. This parameter is probably the one most people think of when they think of scrollbar "size", because it alters not only the width of the arrow, but the scrollbar margin and the thumb width as well.
By specifying a non-negative integer number, you can specify the size, in pixels, of a scrollbar arrow. The scrollbar size will remain unaffected by the system scrollbar settings, even if the user changes these settings whilst your program is running.
Important: By specifying a negative number, you can set the scrollbar dimensions to a multiple of the system scrollbar sizes. A value of -1
or SYSTEM_METRIC
results in scrollbars that are the exact same size as a normal scrollbar. A value of -2
results in scrollbars twice the size of a standard scrollbar, and so on. By using negative values, the cool scrollbars will always be sized according to the system scrollbar metrics. You do not have to call this function every time the user changes the scrollbar system metrics.
You should try to assign the same number to both the length and the width. If you use different values, then your scrollbars will looked squashed in one dimension.
Making Flat Scrollbars
The Cool Scrollbar library supports Flat scrollbars, like the variety found in the common controls library. Unlike the Microsoft flat scrollbars, the cool scrollbars only support two types of flat scrollbars. These are standard flat-looking scrollbars, without "hot-tracking", and normal flat scrollbars, with "hot-tracking" enabled. Hot tracking is the feature that highlights a portion of a scrollbar when the mouse moves over it.
The CoolSB_SetFlatMode
API allows you to give a cool scrollbar enabled window flat scrollbars.
BOOL CoolSB_SetFlatMode(HWND hwnd, int wBar, UINT nFlatMode);
You must specify which scrollbar of a window will be displayed as a flat scrollbar, by using the wBar
parameter. This can be SB_HORZ
, SB_VERT
, or SB_BOTH
if you want both scrollbars to become flat looking. The nFlatMode
parameter can be one of three values.
CSBS_FLAT | Normal looking flat scrollbars |
CSBS_HOTTRACKED | Hot-tracked flat scrollbars |
CSBS_NORMAL | Remove the flat look |
Inserting a Button Into a Cool Scroll Bar
There are four functions available to insert a button into a scrollbar. You can insert buttons to the left or right of a horizontal scrollbar, and above or below a vertical scrollbar.
All of these functions use the SCROLLBUT structure to specify the attributes of each button. You must use the fMask
member to specify which members of the SCROLLBUT
structure contain valid information.
BOOL CoolSB_InsertButton(HWND hwnd, int wSBflags, UINT nPos, SCROLLBUT *psb);
wSBflags
must be either SB_HORZ
or SB_VERT
nPos
is an integer value specifying the position of the inserted button. This can be zero to insert the button in front of all other buttons, or -1
to insert the button after any others. psb
is the address of a SCROLLBUT
structure which specifies the button properties.
The remaining button functions all deal with buttons that are already inserted into a scrollbar.
BOOL CoolSB_ModifyButton(HWND hwnd, int wSBflags, UINT uItem, BOOL fByCmd,
SCROLLBUT *psb);
BOOL CoolSB_RemoveButton(HWND hwnd, int wSBflags, UINT uItem, BOOL fByCmd);
BOOL CoolSB_GetButton (HWND hwnd, int wSBflags, UINT uItem, BOOL fByCmd,
SCROLLBUT *psb);
wSBflags
must be either SB_HORZ
or SB_VERT
- If
fByCmd
is TRUE
, then uItem
is the command identifier of the button, which is specified by the uCmdId
member of the SCROLLBUT
structure when a button is inserted.
If fByCmd
is FALSE
, then uItem
is a non-negative integer specifying the position of the button to modify / remove. psb
is the address of a SCROLLBUT
structure.
Inserted buttons will receive mouse click notifications The NM_CLICK
message will be sent for mouse-down events, and the normal WM_COMMAND
message for mouse-up events. The NM_CLICK
message (sent in the form of a WM_NOTIFY
) uses the NMCOOLBUTMSG structure, which contains useful information such as the coordinates of the button.
Tooltips for Inserted Buttons
When COOLSB_TOOLTIPS
is defined in the userdefs.h file, tooltip notifications will be sent to the window if the mouse hovers over one of the inserted buttons. By ignoring these messages, tooltips will not be displayed. However, by handling the WM_NOTIFY
message correctly, it is simple to add tooltips to any of the inserted buttons.
You only need to respond to the standard TTN_GETDISPINFO
notification in order to display a tooltip. The button's command identifier will be specified in the hdr.idFrom
member of the NMTTDISPINFO
structure. Below is the sample code required to support tooltips.
case WM_NOTIFY:
{
NMTTDISPINFO *ttdi;
ttdi = (NMTTDISPINFO *)lParam;
if(ttdi->hdr.code == TTN_GETDISPINFO)
{
wsprintf(ttdi->lpszText, "This is button %d", ttdi->hdr.idFrom);
ttdi->hinst = hInst;
}
return 0;
}
The cool scrollbar library supports a subset of Custom Draw, the feature found with many of the common controls shipped with Windows. Custom Draw allows an application to completely take over the drawing of a window's scrollbars, and replace the scrollbar graphics with a completely user defined look.
Just like the standard Custom Draw facility, drawing requests are sent to a cool scrollbar enabled window via a WM_NOTIFY
message.
A NMCSBCUSTOMDRAW structure is used to notify you of drawing operations.
Not all of the Custom Draw functionality is implemented for cool scrollbars. Only three notifications will be sent to a window - CDDS_PREPAINT
(to check if you want to custom draw or not), CDDS_POSTPAINT
(after drawing has finished) and CDDS_ITEMPREPAINT
, for each scrollbar item. A pre-paint and post-paint notification is sent one for each scrollbar, whenever it needs to be painted. These two notifications are not sent when the scrollbar gripper (the dead area) needs to be painted; only an item pre-paint is sent in this case.
It is important to note that the return value from the CDDS_ITEMPREPAINT
notification is not currently used. It is assumed that if custom draw is enabled (by returning CDRF_SKIPDEFAULT
in the pre-paint step), then the whole scrollbar will be custom-drawn, so you must draw all portions of a scrollbar if you want to draw any at all.
Removing Cool Scroll Bars
If for some strange reason you want to turn off cool scrollbars, then simply call the UninitializeCoolSB
API, passing the handle to the window. You do not need to call this function when your window is destroyed, as the cool scrollbars will automatically remove themselves in this instance.
A Note About the Source Code
This library is written entirely in the C language, and uses no other library other than the standard win32 API. In order to encapsulate the code, it was necessary to package all of the implementation code into a single file, with all functions given static linkage so they don't conflict with other functions in your projects. This is a drawback of using C. It would be better to split the source into separate files which deal with specific scrollbar functions - such as drawing code in one file, mouse code in another and so on. However, this would require the use of namespaces to encapsulate the library, and namespaces are only available in C++. I thought that reaching a wider audience was more important in this case, but feel free to use your C++ compiler instead.
I should also take this opportunity to defend my use of C++ style comments in a C project. My reason: I find it tedious to use the C-style comments. Also, every C compiler for Windows that I tried accepts C++ comments with no problem. In fact, the new C99 standard allows the use of C++ style comments, but the project will compile fine on older compilers also. So there.
IMPORTANT
There is a file called userdefs.h which can be used to include or exclude certain features of the cool scrollbar library. If you don't want support for inserted buttons, for example, then you can #undef
the INCLUDE_BUTTONS
definition, which will result in a smaller code size. There are many features which can be altered in this way, and they are all fully documented in this file.
Why You Can't Customize a Standard Windows Control
(Only relevant under Windows 95,98,ME)
Someone is bound to complain that they can't add cool scrollbars to a tree-view control, or an edit control, so I'll explain why this isn't possible.
Firstly, it is quite possible to custom-draw the scrollbars on a standard window. All you need to do is call InitializeCoolSB
, after all. However, internally, the standard windows all make use of the standard scrollbar functions such as SetScrollInfo
and SetScrollPos
. This is a problem, because these functions cause a window's scrollbars to be re-drawn, and this redraw does not get performed via a WM_NCPAINT
message. Whenever you resize a standard window, or scroll up and down in one, the standard scrollbar API will be called, and this will cause your nicely drawn custom scrollbars to be over-written. So, unless you have the source-code to a window or control, you cannot apply the cool scrollbar library to it, because you must call the CoolSB_xxx
API functions to perform all scrolling operations instead.
Still not convinced? Well, in theory there is a way to overcome these problems and use the coolscrollbar library on any window, but it is not for the faint-hearted. It is possible to intercept any Windows API call in a program, and perform such tasks as replacing the function call with another user-defined call. For example, you could intercept all scrollbar API calls that your program made, and forward them on to the CoolSB_xxx
versions instead.
There are loads of sources on the net which do API interception. These are the two techniques that I would look at:
- Import Address Table (IAT) patching
This technique could be used with the common controls library, because the library contains a thunk table which contains pointers to the scrollbar functions which reside in USER32.DLL. The IAT can be re-written so that all scrollbar API calls get diverted to the CoolSB_xxx
functions.
- Binary re-writing of the target function
This is more complex, and involves over-writing the start of each scrollbar function actually inside USER32.DLL. You would have to use this technique if you wanted cool scrollbars on an edit or list control. This is because these controls are implemented inside USER32.DLL, and you can't patch the IAT for USER32, simply because it does not import its own API calls. So, you have to intercept the scrollbar API calls inside USER32 itself.
The new coolsb_detours
library uses the second technique above to overcome this problem, but this will only work when your program runs under Windows NT. If you are using the Windows 9x family of operating systems, then you are on your own.
Conclusion
The Cool Scrollbar library is now in its second release (version 1.1). Although I have addressed a couple of minor problems and introduced some new features, you must still be careful when you use this library. There might be bugs or incompatibilities with your projects which I have over-looked. I've tested the library thoroughly with my own projects, but it remains to be seen whether I've got it completely right. Feedback would be appreciated!
Specifies the attributes of a button which has been inserted into a window's scrollbar. This structure is used by the CoolSB_InsertButton
, CoolSB_ModifyButton
and CoolSB_RemoveButton
API calls.
typedef struct
{
UINT <a href="#fMask">fMask</a>;
UINT <a href="#uPlacement">uPlacement</a>;
UINT <a href="#uCmdId">uCmdId</a>;
UINT <a href="#uButType">uButType</a>;
UINT <a href="#uState">uState</a>;
int <a href="#nSize">nSize</a>;
HBITMAP <a href="#hBmp">hBmp</a>;
HENHMETAFILE <a href="#hEmf">hEmf</a>;
HCURSOR <a href="#hCurs">hCurs</a>;
int <a href="#nMinSize">nMinSize</a>;
int <a href="#nMaxSize">nMaxSize</a>;
} SCROLLBUT;
Members
fMask
is a set of flags OR'd together which specify which of the SCROLLBUT
members contain valid information. This member can have one or more of the following values set:
SBBF_TYPE | The nButType member contains valid data |
SBBF_ID | The nCmdId member contains a valid command identifier for the button |
SBBF_STATE | The nState member contains a valid state identifier |
SBBF_PLACEMENT | The nPlacement member contains valid data |
SBBF_SIZE | The nSize member contains valid data |
SBBF_BITMAP | The hBmp member is a valid handle to a Bitmap resource |
SBBF_ENHMETAFILE | The hEmf member is a valid handle to a Enhanced Metafile |
SBBF_CURSOR | The hCurs member is a valid handle to a cursor resource |
SBBF_BUTMINMAX | The nMinSize and nMaxSize members contain valid thumb sizes |
uButType
specifies what type of button will be inserted into the scrollbar. This member can be set to just one of the following values:
SBBT_PUSHBUTTON | The button is a standard pushbutton, with the same look and feel as a normal scrollbar button arrow. |
SBBT_TOGGLEBUTTON | The button can be toggled between an up and down state |
SBBT_FIXED | The button does not respond to mouse clicks |
SBBT_FLAT | The button has no 3d-look |
SBBT_BLANK | The button is a blank area, and does not respond to clicks |
SBBT_DARK | Another type of blank area, but is dark int colour |
SBBT_OWNERDRAW | An NM_CUSTOMDRAW message will be sent to the window to paint the button. |
In addition to the above button types, you can also include one of the following button type modifiers by OR'ing it together with the button type:
SBBM_RECESSED | The button has a recessed look when it is being depressed |
SBBM_LEFTARROW | The button has the standard left arrow bitmap |
SBBM_RIGHTARROW | The button has the standard right arrow bitmap |
SBBM_UPARROW | The button has the standard up arrow bitmap |
SBBM_DOWNARROW | The button has the standard down arrow bitmap |
SBBM_RESIZABLE | The button can be resized using the mouse |
SBBM_TYPE2 | The button uses a different 3d-look |
SBBM_TYPE3 | The button uses a different 3d-look |
uCmdId
specifies a command identifier which is used in the form of a WM_COMMAND
message, which is sent whenever the button is clicked with the mouse.
uState
specifies an initial state for the button, if it is a push-button or toggle-button. Valid values are defined below.
SBBS_NORMAL | The button is in its default, un-pushed state. |
SBBS_PUSHED | The button is in a depressed or "clicked" state. |
nSize
specifies the size of the button. For a button inserted into the horizontal scrollbar, nSize
represents the width of the button, in pixels. For a button inserted into the vertical scrollbar, nSize
represents the height of the button. A non-negative value is used to specify an exact size, in pixels. A negative number can be used to specify the size in multiples of the standard scrollbar button size. A value of -1
produces a button the same size as a scrollbar arrow. A value of -2
gives a button twice the size of a scrollbar button, and so on.
uPlacement
specifies which side of the scrollbar the button should be inserted. This can be one of the following values. The default is SBBP_LEFT
or SBBP_ABOVE
, if nPlacement
is not specified.
SBBP_LEFT | The button is inserted to the left of a horizontal scrollbar. |
SBBP_RIGHT | The button is inserted to the right of a horizontal scrollbar. |
SBBP_ABOVE | The button is inserted above of a vertical scrollbar. |
SBBP_BELOW | The button is inserted below of a vertical scrollbar. |
hBmp
is a handle to a bitmap, which is used as the button's image. The bitmap is centered in the button. If no bitmap is defined, then the button is left blank. Ownership of the bitmap is the responsibility of the user.
hEmf
is a handle to an Enhanced Metafile, which is used as the button's image when a bitmap hasn't been supplied. Ownership of the metafile is the responsibility of the user.
hCurs
is a handle to mouse cursor. This cursor is used whenever the mouse passes over the button. The standard mouse cursor is used if no cursor is specified.
nMinSize
is the minimum size, in pixels, of a resizable button. The uButType
member must include the SBBM_RESIZABLE
flag.
hCurs
is the maximum size, in pixels, of a resizable button. The uButType
member must include the SBBM_RESIZABLE
flag.
Remarks
This structure could be subject to change in future versions of the cool scrollbar library.
Back to the Cool Scrollbar Reference
Specifies a mouse event notification on an inserted scrollbar button. Currently, only NM_CLICK
notifications are supported.
typedef struct
{
NMHDR hdr;
RECT rect;
POINT pt;
UINT uCmdId;
UINT uState;
UINT nBar;
} NMCSBCUSTOMDRAW;
Members
hdr
is the standard NMHDR
structure used in all WM_NOTIFY
messages. The code
member of this structure will NMCUSTOMDRAW
for this message type.
rect
specifies the coordinates of the item to draw. These coordinates are relative to the upper-left corner of the window's non-client area.
pt
specifies the screen coordinates of the cursor, in pixels.
uCmdId
specifies which the command identifier of the scrollbar button.
uState
this member is not currently used.
nBar
specifies which of the window's two scrollbars the button belongs to. This can be either SB_HORZ
or SB_VERT
.
Remarks
This structure could be subject to change in future versions of the cool scrollbar library.
Back to the Cool Scrollbar Reference
Specifies a Custom Draw structure specific to the cool scrollbar library.
typedef struct
{
NMHDR hdr;
DWORD dwDrawStage;
HDC hdc;
RECT rect;
UINT uItem;
UINT uState;
UINT nBar;
} NMCSBCUSTOMDRAW;
Members
hdr
is the standard NMHDR
structure used in all WM_NOTIFY
messages. The code
member of this structure will NMCUSTOMDRAW
for this message type.
dwDrawStage
specifies what the current stage of the custom draw is. The cool scrollbar library supports three possible values for this member:
CDDS_PREPAINT | The scrollbars are just about to be painted. Only the nBar and hdc members contain valid information. You could use this message to select a palette into the device context, for example.
You can return either CDRF_DODEFAULT to let the cool scrollbar paint itself, or CDRF_SKIPDEFAULT to completely take over drawing of the specified scrollbar.
|
CDDS_POSTPAINT | The scrollbars have finished painting. Only the nBar and hdc members contain valid information. You should restore the device context to its original state if you selected any palettes into it.
The return value of this message is not used.
|
CDDS_ITEMPREPAINT | An item needs to be painted. An item could be a scrollbar arrow, the scroll-thumb or a scrollbar margin. Currently, the cool scrollbar library expects all items to be painted by the user if CDRF_SKIPDEFAULT was returned in the pre-paint message. So, the return value of this message is not currently used. |
Currently, no other drawing stage notifications are sent.
hdc
specifies a device context which should be drawn into. This device context does not have any clipping regions defined.
rect
specifies the coordinates of the item to draw. These coordinates are relative to the upper-left corner of the window's non-client area.
uItem
specifies which scrollbar item needs to be drawn. This can be one of the following values:
HTSCROLL_LEFT / HTSCROLL_UP | The left or up arrow button, depending on the value of nBar |
HTSCROLL_RIGHT / HTSCROLL_DOWN | The right or down arrow button |
HTSCROLL_PAGELEFT / HTSCROLL_PAGEUP | The left or upper scrollbar margin, before the scroll thumb |
HTSCROLL_PAGERIGHT / HTSCROLL_PAGEDOWN | The right or lower scrollbar margin |
HTSCROLL_THUMB | The scrollbar thumb |
uState
defines the current state of the item to be painted. This can be one of the following values:
CDIS_HOT | The item is currently under the mouse pointer ("hot"). |
CDIS_DISABLED | The item is disabled. |
CDIS_SELECTED | The item is being activated by the mouse (clicked down on). |
CDIS_DEFAULT | The item is in its normal state. |
nBar
specifies which of the window's two scrollbars the item belongs to. This will be one of four values.
The SB_HORZ
and SB_VERT
identify that the item to draw is part of a horizontal or vertical scrollbar, respectively.
The SB_BOTH
value is used when the scrollbar dead area (the size-grip area) needs to be painted. No pre-paint or post-paint notifications are sent when the gripper-area needs to be painted, because this is always a one-off message.
Lastly, the SB_INSBUT
value is used when an inserted button needs to be painted. In this case, the uItem
member specifies the command identifier of the inserted button. Only a CDDS_ITEMPREPAINT
notification is sent, and the inserted button must have the SBBT_OWNERDRAW
type set.
Remarks
This structure could be subject to change in future versions of the cool scrollbar library.
Back to the Cool Scrollbar Reference
History
- 9th May, 2003: Initial version
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below. A list of licenses authors might use can be found here.