flicker on tab control - WIN32 - c

I have a WIN32 application. Its main window is hwndMain, one of its child is hwndView. There is one tab control hwndTab on hwndView.
When I resize hwndMain, hwndView is resized and so is hwndTab. It flicker a little, but not much.
I have tried to use WS_EX_COMPOSITED style ( for hwndView or hwndTab), but it just gave me blank window. I tried to use WS_EX_TRANSPARENT and it solves flicker, but when the windows is resized to be larger, the childs are updated very slow, e.g I see black region for one second, then the region is updated.
I have successfully sloved the flicker issue for TreeView by using WS_CHIPCHILDREN style. (See remark below). But using WS_CHIPCHILDREN stlye for hwndView doesn't fix the flicker issue for tab control.
I have paid attention to WM_ERASEBKGND and Not set hbrBackground also.
I want to use double buffer for tab control, but I can't find a tutorial for this purpose. All the tutorial I found is: In WM_PAINT, after creating CompatibleDC and CompatibleBitmap, draw what you want in memdc and.....; But I don't want to do any custom drawing in WM_PAINT for hwndTab. I just want to leave the tab control do this job, but shows the final result only.
Could someone show me a small example how to double buffer a tab control (if you think this will fix the flicker issue of tab control), in the language c + winapi, since I don't have the knowledge of C#, Net,..etc.
Remark: For my TreeView, it is a child of a window hwndContainer. It is created as:
win->hwndContainer = CreateWindowEx(
WS_CLIPCHILDREN,
_T("SUMATRA_PDF_TOCBOX"), NULL,
WS_CHILD,
0, 0, gGlobalPrefs.sidebarDx, 0,
win->hwndPanel, NULL,
ghinst, NULL);
Using WS_CLIPCHILDREN fix the flicker, even if I don't use double buffer. But it is strange to put
WS_CLIPCHILDREN in the first parameter position. If I put it after WS_CHILD, i.e
win->hwndContainer = CreateWindowEx(
NULL,
_T("SUMATRA_PDF_TOCBOX"), NULL,
WS_CHILD | WS_CLIPCHILDREN,
0, 0, gGlobalPrefs.sidebarDx, 0,
win->hwndPanel, NULL,
ghinst, NULL);
,then the flicker still occurs.
So I also tried to use the first way when I created hwndView, but it just gave blank white window.
I am really confused with these stuff.
Here is the blank window picture when I used WS_EX_COMPOSITED for hwndView.
There is no such problem when I used it for hwndContainer.
hwndView in fact has two child: a Tab Control hwndTab and a child which has its own double buffer and drawing. I am not sure if this cause the problem for using WS_EX_COMPOSITED.

You are using the WS_EX_COMPOSITED style. When you pass WS_CLIPCHIDREN as the first argument to the CreateWindowEx, it's interpreting the value of WS_CLIPCHILDREN as an extended window style. Since the value of WS_CLIPCHILDREN is 0x02000000L, the same as WS_EX_COMPOSITED, you've just created a composited window.
And a composited window, according to the documentation, has all of its descendants painted in a bottom-to-top painting order using double-buffering.
I'm not sure what you mean when you say:
I have tried to use WS_EX_COMPOSITED style ( for hwndView or hwndTab), but it just gave me blank window.
You'll have to post code the reproduces this problem. But your second-to-last code snippet is producing a composited window.

Related

Win32, and the window style

