Problem with CS_SAVEBITS class style

  • Thread starter Thread starter Norman Bullen
  • Start date Start date
N

Norman Bullen

Does anyone understand how a window with class style CS_SAVEBITS gets
the bitmap which it uses to restore the screen image when the window
is closed?

I'm trying to trouble-shoot a problem with a complicated dialog. The
area covered by a ComboBox drop-down is not restored correctly when
the drop-down is closed.

The main dialog is almost entirely covered by a large Tab control.
Depending on which tab the user clicks, either of two child dialogs is
shown completely within the area of the Tab control. Then, depending
on the selection in a ComboBox a "grandchild" dialog is displayed.
The grandchild dialog has a couple of check box buttons. These are
partially covered by the ComboBox drop-down and are not restored when
the drop-down closes. It looks sort of like this:
+-----------------------------------------------+
|-----------------------------------------------| <-- main dialog
| +---------+-----------+ |
| | Tab 1 \ Tab 2 \--------------------+ | <-- Tab control
| | - - - - - - - - - - - - - - - - - - - - - | | <-- child dialog
| | +----------------------+ | |
| | | Static | |V| | | | <-- ComboBox
| | +----------------------+ | |
| | | + . . . . . . . . . . . . . . . . . + | | | <-- grandchild
| | . X sllkjfelwjwlejflwejlsjclejlewj . | | dialog
| | | . X lfkewjflsvkvnlewjhvlsnlw . | | | <-- check boxes
| | + . . . . . . . . . . . . . . . . . +
The child dialog and the Tab control are both children of the main
dialog. The ComboBox and grandchild dialog (and some other controls)
are children of the child dialog. The check boxes (and some other
controls) are children of the grandchild dialog.

Looking at the application with Spy++, I discovered that each of the
five ComboBoxes (all children of the child dialog) have an associated
WS_POPUP window which presumably is used to show the drop-down list.
Normally they are not visible. The window class for these windows
(ComboLBox) has the CS_SAVEBITS style which is what prompts me to ask
about that style.

Evidently the drop-down window for this ComboBox somehow does not get
the image of the check boxes text when it becomes visible and so,
when it becomes invisible the check boxes' text is replaced with the
dialog background color.

I captured a message trace with Spy++ showing the various paint and
command messages that show what happens when the drop-down arrow on
the ComboBox is clicked and then something selected from the list.

000E08E6 S ..WM_COMMAND wNotifyCode:CBN_SETFOCUS wID:3 hwndCtl:000E0812
000E08E6 R ..WM_COMMAND
000E08E6 S WM_COMMAND wNotifyCode:CBN_DROPDOWN wID:3 hwndCtl:000E0812
000E08E6 R WM_COMMAND
000E0812 S WM_PAINT hdc:00000000
000E0812 S .WM_ERASEBKGND hdc:7B010806
000E0812 R .WM_ERASEBKGND fErased:True
000E0812 R WM_PAINT
000E0800 S .WM_ERASEBKGND hdc:470106A1
000E0800 R .WM_ERASEBKGND fErased:True
000E0812 S WM_PAINT hdc:00000000
000E0812 S .WM_ERASEBKGND hdc:7B010806
000E0812 R .WM_ERASEBKGND fErased:True
000E0812 R WM_PAINT
000E0812 S WM_COMMAND wNotifyCode:0001 wID:1000 hwndCtl:000E0800
000E08E6 S .WM_COMMAND wNotifyCode:CBN_SELENDOK wID:3 hwndCtl:000E0812
000E08E6 R .WM_COMMAND
000E0812 S .WM_PAINT hdc:00000000
000E0812 S ..WM_ERASEBKGND hdc:44010987
000E0812 R ..WM_ERASEBKGND fErased:True
000E0812 R .WM_PAINT
000E08E6 S .WM_COMMAND wNotifyCode:CBN_CLOSEUP wID:3 hwndCtl:000E0812
000E08E6 R .WM_COMMAND
000E08E6 S .WM_COMMAND wNotifyCode:CBN_SELCHANGE wID:3 hwndCtl:000E0812
000E08E6 R .WM_COMMAND
000E0812 R WM_COMMAND
0009082A P WM_COMMAND wNotifyCode:BN_CLICKED wID:1008 hwndCtl:00090820
00090838 P WM_PAINT hdc:00000000
000E0818 S WM_PAINT hdc:00000000
000E0818 S .WM_NCPAINT hrgn:00000001
000E0818 R .WM_NCPAINT
000E0818 S .WM_ERASEBKGND hdc:E001092F
000E0818 R .WM_ERASEBKGND fErased:True
000E0818 R WM_PAINT
00090838 S WM_ERASEBKGND hdc:44010987
00090838 R WM_ERASEBKGND fErased:True
000E08E6 S .WM_COMMAND wNotifyCode:CBN_SELENDCANCEL wID:3 hwndCtl:000E0812
000E08E6 R .WM_COMMAND
000E08E6 S .WM_COMMAND wNotifyCode:CBN_KILLFOCUS wID:3 hwndCtl:000E0812
000E08E6 R .WM_COMMAND

