How to avoid flicker while handling WM_ERASEBKGND in Windows dialog - windows

I have a dialog that resizes. It also has a custom background which I paint in response to a WM_ERASEBKGND call (currently a simple call to FillSolidRect).
When the dialog is resized, there is tremendous flickering going on. To try and reduce the flickering I enumerate all child windows and add them to the clipping region. That seems to help a little -- now the flickering is mostly evident in all of the child controls as they repaint.
How can I make the dialog flicker-free while resizing? I suspect double-buffering must play a part, but I'm not sure how to do that with a dialog with child controls (without making all child controls owner-draw or something like that).
I should note that I'm using C++ (not .NET), and MFC, although pure Win32-based solutions are welcomed :)
NOTE: One thing I tried but which didn't work (not sure why) was:
CDC memDC;
memDC.CreateCompatibleDC(pDC);
memDC.FillSolidRect(rect, backgroundColor);
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);

Assuming that "FillSolidRect" is the erase of your background then return TRUE from the WM_ERASEBKGND.
To do the double buffering that you are almost doing in your code fragment, you will need to use CreateCompatibleBitmap and select that into your memDC.

Try adding the following line to your OnInitDialog function:
ModifyStyle(0, WS_CLIPCHILDREN, 0);

Do nothing in the WM_ERASEBKGND handling and do the erase as part of your main WM_PAINT. You can either paint smarter so that you only redraw the invalid areas, or more easily, double-buffer the drawing.
By not doing anything in the erase background, you have all your drawing code in one location which should make it easier for others to follow and maintain.

If you are targeting WinXP or higher, you can also use the WS_EX_COMPOSITED style to enable double-buffering by default for top-level windows with this style. Bear in mind this has its own set of limitations -- specifically, no more drawing out of OnPaint cycles using GetDC, etc.

you can set parameter of your call to InvalidateRect method as false. This will prevent you to send WM_ERASEBKGND when the window will redraw.

Double buffering is indeed the only way to make this work.
Child controls will take care of themselves so long as you make sure CLIPCHILDREN.

Related

what is the best approach to scheduling repaint for winapi window?

I already tried multiple ways of rendering simple animations on winapi windows using wingdi, and realized that its fairly slow even for primitive animations, and inconsistent if i do it by spamming SendMessageW when there are no events for this window. So I thought that instead of rendering when I can i should schedule it with a fixed interval hence improving framerate and quality of the animation. I now wondering if this can be done with registering some callback with windows or there is no such functionality and i do have to spin another thread to function as a timer? Is it generally a better idea? What is the commonly accepted way to do things like this, when event triggered redraws just wont cut it?
Concerning the scheduled animations, there is SetTimer() and KillTimer(), if I remember their names correctly. In any case, "timer" is what you're looking for.
Concerning the painting in the timer callbacks, don't. Instead, adjust animation parameters (positions, colours etc) and trigger a redraw using InvalidateRect(). This will in turn invoke the regular drawing event handler. The difference is that this will not waste any CPU if your window is hidden or minimized. Also, in any case, when the window is un-hidden, the drawing event handler has to be able to draw the right window content anyway.
There is a derived version of this where you update an image in a memory DC and only blit to the window in the drawing event handler. It's unclear whether that is necessary in your case though. It's only when drawing takes an extended amount of time and you want to keep your UI responsive.
Well as my luck usually goes i found this article on MSDN, and it has a very nice example. Putting it here if someone will have same question

SwitchToThisWindow sends current window to the back

So yes, I find myself in the dubious position of implementing a SwitchToThisWindow call to force my window to the front. I agree, its not ideal, but its not always possible to argue against product "features" that others deem necessary.
Now, I consider SwitchToThisWindow to be a win over the AttachThreadInput hack to do a forced window switch as its less likely to deadlock, and should SwitchToThisWindow be removed, or cease to function I won't complain.
However, SwitchToThisWindow has the unfortunate side effect of pushing the current foreground window to the bottom of the z-order in addition to bringing the target window to the top when FALSE is passed for the fAltTab parameter, and not doing anything if TRUE is passed.
How can I avoid this 'push current active to z-bottom' behavior without resorting to AttachThreadInput?
Alternatively, MS can just remove AttachThreadInput as a viable workaround and I can just tell my manager that the impossible, is in fact, actually, impossible.
I don't know if this helps, but the only way i found out to bring my window to top reliably is to make the following 2 calls:
ShowWindow(myhwnd, SW_MINIMIZE);
ShowWindow(myhwnd, SW_RESTORE);
Obviously these calls only should be made, when your window currently is not the topmost one in order to avoid flickering. But this also should not have the side effect of bringing the current front window to the bottom of the z order.
When passing fAltTab=FALSE you are actually emulating Alt+Esc. So you could reverse this z-order change with SetWindowPos and its hWndInsertAfter after the SwitchToThisWindow call, but then you are back in ugly hacky-land IMHO.
The question is, do you really need keyboard focus?
Let me suggest another alternative:
If your window is minimized, restore it
Set your window to be topmost, when your window is activated remove the style again.
Call SetForegroundWindow to flash the taskbar button (Or FlashWindowEx)
This should avoid the scenario where a user is typing and ends up performing some action in your UI without even looking at the screen.
Edit:
HWND hwndFgnd=GetForegroundWindow();
SetWindowPos(hwnd,hwndFgnd,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE);
SetWindowPos(hwndFgnd,hwnd,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
..will probably work if you don't want to set the topmost bit at any point (Even if your window is at the top of the z-order, you still can't legally get the focus with SetForegroundWindow)
This is a bad problem I faced too. See my solution here. It works both for Show() and ShowDialog().