So I have a window, and I have coded it so that during run-time it can enter and exit full-screen mode. Entering full-screen works, but exiting places the window tile bar in reverse order.
Exit full screen code:
SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 640, 480, NULL);
InvalidateRect(hWnd, NULL, TRUE);
Picture of the result: https://www.dropbox.com/s/p15eltz7b2hxx4y/window.png?dl=0
I tried using GWL_STYLE instead of GWL_EXSTYLE but that works even worse, with the window being visible but clicking anything on the window will act like the window is not there and the click on whatever is behind it...
Thanks!
Philip
Just a thought, couldn't you get the window style (with GetWindowLongPtr), store it as a member variable in you class and then use this as the style to reset in SetWindowLongPtr?
Following is uncheck code (this is assuming you are using C++),
MainWnd::OnFullScreen(...)
{
m_oldStyle = GetWindowLongPtr(GWL_EXSTYLE, m_hwd);
/*
what ever other code is necessary
*/
}
MainWnd::OnExitFullScreen(...)
{
SetWindowLongPtr(m_hwn, GWL_EXSTYLE, m_oldStyle);
/*
and other code as needed
*/
}
I've made two assumptions here:
(1) that you will have two variables, one to contain the old style (m_oldStyle) and one to hold the handle to the window (m_hwd). Note if you are doing strict SDK style coding then the handle will be passed to you as part of WndProc. If you are using MFC there should be member function in the class you derived you main window from. In other cases you are on your own.
(2) the second assumption is that SetWindowLongPtr is called prior to any change of screen type. I believe that SetWindowLongPtr is called during window construction, but it has been several years since I've done serious windows programming using Microsoft frameworks (now I tend to used QT's framework).

Is this how I make custom controls both transparent and flicker-free in Windows? Or do I have one of these steps wrong?

I could begin by asking the question outright or by citing my sources (this, this, this, and this) descriptively, but I'll walk you ll through what I'm trying to do instead.
Let's start with a main window. It has its own window class whose hbrBackground is set to COLOR_BTNFACE + 1. Now let's do
EnableThemeDialogTexture(hwnd, ETDT_ENABLE | ETDT_USETABTEXTURE)
so the tab control we're about to add will be drawn with visual styles. (Try Windows XP with the standard Luna theme for best results.) Now let's add a tab control and two tabs.
On the first tab, we create an instance (let's call it container) of a new window class. This window class is going to hold various controls. I could set hbrBackground to COLOR_BTNFACE + 1, but then it will draw over the tab background. So I want this new child window to be transparent. So what I do is
set the class hbrBackground to GetStockObject(HOLLOW_BRUSH)
set container's extended style to WS_EX_TRANSPARENT
set the class WM_ERASEBKGND handler to do SetBkMode((HDC) wParam, TRANSPARENT); return 0; to set the device context and have Windows draw the transparent background.
So far so good, right? I'm not sure if I'm really doing all this correctly, and I'd like this to also be flicker-free, which doesn't seem to happen: when I resize the window (at least in wine) I get either flicker or garbage drawn (even in child controls, somehow!). Windows XP in a VM just shows flicker. I tried tweaking some settings but to no avail.
But wait, now I want to have another control, one that just draws some bitmap data. On the next tab, create another container, then have a third window class area as a child of that. area only draws in the upper-left 100x100 area and has scrollbars; the rest of the window area should be transparent.
Right now, what I have for area is:
the window class hbrBackground set to NULL and styles CS_HREDRAW and CS_VREDRAW set
the extended window style being 0
the WM_ERASEBKGND simply doing return 1;
the WM_PAINT filling the entire update rect with COLOR_BTNFACE + 1 before drawing, and rendering all of it
This is flicker-free, but obviously not transparent. NOW I'm really not sure what to do, because I want the area to be transparent in such a way that it shows the tab control background. Again, I tried tweaking settings to bring them closer to what I tried above with container, but I got either flicker or invalidation leftovers when I tried.
So how do I get both of these custom control types (the container and the drawing area) to be both flicker-free and transparent?
I presently must target Windows XP at a minimum, though if the solution would be easier with Vista+ only I'd be happy to keep that solution on the side in case I ever drop XP support (unfortunately Stack Overflow doesn't let me hand out silver medals...).
Thanks!
To paint your window in a manner that is "flicker free", you will need to paint your window to a bitmap, then copy the bitmap to the destination device context. On Windows XP, you will need to create a bitmap, adjust the origin of the drawing DC and then paint your window. On Vista and later you can use BeginBufferedPaint and its associated routines to do the buffering for you.
Once you have buffered painting working, you can then use WM_PRINTCLIENT to paint your window's parent window into the your drawing DC before you do any actual drawing. Unfortunately, not all windows will support WM_PRINTCLIENT.
You could consider using DrawThemeParentBackground, rather than WM_PRINTCLIENT directly.
Combining these two methods together will leave you with transparent flicker-free drawing.