Handle 000E08E6 is the child dialog
Handle 000E0812 is the ComboBox
Handle 000E0800 is the ComboBox drop-down list window
Handle 0008972A is the grandchild dialog
Handle 00090838 is a ListView window (not show) that is reloaded as a
of the selection in the ComboBox
Handle 000E0818 is the ListView header

Note that there are no paint messages of any kind to the child dialog,
the grandchild dialog, or the check boxes. This is to be expected
because the drop-down list window has the CS_SAVEBITS class style.
What is not expected is that the CS_SAVEBITS don't properly restore
the screen.

Any ideas,
Norm
 
Norman said:
Does anyone understand how a window with class style CS_SAVEBITS gets
the bitmap which it uses to restore the screen image when the window
is closed?

I'm trying to trouble-shoot a problem with a complicated dialog. The
area covered by a ComboBox drop-down is not restored correctly when
the drop-down is closed.
....

Put the minimal project that demonstrates the problem somewhere on the net.

From MSDN about CS_SAVEBITS style:
"Saves, as a bitmap, the portion of the screen image obscured by a window. The system uses the saved bitmap to re-create the screen image when the window is removed. The system displays the bitmap at its original location and does not send WM_PAINT messages to windows obscured by the window if other screen actions have not invalidated the stored image. Use this style for small windows that are displayed briefly and then removed before other screen activity takes place (for example, menus or dialog boxes). This style increases the time required to display the window, because the system must first allocate memory to store the bitmap."
 
Norman Bullen said:
Does anyone understand how a window with class style CS_SAVEBITS gets
the bitmap which it uses to restore the screen image when the window
is closed?

I'm trying to trouble-shoot a problem with a complicated dialog. The
area covered by a ComboBox drop-down is not restored correctly when
the drop-down is closed.

The main dialog is almost entirely covered by a large Tab control.
Depending on which tab the user clicks, either of two child dialogs is
shown completely within the area of the Tab control. Then, depending
on the selection in a ComboBox a "grandchild" dialog is displayed.

From this description I'd guess that the area under the COMBOBOX dropdown
*is* restored, while you are wanting it to change to match your new
selection. Is this correct?
 
Ben said:
From this description I'd guess that the area under the COMBOBOX dropdown
*is* restored, while you are wanting it to change to match your new
selection. Is this correct?
No. The selection that I made in the ComboBox should have the grandchild
dialog and all of its controls in the same state as they were before the
ComboBox list was dropped.

Norm
 
Grzegorz said:
...

Put the minimal project that demonstrates the problem somewhere on the net.

From MSDN about CS_SAVEBITS style:
"Saves, as a bitmap, the portion of the screen image obscured by a
window. The system uses the saved bitmap to re-create the screen image
when the window is removed. The system displays the bitmap at its
original location and does not send WM_PAINT messages to windows
obscured by the window if other screen actions have not invalidated the
stored image. Use this style for small windows that are displayed
briefly and then removed before other screen activity takes place (for
example, menus or dialog boxes). This style increases the time required
to display the window, because the system must first allocate memory to
store the bitmap."

Believe me, I've read that passage in MSDN.

As I stated in my posted message, I believe the ComboLBox window put the
wrong bitmap onto the screen when it was closed. I want to know how it
might have gotten the wrong bitmap in the first place.

Norm
 
Norman said:
Believe me, I've read that passage in MSDN.

As I stated in my posted message, I believe the ComboLBox window put the
wrong bitmap onto the screen when it was closed. I want to know how it
might have gotten the wrong bitmap in the first place.

Norm

Assuming you're right this is CS_SAVEBITS related, ComboLBox window, when closed displays the bits it stored just before it was drawn. You probably won't find more detailed description about CS_SAVEBITS details, since these are irrelevant for windows programmers (at least as long as it works). Give us this dialog with it's window procedure and some of us will take a look.
 
