ScrollCall is a demo program that takes a sample of Windows standard controls, along with a standard GDI image, and arranges them on a Device Context (or DC), in a window. Depending on the dimensions of the image, and the size of the containing window, horizontal and/or system scrollbars become visible, to enable scrolling for the image and controls. Thus ScrollCall is as at least as much focused on sizing as it is scrolling, and both offer unique challenges for the programmer, as will be seen in the following sections.
- System scroll bars
- Optional groupbox
- Button to open images on the Device Context (DC)
- Radio options for choice of window scroll function
- Checkbox to stretch rather than scroll the image
- Label Paint Mult with UpDown and Buddy to increase the wait times of
WM_SIZE during sizing, thus reduced
- Right click for system snapshot of view in default or monitor attached to desktop
- Double-click to print the visible part of the (mostly empty) client window to the DC, and back to the client window (experimental)
ScrollCall temporarily turns on
SPI_SETDRAGFULLWINDOWS for the testing of the visual effects of dragging, if ever it was toggled off
- Compatibility with
The objective of a Graphical User Interface (GUI) based app is to promote a clear, uncluttered, and sizable form for ease of use.
ScrollCall considers the scenario of sizing, scaling and scrolling controls adjacent to an image background which can also be scrolled with the same scrollbar. A handy feature if any controls containing text are too small to read.
ScrollCall is intended to display the capabilities of the scrolling and sizing functions under a variety of conditions, but it has only been tested on (by today's standards at least), slow rigs.
In the next section, the purpose and utility of the main functions in
ScrollCall will have the focus, leaving the finer details to the reader.
Using the Code
ScrollCall is tagged as C++, every attempt has been made to make it as "C-friendly" as possible, so that a future conversion to C may be possible. For best results, build
ScrollCall as a debug in Visual Studio. Alternatively, a release executable is included in the attachment, or made available at Github.
ScrollCall, as per requirement of
ScrollWindow(Ex), we are obliged to use the
WM_PAINT message to paint the output of the scrolling function. With form sizing, we choose to perform painting for the loaded images and system screenshot with the function
BOOL AdjustImage(HWND hWnd, HBITMAP hBitmap, HBITMAP &hBitmapScroll,
HGDIOBJ &hdefBitmap, HGDIOBJ &hdefBitmapScroll, BITMAP bmp, HDC& hdcMem,
HDC& hdcMemIn, HDC& hdcMemScroll, HDC hdcScreen, HDC hdcScreenCompat,
HDC hdcWinCl, UINT& bmpWidth, UINT& bmpHeight, int curFmWd, int curFmHt,
int resizePic, int minMaxRestore, BOOL newPic)
This function deals with the handling of the system screenshot as well as any images loaded from the disk, so the parameter list is a bit "overloaded". Most notable are the memory DCs:
hdcMemScroll, the handle to the system screenshot object,
hdcScreen, our compatible version of it,
hdcScreenCompat, and the window DC
hdcWinCl for all image output to our client window. These constitute a fundamental part of our image sandbox.
AdjustImage() is divided into two sections each for both image and screenshot processing. The first section focuses more on manipulation of the data on the memory DCs, the second section is for faster
Blting during sizing moves. If
Stretch is toggled on, the image is painted to the limits of the window rectangle, so additional bitmap analysis is required to establish stretch cutoff co-ordinates to retain a reasonable image quality.
The painting for the printed window is performed by
PaintScrolledorPrinted() (gotten by double-click) called from a timer. The timer is used to avoid possible complications with window (in)validation by scrollbar changes after sizing.
PaintScrolledorPrinted(PAINTSTRUCT *ps, HDC hdcWinCl, HDC hdcMem,
int xNewSize, int yNewSize, UINT bmpWidth, UINT bmpHeight, BOOL printCl, BOOL noFill)
The printed image is not updated during sizing as can happen with the system screenshot.
Here's a typical implementation of
ScrollCall, where the action goes from right to left. The data from the rectangle in
hdcMem is blt'd into
hdcWinCl, the DC for our output client window, thus the visible effect is immediate.
BitBlt(hdcWinCl, wd - xCurrentScroll, -yCurrentScroll, bmpWidth + newPicWd,
bmpHeight, hdcMem, newPicWd, 0, SRCCOPY);
What scrolling function to choose?
ScrollDC() was the function of choice in scrolling the printed window (got by double-click). This is partly because the generation of a complete printed window of a client window is performed in a different way to default image handling.
ScrollDC() works in a different way to the other functions. It will not populate PAINTSTRUCT's
rcPaint with co-ordinates for convenient painting.
ScrollDC() will also not scroll owned/child/parent/sibling windows which may be visible in the DC, so if there are controls not clipped out by the DC, the scrolling image will be visible under them.
ScrollCall can also use the
groupbox around the controls to hide the scrolling around them.
PaintScrolledorPrinted(PAINTSTRUCT *ps, HDC hdcWinCl, HDC hdcMem, int xNewSize,
int yNewSize, UINT bmpWidth, UINT bmpHeight, BOOL printCl, BOOL noFill)
PaintScrolledorPrinted(), mentioned in the last section, effectively complements
AdjustImage() for all the painting in
ScrollCall. The printed window scrolled by
ScrollDC() never gets any data from
*ps, but the window scrolling functions
In comparing the two functions below,
ScrollDC() does not scroll the rectangle containing the controls, whereas the magic required for
ScrollWindowEx() is promulgated by the
ScrollDC(hdcWinCl, -xDelta, 0, (CONST RECT*) &rectControls, (CONST RECT*) &rectControls,
(HRGN)NULL, (RECT*) &rectControls)
ScrollWindowEx(hWnd, -xDelta, 0, (CONST RECT*) NULL, (CONST RECT*) NULL, (HRGN)NULL,
(PRECT)NULL, SW_SCROLLCHILDREN | SW_INVALIDATE);
To test & compare usage with loaded images or system screenshot, we can choose either
ScrollWindowEx() within the program's GUI interface. Albeit little noticeable difference in the performance of the two, even on lightweight systems or power saver plans, the function recommended for general use today is
ScrollWindowEx(). In most situations,
ScrollWindow() operates the same way as
ScrollWindowEx(), but it requires
Invalidaterect() on the scrolled rectangle for
The data required for scrolling is managed by
ScrollInfo(), which processes the various scrolling parameters determined from
ScrollInfo(HWND hWnd, int scrollXorY, int scrollType, int scrollDrag,
int xNewSize, int yNewSize, int bmpWidth, int bmpHeight, BOOL newPic)
Of most interest is when
scrollXorY is zero, which is when a scrollbar is created, changed in some way, or destroyed. The
scrollbar attributes are stored in the SCROLLINFO structure, where notably, the
nPos values correspond with the well used global variables
yCurrentScroll. For example, the vertical height (
nMax) of the system screenshot determines the range of values
For convenience, the default window size after load is based on a multiple of control width (
wd) and height (
ht), which are set as
ht for the Open Image button, half
wd and full
ht for the
wd and half
ht for the rest of the controls. The function
SizeControls() is able to modify the size of the controls, as well as their vertical separation.
SizeControls(int bmpHeight, HWND hWnd, int &yOldScroll, int resizeType,
int curFmWd, int curFmHt, BOOL newPic)
yOldScroll are required when it is desired for a replacement image to be positioned at the last scroll point rather than the zero default. As mouse speed is not tracked during sizing,
ScrollCall generates its own timed sizing with the variables
newCtrlSizeTriggerHt. Because the Open Image button is always set at the top of the client window, the vertical sizing and separation of the other controls require the extra management of a function like
delegateSizeControl(RECT rectOpt, HWND hWndOpt, int oldOptTop,
int resizeType, int oldResizeType, int defOptTop, int yScrollBefNew,
int newCtrlSizeTriggerHt, int newWd, int newHt, int minHt, BOOL newPic, HWND buddyHWnd)
ScrollCall uses the scaling factors
scaleY modified by
GetDims() to meet the various conditions passed from
GetDims(HWND hWnd, int resizeType, int oldResizeType)
Of interest is during sizing, when
firstSizeAfterSTART_SIZE_MOVE is required to set the scale based on the window's previous state. Further, for each image load, the precise dimensions of the client window with any combination of scrollbars flagged by
scrollStat is calculated in:
InitWindowDims(HWND hWnd, int scrollStat, int& xNewSize, int& yNewSize,
UINT bmpWidth, UINT bmpHeight)
Finally, here's a typical implementation of a sizing function used in
SetWindowPos(hWndButton, NULL, -xCurrentScroll,
-yCurrentScroll, newWd, newHt, SWP_NOSENDCHANGING);
SWP_NOSENDCHANGING is added to avoid an extra
WM_SIZE message sent after this statement is executed.
ScrollCall through a debugger, the debug log shows extra info when non zero values of Paint Mult are set. Can be of use when considering the need for less graphic output on high end systems. The format for this output is set out in
ReportErr(const wchar_t* szTypes, ...)
For zero values of Paint Mult, we know there is just about one
WM_PAINT processed for every
WM_SIZE. For non zero values of Paint Mult, the debug program log outputs the frequency of each event, noting that as Paint Mult increases, the
WM_SIZE hits decrease.
As the functionality of
ScrollCall applies to within the dimensions of the current monitor, any painting from moving/sizing outside the screen cannot be relied upon. Consequently, we have:
IsAllFormInWindow(HWND hWnd, BOOL toolTipOn, BOOL isMaximized)
If a "monitor breach" has occurred,
CreateToolTipForRect() for the tooltip notification.
When the setting in Control Panel: System Properties > Advanced > Performance > Settings is turned off,
SetDragFullWindow() turns it on temporarily to show the paint while sizing.
SetDragFullWindow(BOOL dragFullWindow, BOOL restoreDef)
defDragFullWindow holds the setting until it is required to be restored in the function outlined next:
Last, but not least, is the return of
ScrollCall memory allocations to the shared memory pool, by releasing, deleting, freeing, or just zeroing them.
Kleenup() is also required before each loading or reloading of an image.
Kleenup(HWND hWnd, HBITMAP& hBitmap, HBITMAP& hBitmapScroll, HGDIOBJ hdefBitmap,
HGDIOBJ hdefBitmapScroll, HBITMAP& hbmpCompat, GpBitmap*& pgpbm, HDC& hdcMem,
HDC& hdcMemIn, HDC& hdcMemScroll, HDC& hdcWinCl, int typeOfDC, BOOL noExit)
Points of Interest
For further reading, this article on painting and this article on scrolling complement the above subject matter rather well.
For more information and ideas for expansion on the project, please refer the Github wiki, as well as ScrollCall's known issues, logged there.
Scroll up! Scroll up! for the last bit.
We all know something is unscaled because no-one has ever attempt to scale it, yet something that is unscrolled? We are all sure to have scrolled it!
- 30th January, 2021: Version 1.0
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.