Win32: How to custom draw an Edit control?

i need to implement the functionality of EM_SETCUEBANNER, where a text hint appears inside an Edit control:
The catch is that i cannot use version 6 of the Common Controls, which is what is required to get the Microsoft supplied implementation of a cue banner.
i've looked into simply changing the text of the edit control, and the font format to
Dark Gray Italic Text
but it will throw Change events (component wrapper provided by higher component library) that i can't find a way to avoid.
So i was instead going to custom draw the text, drawing the Cue Banner text when the control is unfocused and empty, and rely on default painting otherwise.
The Edit control doesn't nicely expose a custom drawing mechanism, like ListView, TreeView and others provide.
Other people have looked into it, but it seems to be an nearly impossible task:
From the way things are looking, I'll
have to handle the following
messages:
WM_ERASEBKGND, WM_PAINT (for obvious reasons)
WM_SETFOCUS, WM_KILLFOCUS (to prevent
the white bar from displaying --
described above)
WM_CHAR (to process and update the
text in the control)
And I also need to find a way to
display the caret in the control,
since I haven't found a way to allow
Windows to do that for me without also
painting the white bar I mentioned.
This is going to be fun. :rolleyes:
Given that the Windows Edit control was never meant to be custom drawn: does anyone know how to custom draw a Windows Edit control?
Note: i will also accept answers that solve my problem, rather than answering my question. But anyone else wanting to custom draw an Edit control, coming across this question, would probably like an answer.
Custom drawing an Edit control is essentially impossible. There are a few specialized cases were you are doing so little that can get away with it, but you risk breaking badly in the next revision of windows (or when someone runs your app on an older version, or via terminal services, etc).
Just taking over WM_PAINT and WM_ERASEBKGROUND aren't good enough, because the control will sometimes paint on other messages as well.
You are better off just writing your own edit control. I know that's a huge amount of work, but in the long run it will be less work than trying to hack your way into taking over all of the Edit controls' drawing code.
I remember back in the good old days when everyone use to subclass the button control to add color and graphics, etc. The thing is, one day I sat down and just wrote my own button window class. and it was LESS CODE than what we had in our source tree to subclass and custom draw the Windows button.
Create a window class of your own that looks like and empty edit control, that draws the cue text and shows a caret and has focus. Create the edit control also, but position it behind your window. (or leave it hidden)
Then when you get the first WM_CHAR message (or WM_KEYDOWN?). You put your window behind the edit conrol, give focus to the edit, and pass the WM_CHAR message on. From then on the edit control will take over.
You can listen to EN_CHANGE notifications from the edit control if you need to go back to showing your cue text when the edit gets empty. But I'd think that it would be fine to go back to the cue text only when the edit looses focus AND is empty.
Subclassing the EDIT control worked well for me - needed to display some formatting information to the user when editing object attributes (and some attributes could be multiple lines). The important thing, like Adrian said in his answer too, is to call the EDIT control's procedure before your own drawing. Calling it afterward or issuing your own BeginPaint/EndPaint (with return 0 or DefWindowProc) caused issues for me from the text not displaying at all, to it displaying only when resizing but not after editing, to leaving screen litter of the leftover caret. With that, I haven't had any issues regardless of the EDIT control's other repaint times.
Some setup:
SetWindowSubclass(attributeValuesEdit, &AttributeValueEditProcedure, 0, reinterpret_cast<DWORD_PTR>(this));
// Not only do multiline edit controls fail to display the cue banner text,
// but they also ignore the Edit_SetCueBannerText call, meaning we can't
// just call GetCueBannerText in the subclassed function. So store it as
// a window property instead.
SetProp(attributeValuesEdit, L"CueBannerText", L"<attribute value>");
The callback:
LRESULT CALLBACK AttributeValueEditProcedure(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
UINT_PTR subclassId,
DWORD_PTR data
)
{
...
case WM_PRINTCLIENT:
case WM_PAINT:
{
auto textLength = GetWindowTextLength(hwnd);
if (textLength == 0 && GetFocus() != hwnd)
{
// Get the needed DC with DCX_INTERSECTUPDATE before the EDIT
// control's WM_PAINT handler calls BeginPaint/EndPaint, which
// validates the update rect and would otherwise lead to drawing
// nothing later because the region is empty. Also, grab it from
// the cache so we don't mess with the EDIT's DC.
HDC hdc = (message == WM_PRINTCLIENT)
? reinterpret_cast<HDC>(wParam)
: GetDCEx(hwnd, nullptr, DCX_INTERSECTUPDATE|DCX_CACHE|DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS);
// Call the EDIT control so that the caret is properly handled,
// no caret litter left on the screen after tabbing away.
auto result = DefSubclassProc(hwnd, message, wParam, lParam);
// Get the font and margin so the cue banner text has a
// consistent appearance and placement with existing text.
HFONT font = GetWindowFont(hwnd);
RECT editRect;
Edit_GetRect(hwnd, OUT &editRect);
// Ideally we would call Edit_GetCueBannerText, but since that message
// returns nothing when ES_MULTILINE, use a window property instead.
auto* cueBannerText = reinterpret_cast<wchar_t*>(GetProp(hwnd, L"CueBannerText"));
HFONT previousFont = SelectFont(hdc, font);
SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
SetBkMode(hdc, TRANSPARENT);
DrawText(hdc, cueBannerText, int(wcslen(cueBannerText)), &editRect, DT_TOP|DT_LEFT|DT_NOPREFIX|DT_NOCLIP);
SelectFont(hdc, previousFont);
ReleaseDC(hwnd, hdc);
// Return the EDIT's result (could probably safely just return zero here,
// but seems safer to relay whatever value came from the edit).
return result;
}
}
break;
Writing your own EDIT control (which I've actually done more than once, to partial degrees of completeness compared to the built-in one) is not much work if you do the bare minimum (maybe English only with basic caret support), but it's a LOT of work to get correct if you want caret navigation over complex scripts with variable sized clusters, selection over ranges, IME support, context menus with copy and paste, high contrast modes, and accessibility features such as text to speech. So unlike so many other answers, I recommend not implementing your own EDIT control merely for cue banner text.
Subclass the edit control. Handle WM_PAINT by first calling the original window procedure and then, if it's empty and not in focus, draw the cue text. Pass every other message to the original window procedure.
I've done this--it works. The problem the CodeGuru person had doesn't seem to apply to your situation. I believe he's trying to do more to the appearance. For performance, it looks like the edit control is doing some updates outside of WM_PAINT processing (probably for performance). That's going to make it nearly impossible to take complete control of the appearance. But you CAN draw the cue prompt.
And I also need to find a way to display the caret in the control,
since I haven't found a way to allow Windows to do that for me without
also painting the white bar I mentioned.
If you want to handle WM_PAINT by yourself without forwarding the message to the original windowproc of your superclass, you should not forget to call DefWindowProc. So that the caret will be drawn.
To avoid the white bar you should remove class brush with SetClassLongPtr.
And somehow keep your DC's clipping region to clip Edit controt's ExtTextOut outputs.
The white bar may be the result of OPAQUE option passed to ExtTextOut by Edit control.
Conclusion: Write your own control. No pain, no gain.

Pure win32 cross-process child windows

I need to create a transparent overlay window, that goes above another window. The other window is from another vendor. And when the user drags that window mine needs to follow.
WS-CHILD seems like a nice idea but it cannot be combined with WS-EX-LAYERED, which I really need (for transparency). But I still can set a parent without using WS-CHILD.
Parenting does give my winproc notifications (WM-WINDOWPOSCHANGING), but only after dragging is complete, on mouse-up. To give a nice feeling i need to get those notifications (or for example WM-MOVE) continuosly while dragging.
I guess my problem is similar to docking, but the fine docking solution seen fx at CodeProjet uses WS-CHILD. ( http://www.codeproject.com/KB/toolbars/dockwnd.aspx )
I guess I could use polling but that is not what I am looking for. Also I could use ::SetWindowsHook(). But that is my final resort. I am hoping I have missed something trivial and that somebody can point me in a good direction.
Thanx
I know it is not your preferred solution, but I think you need to use a global mouse hook. Pass WH_MOUSE_LL to SetWindowsHookEx() and do nothing in the default case of your low-level mouse proc. But when you get the WM_WINDOWPOSCHANGING notification, start tracking the mouse movements and making appropriate calls to MoveWindow() or whatever.
I use a LayeredWindow for that and set the other window as parent.
This is the code I used for that:
::SetWindowLong(GetHwnd(), GWL_EXSTYLE, GetWindowLong(GetHwnd(), GWL_EXSTYLE) |WS_EX_LAYERED);
::SetLayeredWindowAttributes(GetHwnd(), RGB(255,0,255), 255, LWA_COLORKEY | LWA_ALPHA);
::SetWindowLongPtr(GetHwnd(),GWLP_HWNDPARENT,(long)GetParentHWND());
::SetWindowPos(hndOtherWindow, hndOverlayWinow, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |SWP_NOACTIVATE);
It works for my purposes. There's only one problem left: If my overlaying window loses the focus I want to set the focus, or activate the other window. Do you have an idea?
How about WM_MOVING message? You may try intercepting this message and move your window accordingly.
If you want to know when a window in another process is being moved or sized, you need to install a hook,catch the WM_MOVING and WM_SIZING messages and reflect those messages back to your controller process. Sorry it's not the answer you want! I don't blame you for wanting to avoid cross process hooks, its a bit of a pain...

Can a window be always on top of just one other window?

In Windows, is it possible to set window A such that it is always on top of window B, yet allow other windows to work as normal and appear over the top of both, when active.
In other words, I want a parent-child relationship between two windows. Can this be done without making window A a child of window B, MDI-style? Window B isn't mine (Internet Explorer), and screws my dialog A's graphics up when I try to achieve this with SetParent.
I thought I'd cracked it with this idea from an MSDN forum post, but alas windows A is still always on top of everything, not just window B.
// Place window A on top
SetWindowPos(hWndWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
// Place window B underneath it
SetWindowPos(hWndParent, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
Is it possible?
Wouldn't creating an ownership relationship do the trick?
SetWindowLong(hwndChild, GWL_HWNDPARENT, hwndOwner)
The windows can be in different processes and you can call this from any process. This will ensure that the child window is always above the owner window. This is different than SetParent which actually creates a Parent / Child relationship. Read through this article (its from 1993 but still mostly correct) to see the distinction between ownership and parenting.
When your window's Z-order (or size or position) is changing, it should receive a WM_WINDOWPOSCHANGING message. If you process that message, you have an opportunity to modify the final Z-order (or size or position) to which the window is moved.
To illustrate, in hWndA's window procedure:
case WM_WINDOWPOSCHANGING:
DefWindowProc(hWnd, msg, wParam, lParam);
WINDOWPOS *p = (WINDOWPOS*)lParam;
p->hwndInsertAfter = hWndB;
p->flags &= ~SWP_NOZORDER;
return 0;
should insert hWndA after hWndB in the Z-order any time hWndA's position changes.
Until Vista, one way to do it would have been to use SetWindowsHookEx, and hook the WH_CBT or WH_CALLWNDPROC hook, and then take appropriate action when you detect the Z order changing. However this doesn't work with Vista (as far as I can tell from googling).
The only other solution I can think of is to set up a timer to fire every few seconds, and then when you receive a WM_TIMER, you interrogate the system using GetNextWindow to find out which window is behind yours. If it's not IE, then call SetWindowPos to position your window above IE (I assume you have a HWND for the IE window you care about - remember there can be multiple IE windows).
This will cause problems if people try to bring your window to the front - it will flip back to being just above IE. In this case, in your code you could handle WM_ACTIVATE and try to change the Z-order of IE's window so it's below your window (call SetWindowPos to move IE's window so it's above the window that is currently below your window). This solution may be fraught with problems as Windows may try to prevent you messing with the windows of another process, for security reasons. On the other hand, the MSDN docs for SetWindowPos don't explicitly mention that you can't manipulate the windows of another process. There may be obscure limitations though.
Even with this timer hack, you're going to effectively have a busy-waiting loop in your app (with the frequent WM_TIMER messages) and this is generally a bad thing to do, especially for battery life of laptops etc. (because you prevent the CPU from entering a sleep state, and so on).
I'd say there's no good way of doing this, and anything you're likely to get working will be brittle and cause problems. I strongly recommend not trying to do it. Is it possible to make your program into some kind of plug-in or toolbar for IE instead?
NB Be particularly aware that SetWindowsHookEx imposes a performance penalty at a system-wide level if you go down this route.
Maurice's answer is the best out of what's here but is missing an important step. When you call show on your window that you want as the overlay, you need to call the show method that has the parameter. You'll need to define a class that implements the IWin32Window interface and just make a new instance of that. The only thing that interface cares about is the Handle so just set that to the handle of the IE window and it should work pretty well
If the parent-child relationship is made by yourself with the SetWindowPos() function, your desire can be implemented.
Can you access the Z-order of the windows?
I cannot recall the default z-order of windows, but I think it is 1. You might be able to set IE to a -1 and your app to 0.
Try this:
// Place window A on top of window B
SetWindowPos(hWndA, hWndB, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
The second window handle parameter specifies the next window down in the Z order.
Note this doesn't actually change the window parent-child relationships - but you can simulate it.

Resources