FWIW, I have sometimes encountered weird painting problems similar to what
you have seen (incorrect re-paintings and odd sequences of messages), in
circumstances where a dialog is largely covered by a tab control.

The problems were caused by WS_CLIPCHILDREN style, which was set for the
dialog, but which was not also set for the tab control. Setting
WS_CLIPCHILDREN in both the dialog and in the tab control resolved them.

Mike
 
Michael said:
FWIW, I have sometimes encountered weird painting problems similar to what
you have seen (incorrect re-paintings and odd sequences of messages), in
circumstances where a dialog is largely covered by a tab control.

The problems were caused by WS_CLIPCHILDREN style, which was set for the
dialog, but which was not also set for the tab control. Setting
WS_CLIPCHILDREN in both the dialog and in the tab control resolved them.

Mike
Thanks for the reply.

I started creating a simplified version of my program and encountered
nearly the same thing. This (nearly) minimal program paints correctly
when first opened but the Static control is not repainted if it is
obscured by another window and then exposed.

============= rctester.c =================
#include <windows.h>
#include <commctrl.h>

#define _T(str) str

#include "resource.h"

HINSTANCE hinstApplication;

HWND hdlgMain;

BOOL CALLBACK ChildDialog(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM
lParam)
{ return FALSE;
}

HWND hdlgCurrent;

int iCurrent;

BOOL CALLBACK MainDialog(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{ NMHDR nmHdr;
struct {
TCITEMHEADER hdr;
DLGPROC lpDialogFunc;
int idDialogRes;
} tcItem;

switch (wMsg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDCANCEL:
DestroyWindow(hDlg);
PostQuitMessage(0);
break;
default:
return FALSE; }
break;

case WM_NOTIFY:
switch (((LPNMHDR)lParam)->idFrom) {
case ID_TAB:
if (((LPNMHDR)lParam)->code==TCN_SELCHANGE) {
memset(&tcItem, 0, sizeof tcItem), tcItem.hdr.mask = TCIF_PARAM;
SendMessage(((LPNMHDR)lParam)->hwndFrom, TCM_GETITEM,
SendMessage(((LPNMHDR)lParam)->hwndFrom, TCM_GETCURSEL,
0, 0
),
(LPARAM)&tcItem
);
if (tcItem.idDialogRes!=iCurrent) {
DestroyWindow(hdlgCurrent);
// if (tcItem.bLoadRichEdit)
// hinstRichEdit = LoadLibrary(_T("RICHED20.DLL"));
hdlgCurrent = CreateDialog(hinstApplication,
MAKEINTRESOURCE(tcItem.idDialogRes),
hDlg, tcItem.lpDialogFunc
);
ShowWindow(hdlgCurrent, SW_NORMAL);
/* if (hrgnTab==NULL) {
GetWindowRect(((LPNMHDR)lParam)->hwndFrom, &rect);
hrgnTab = CreateRectRgn(0, 0,
rect.right-rect.left,
rect.bottom-rect.top
);
GetWindowRect(hdlgCurrent, &rectDlg);
rect.left = rectDlg.left-rect.left;
rect.top = rectDlg.top-rect.top;
hrgn = CreateRectRgn(rect.left, rect.top,
rect.left+rectDlg.right-rectDlg.left,
rect.top+rectDlg.bottom-rectDlg.top
);
CombineRgn(hrgnTab, hrgnTab, hrgn, RGN_DIFF);
SetWindowRgn(((LPNMHDR)lParam)->hwndFrom, hrgnTab, FALSE);
DeleteObject(hrgn);
} */
} }
}
break;

case WM_INITDIALOG:
SendMessage(nmHdr.hwndFrom = GetDlgItem(hDlg, nmHdr.idFrom = ID_TAB),
TCM_SETITEMEXTRA, sizeof tcItem-sizeof (TCITEMHEADER), 0
);
tcItem.hdr.mask = TCIF_PARAM|TCIF_TEXT;
tcItem.hdr.pszText = _T("Tab 1");
tcItem.lpDialogFunc = ChildDialog, tcItem.idDialogRes = CHILD_1;
SendMessage(nmHdr.hwndFrom, TCM_INSERTITEM, 0, (LPARAM)&tcItem);
tcItem.hdr.pszText = _T("Tab 2");
tcItem.lpDialogFunc = ChildDialog, tcItem.idDialogRes = CHILD_2;
SendMessage(nmHdr.hwndFrom, TCM_INSERTITEM, 1, (LPARAM)&tcItem);
SendMessage(nmHdr.hwndFrom, TCM_SETCURSEL, 0, 0);
nmHdr.code = TCN_SELCHANGE;
SendMessage(hDlg, WM_NOTIFY, 0, (LPARAM)&nmHdr);
break;

default:
return FALSE;
}
return TRUE;
}