Tracking tooltip causes grey "trail" of excruciatingly slow repaint

Let me first decribe the symptoms of the problem. I'll then give additional facts and explain my question.
Symptoms
I have written a custom Windows control. The control paints itself in response to a WM_PAINT message. It also uses tracking tooltips (ie the tracking feature of the TOOLTIPS_CLASS common control).
When I drag the mouse over the control, the tooltip nicely follows the mouse. The problem is that it leaves a grey streak in its wake. This streak takes an observable amount of time to repaint -- as you can see from the attached image, I was able to hit PRNTSCRN and take a screenshot of it before the control had time to repaint itself.
(What is even more peculiar is that the WM_PAINT handler doesn't appear to have run even once. But note that the code that causes the tooltip to track is in WM_MOUSEMOVE, and that is obviously being perfectly responsive.)
Facts
Please assume vanilla C using Win32 libraries.
The WM_PAINT handler is actually very fast. The control has a number of features that require repainting the entire client area, and this is imperceptible to the user.
Indeed, some features run animations that repaint the whole client area at 15-24fps.
It is also decently efficient and doesn't repaint much more than the update rectangle on any given repaint.
The WM_ERASEBKGND handler does nothing and simply returns 1.
I never erase the background, I simply paint over it.
The window has the following style bits set:
ws: WS_CHILD | WS_VISIBLE
ex: WS_EX_COMPOSITED
cs: CS_DBLCLKS
The parent window is a top-level window with the following style bits set:
ws: WS_TILEDWINDOW | WS_CLIPSIBLINGS | WS_VISIBLE
ex: WS_EX_WINDOWEDGE
cs: CS_REDRAW | CS_DBLCLKS
The control's window class background brush is GetStockObject(NULL_BRUSH).
The only other way I have found to cause the same kind of "trail" is by dragging another top-level window over my control. The area that is temporarily obscured by the dragged top-level window leaves the same trail.
Giving the control's window class the CS_SAVEBITS style doesn't seem to make any difference. I still get the same perceptible trail of slow repaints.
Questions
Why am I getting the grey at all, especially if I set CS_SAVEBITS?
What can I do to make the grey go away?
Should I call UpdateWindow() each time I move the tooltip?
But this doesn't solve the issue of other top-level windows being dragged over top of my control.
Help!
Adding the WS_CLIPCHILDREN style bit to the parent window made this issue go away.
For whatever reason, when a window is partly obscured and then revealed, the OS is very generous with the WM_ERASEBKGND messages and very stingy with the WM_PAINT messages. What was happening is that the parent's WM_ERASEBKGND handler was erasing over top of my control. Adding WS_CLIPCHILDREN causes the parent window to clip its erasing.
Funnily enough, this solution worked for my control, which simply ignores the WM_ERASEBKGND message, but didn't work for standard BUTTON controls with style BS_GROUPBOX style. I expect this is because of the same generous WM_ERASEBKGND policy. The standard button control probably dutifully erases its background in handling that message and then vainly waits around for a WM_PAINT message.

How to hide window from "Applications" tab in task manager?

I have question regarding the CreateWindowEx function. I have 2 windows, a main one and a popup one. I want a popup window to hide everywhere. It is currently not displayed in taskbar and it is not even visible in alt+tab menu. However it is visible on "Applications" tab in task manager. What flags do I need to use in CreateWindowEx to hide my popup window from there?
Current code:
hHistoryWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED, szAppName, L"HistoryWindow", WS_EX_TOPMOST | WS_POPUP, WIDTH, TOP, width, height, NULL, NULL, hInstance, NULL);
I also wanted to ask, whether I need to release a bitmap resource from "static" window before using DestroyWindow() function? I set image to a "static" window this way:
SendMessage (hStatic, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hWhiteStone);
Is it enough to release hWhiteStone or do I need to release also handle returned by SendMessage() (- or is it done automatically by DestroyWindow)?
Thank you for any info.
Kra
Make it a child of your main window. Do this by changing the fourth last parameter to the HWND of your main window. This SHOULD make windows treat your popup window as part of the same application as your main window.

