Custom Scrollbars

Introducing the Cool Scrollbar library, for customizing the appearance of scrollbars

screen-shot

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 project.

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 goto 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 goto the Detours page here:

http://research.microsoft.com/sn/detours

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 window.

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 scrollbars 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 to alter 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 which 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 scrollbar system metrics are changed by the user.

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 either 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;
}

Custom draw scroll bars

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 feint-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 your are on your own.

Conclusion

The Cool Scrollbar library is now in it's 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 incompatibilites 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!