static INITCOMMONCONTROLSEX initCC = {
sizeof initCC, // dwSize
ICC_LISTVIEW_CLASSES|ICC_TAB_CLASSES // dwICC
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nSowCmd
)
{ MSG msg;

InitCommonControlsEx(&initCC);

hdlgMain = CreateDialog(hinstApplication = hInstance,
MAKEINTRESOURCE(MAIN),
NULL, MainDialog
);
while (GetMessage(&msg, NULL, 0, 0)) {
if (!IsDialogMessage(hdlgMain, &msg))
TranslateMessage(&msg), DispatchMessage(&msg);
}
return msg.wParam;
}
=========== rctester.rc ================
#include <windows.h>
#include <commctrl.h>

#include "resource.h"

MAIN DIALOGEX 0, 0, 268, 190
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_CLIPCHILDREN |
WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE
EXSTYLE WS_EX_CONTROLPARENT
CAPTION "rctester"
// CLASS GUID_R_C
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "", ID_TAB, WC_TABCONTROL, TCS_FOCUSNEVER | WS_CLIPSIBLINGS,
4, 3, 260, 170
RTEXT "Black Cat Associates", ID_BLACK_CAT, 164, 177, 100, 10
CTEXT "Preview Version", -1, 7, 177, 145, 10
PUSHBUTTON "button", -1, 14, 175, 131, 14, NOT WS_VISIBLE
END

CHILD_1 DIALOGEX 5, 16, 257, 154
STYLE WS_CHILD
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Sans Serif"
BEGIN
CTEXT "child 1", -1, 6, 75, 240, 11, WS_BORDER
END

CHILD_2 DIALOGEX 5, 16, 257, 154
STYLE WS_CHILD
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Sans Serif"
BEGIN
CTEXT "child 2", -1, 6, 75, 240, 11, WS_BORDER
END
=============== resource.h =================
#define MAIN 1
#define CHILD_1 2
#define CHILD_2 3

#define ID_TAB (IDCANCEL+1)
#define ID_BLACK_CAT (IDCANCEL+2)

Note that my child dialogs are children of the main dialog, not the Tab
control. I set style WS_CLIPSIBBLINGS on the Tab control and it did not
solve the problem. However, removing WS_CLIPCHILDREN from the main
dialog seems to cause it to paint correctly.

Norm
 
Sorry to be dense, but I don't see CS_SAVEBITS in your (nearly) minimal
sample. Are you saying that CS_SAVEBITS was unrelated to your original
problem? Instead, the problem turned out to be related to the
WS_CLIPCHILDREN style, and that removing that style from your actual code
made it work correctly?

Incidentally, why do you create and destroy your child dialogs when tabbing,
instead of simply SW_SHOW/SW_HIDE'ing them? Also, why is it that the child
dialogs are children of the main dialog, and not children of the tab
control?

Mike
 
Michael said:
Sorry to be dense, but I don't see CS_SAVEBITS in your (nearly) minimal
sample. Are you saying that CS_SAVEBITS was unrelated to your original
problem? Instead, the problem turned out to be related to the
WS_CLIPCHILDREN style, and that removing that style from your actual code
made it work correctly?

Incidentally, why do you create and destroy your child dialogs when tabbing,
instead of simply SW_SHOW/SW_HIDE'ing them? Also, why is it that the child
dialogs are children of the main dialog, and not children of the tab
control?

Mike
The CS_SAVEBITS was on the window that the ComboBox uses to display its
drop-down list. When I started reconstructing my project to get a
minimal set of code, I found that it exhibited a problem similar to that
which you described before I got to the point of adding any ComboBoxes.
So, yes, it is a different problem but probably related; I'm fairly sure
the Tab control has something to do with both.

Removing WS_CLIPCHILDREN from this small program made it function
correctly but does not solve the painting problems in my complete
program. I'm still trying to get a minimal program to show the problem.

I create and destroy the child dialogs because the program is meant to
be ported to Windows CE as well as "big" Windows and I want to minimize
the system resources that it uses.

Norm
 
Back
Top