How do you place sub controls inside a group box?

When I enable common control visual style support (InitCommonControls()) and I am using any theme other then Windows Classic Theme, buttons inside a group box appear with a black border with square corners.
Windows Classic Theme appears normal, as well as when I turn off visual styling.
I am using the following code:
group_box = CreateWindow(TEXT("BUTTON"), TEXT("BS_GROUPBOX"),
WS_CHILD | WS_VISIBLE | BS_GROUPBOX | WS_GROUP,
10, 10, 200, 300,
hwnd, NULL, hInstance, 0);
push_button = CreateWindow(TEXT("BUTTON"), TEXT("BS_PUSHBUTTON"),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
40, 40, 100, 22,
group_box, NULL, hInstance, 0);
EDIT: The issue occurs with radio buttons as well
EDIT: I am not using any dialogs/resources, only CreateWindow/Ex.
I am compiling under Visual C++ 2008 Express SP1, with a generic manifest file
Screenshot http://img.ispankcode.com/black_border_issue.png
The problem is having the groupbox as the controls' parent. Groupboxes are not supposed to have any children and using them as parents will cause all kinds of errors (including painting, keyboard navigation and message propagation). Just change the parent in the buttons' CreateWindow call from group_box to hwnd (i.e. the dialog).
I'm guessing you used the groupbox as the parent in order to position the other controls easily inside it. The proper way to do this is to get the position of the groupbox client area and map it to the client area of the dialog. Everything placed in the resulting RECT will then appear inside the groupbox. Since groupboxes don't actually have a client area, it can be calculated with something like this:
// Calculate the client area of a dialog that corresponds to the perceived
// client area of a groupbox control. An extra padding in dialog units can
// be specified (preferably in multiples of 4).
//
RECT getClientAreaInGroupBox(HWND dlg, int id, int padding = 0) {
HWND group = GetDlgItem(dlg, id);
RECT rc;
GetWindowRect(group, &rc);
MapWindowPoints(0, dlg, (POINT*)&rc, 2);
// Note that the top DUs should be 9 to completely avoid overlapping the
// groupbox label, but 8 is used instead for better alignment on a 4x4
// design grid.
RECT border = { 4, 8, 4, 4 };
OffsetRect(&border, padding, padding);
MapDialogRect(dlg, &border);
rc.left += border.left;
rc.right -= border.right;
rc.top += border.top;
rc.bottom -= border.bottom;
return rc;
}
Note that the same applies to Tab controls. They too are not designed to be parents and will exhibit similar behavior.
Just a guess here, but it looks like you are inheriting either the Static Edge or Client Edge style from you theme. I create most of my dialogs from the resource editor and set these properties there.
In your case, you can replace your CreateWindow with a CreateWindowEx to set these extended styles, which are probably being defaulted in CreateWindow. Specifically check out WS_EX_STATICEDGE, WS_EX_WINDOWEDGE and WS_EX_CLIENTEDGE
Edit: I'm assuming that this is not happening because you button is the default control in the dialog, which would also give a black edge.
Apparently group boxes are not meant to group controls (be a parent hwnd)
So in order to get rid of the black borders/painting issues I would have to subclass group box and implement WM_PAINT and WM_PRINTCLIENT
Ahh yes the black background with radio buttons and group boxes. Although I'm not sure if this will work for VC++ 2008, but back-in-the-day the solution for VB6 themed apps was to put the radio controls on a PictureBox (a generic container really) first and then add that to the group box.
Its worth a shot!

Resources