Paint problem when handling WM_CTLCOLOREDIT - winapi

I have an non read only edit control for which I need to change colors so I handle WM_CTLCOLOREDIT. It works well when I am typing continuously but the problem is when I hit backspace or delete or even start typing from the middle of an existing text, the display is all junked up. That remains untill I cause a repaint by resizing the parent etc. How to fix this?
Edit: Some detail. The problem seems only when the background color is set and not when just the text color is set. The code looks like this
ON_MESSAGE(WM_CTLCOLOREDIT, OnEditColor)
LRESULT CMyWindow::OnEditColor(WPARAM wp, LPARAM lp)
{
HDC hdc = (HDC)wp;
HWND hwnd = (HWND)lp;
if(hwnd == m_edit.GetSafeHwnd())
{
// SetBkMode(hdc, TRANSPARENT);
MyControlInfo*pcti;// accessed from somewhere
SetTextColor(hdc, pcti->theme.clrText);
// return (LRESULT)pcti->brush.GetSafeHandle();
}
return 0;
}
Thanks

I rather doubt that this is caused by this code. It is the kind of problem you get when you try to subclass the edit control and override painting. Windows version 2 having to run on a 386SUX and 20 years of appcompat prevented Microsoft from fixing this control so it only draws itself in the WM_PAINT message handler. It indeed draws directly to the screen when you backspace. There's no workaround for it.
Same comment applies as in your previous question, use a real edit control.

You should set a background color with SetBkColor (And don't use SetBkMode) and return a valid brush. (You don't know how the control does its painting, it is free to use ExtTextOut with ETO_OPAQUE etc)

http://msdn.microsoft.com/en-us/library/bb761691(v=vs.85).aspx
Return Value
If an application processes this
message, it must return the handle of
a brush. The system uses the brush to
paint the background of the edit
control.
So try something like:
return static_cast<LRESULT>(::GetSysColorBrush(COLOR_WINDOW));

Related

My code isn't enough to show a triangle

The codes below is in winmain function after registering a class for the parent window:
RECT disrect;
HWND stat = CreateWindow("BUTTON","abcdef",
WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,10,150,500,100,dis,0,0,0);
HDC hdc=GetDC (stat);
FillRect(hdc,&disrect,CreateSolidBrush(RGB(3,5,54)));
SetTextColor(hdc,RGB(25,250,250));
POINT p[3];
p[1].x=280;
p[1].y=280;
p[2].x=280;
p[2].y=290;
p[3].x=285;
p[3].y=285;
Polygon(hdc,p,3);
TextOut(hdc,10,10,"hhhhh",5);
But when I run it, only shows a white rectangle into the parent window, neither the rect is filled with black brush, nor there is any text in it.
Could you guys tell me where I am wrong?
Unless you want to display animations, you should never try to directly write to a window that way, because many events could cause the window to redraw itself erasing what you have just written.
The correct way is to put it in the WM_PAINT handler.
A few issues, in addition to not using WM_PAINT.
First, merely calling CreateSolidBrush() is not enough to mark that brush as the one for your drawing operations to use. You have to select the brush into a DC (device context) before you can use it. This is done with the SelectObject() function. General usage looks like
HBRUSH prevBrush;
prevBrush = SelectObject(hdc, newBrush);
// drawing functions
SelectObject(hdc, prevBrush);
Yes, it is important to restore the previous brush when finished, even on a fresh DC; the initial state must be restored. The initial state uses a brush that draws nothing; this is why your Polygon() doesn't draw anything. SelectObject() is used for all the various things you use to draw with (pens, fonts, etc.), not just brushes.
Second, in C array indices start at 0 and go to size - 1, not start at 1 and go to size. So instead of saying pt[1], pt[2], and pt[3], you say pt[0], pt[1], and pt[2]. Your compiler should have warned you about this.
Third, as the documentation for CreateSolidBrush() will have said, once you are finished with the brush you must destroy it with DeleteObject(). You must do this after selecting the previous brush back in. You must also do this with the brush you used in the FillRect() call.

Flicker when moving/resizing window

I have developed an application to display jpeg images. It can display 4 images, one in each quadrant of the screen. It uses 4 windows for that. The windows have no border (frame) nor titlebar.
When a new image is loaded, the window size is adjusted for the new image and then the image is displayed.
Especially when the window is made larger, there is often a flicker. With my eyes to slits, it seems that the old contents is moved when resizing before the new contents is displayed.
I consulted many resources and used all tricks:
the window has only style CS_DBLCLKS (no CS_HREDRAW or CS_VREDRAW);
the background brush is NULL;
WM_ERASEBKGND returns 1;
WM_NCPAINT returns 0;
WM_NCCALCSIZE tells to align to the side not moved (can you tell it to discard the client area?);
WM_WINDOWPOSCHANGING returns 0;
SetWindowPos has flags SWP_NOCOPYBITS | SWP_DEFERERASE | SWP_NOREDRAW | SWP_NOSENDCHANGING.
Still, the flicker (or the contents move) occurrs when resizing the window. What I want is to:
SetWindowPos to new size and position;
InvalidateRect (hWnd, NULL, FALSE);
UpdateWindow(hWnd);
without any painting, background erasing or content moving until WM_PAINT.
WM_PAINT does:
hDC= BeginPaint (hWnd, &ps);
hMemDC= CreateCompatibleDC (hDC);
hOldBitmap = SelectObject (hMemDC, hNewBitmap);
BitBlt (hDC,...,hMemDC,0,0,SRCCOPY);
SelectObject (hMemDC, hOldBitmap);
DeleteDC (hMemDC);
EndPaint (hWnd, &ps);
Can anyone tell me if/where I make a mistake that causes the old content of the window to be moved?
Hardware etc: Windows 7 on HP Elitebook Core7 64 bits with NVIDIA Quadro K1000m driver 9.18.13.3265 (updated to 341.44).
UPDATE (Jul '17)
I have seen the behavior of the pogram also on another Windows computer (Windows 8/10). It does not seem to be the NVIDIA display driver.
The behavior is the most visible when resizing a window tiled to the centre of the screen (right bottom = w/2, h/2) to the left or left upper corner (0, 0).
I may have problems with the calculations for the WM_NCCALCSIZE message to tell Windows not to do anything. Could anyone give an example calculation for my purpose? See also How do I force windows NOT to redraw anything in my dialog when the user is resizing my dialog?
You have an impressive list of anti-flickering tricks :-)
I don't know if this is of importance (since it depends on how your tool windows are created and espacially if they are child windows to a common parent):
Try setting window style WS_CLIPCHILDREN in the parent window of the tool windows (if there is one).
If not set the parent window will erase it's (entire) background and then forward the paint messages to the child windows which will cause flickering. If WS_CLIPCHILDREN is set the parent window does nothing to the client area occupied by child windows. As a result the area of child windows isn't drawn twice and there is no chance for flickering.
This is a theory more than an answer:
By default, in modern Windows, your window is just a texture on the video card, and the desktop window manager is mapping that to a rectangle on the screen. You've seem to have done everything necessary to make sure that texture gets updated in one fell swoop.
But when you resize the window, perhaps the desktop compositor immediately updates its geometry, causing the (still unchanged) texture to be appear in the new position on the screen. It's only later, when you do the paint, that the texture is updated.
You can test this theory by temporarily turning off desktop compositing. On Windows 7, you navigate to System Properties, choose the Advanced tab, under Performance choose Settings..., on the Visual Effects tab, deselect the "Enable desktop composition" setting. Then try to reproduce the problem. If it goes away, then that supports (but doesn't absolutely prove) my theory.
(Remember to re-enable compositing, since that's how most of your users will be running most of the time.)
If the theory is true, it seems the goal is to get to the paint as soon possible after the window resize. If the window of time is small enough, then both could happen within a monitor refresh cycle and there would be no flicker.
Ironically, your efforts to eliminate flicker may be working against you here, since you've intentionally suppressed the invalidation and redraw that would normally result from SetWindowPos until you do it manually at a later step.
A debugging tip: Try introducing delays (e.g., Sleep(1000);) at key points in the process so you can see whether the resize and redraw are actually rendering on screen as two distinct steps.
You have indeed got a nice set of tricks.
First I can suggest a few variants on the existing tricks that might help especially on XP/Vista/7, and second I want to mention where the persistent flicker you see on Win8/10 is likely coming from and some tricks to reduce that.
First, despite advice to the contrary in other OP posts, you may find that if you set the CS_HREDRAW|CS_VREDRAW window styles that you avoided before, it actually eliminates the BitBlt that is done inside the internal SetWindowPos Windows does during window resizing (but---and this is the confusing part---on Windows 8/10 you will still see flicker from another source...more on that below).
If you don't want to include CS_HREDRAW|CS_VREDRAW, you can also intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing.
Or, you could add to your WM_NCCALCSIZE trick by having it return a set of values that will tell Windows to just BitBlt 1 pixel on top of itself so that even if the BitBlt does happen, it doesn't do anything visibly:
case WM_NCCALCSIZE:
{
RECT ocr; // old client rect
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new window rect
// np->rgrc[1] is old window rect
// np->rgrc[2] is old client rect
ocr = np->rgrc[2];
}
else
{
RECT *r = (RECT *)lParam;
// *r is window rect
}
// first give Windoze a crack at it
lRet = DefWindowProc(hWnd, uMsg, wParam, lParam);
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new client rect computed
// np->rgrc[1] is going to be dst blit rect
// np->rgrc[2] is going to be src blit rect
//
ncr = np->rgrc[0];
RECT &dst = np->rgrc[1];
RECT &src = np->rgrc[2];
// FYI DefWindowProc gives us new client rect that
// - in y
// - shares bottom edge if user dragging top border
// - shares top edge if user dragging bottom border
// - in x
// - shares left edge if user dragging right border
// - shares right edge if user dragging left border
//
src = ocr;
dst = ncr;
// - so src and dst may have different size
// - ms dox are totally unclear about what this means
// https://learn.microsoft.com/en-us/windows/desktop/
// winmsg/wm-nccalcsize
// https://learn.microsoft.com/en-us/windows/desktop/
// api/winuser/ns-winuser-tagnccalcsize_params
// - they just say "src is clipped by dst"
// - they don't say how src and dst align for blt
// - resolve ambiguity
// essentially disable BitBlt by making src/dst same
// make it 1 px to avoid waste in case Windoze still does it
dst.right = dst.left + 1;
dst.bottom = dst.top + 1;
src.right = dst.left + 1;
src.bottom = dst.top + 1;
lRet = WVR_VALIDRECTS;
}
else // wParam == 0: Windoze wants us to map a single rect w->c
{
RECT *r = (RECT *)lParam;
// *r is client rect
}
return lRet;
So that's all very nice, but why does Windows 8/10 look so bad?
Apps under Windows 8/10 Aero don't draw directly to the screen, but rather draw to offscreen buffers that are then composited by the evil DWM.exe window manager. DWM actually adds another layer of BitBlt-type behavior on top of the existing legacy XP/Vista/7 BitBlt behavior.
And the DWM blit behavior is even more crazy because they don't just copy the client area, but they actually replicate pixels at the edges of your old client area to make the new one. Unfortunately, making DWM not do its blit is much harder than just passing some extra flags.
I don't have a 100% solution, but I hope the above info will help, and please see this Q&A for a timing trick you can use to reduce the chances of the DWM-layer blit happening to your app:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Just in addition to your list of tricks. I have the same problem with flicker on my Dell XPS notebook while resizing window using left/top edge. I've tried all the tricks you mentioned. As far as I understand window border is drawn in GPU and the window content is prepared in GDI subsystem and transferred to video memory for window composition (DWM introduced in Windows 8.1). I tried to remove GDI rendering completely (setting WS_EX_NOREDIRECTIONBITMAP style) which makes window without any content, and then create content surface directly using DXGI subsystem (using CreateSwapChainForComposition, there are few examples on how to do this). But the problem remains. There is still a lag between rendering a window frame and resizing/displaying content surface.
However it may solve your problem, as you don't have the border and you will not notice this lag. But you will have full control over window repaint and it will be made on GPU side.

How do I get the paintable area in a CFrameWnd with dialog bars?

I have a CFrameWnd with a dialog bar, and I'd like to determine the paintable area (excluding the space used by the dialog bar) so I can draw in it without being overlapped by the dialog bar. How can I do this? GetClientRect() seems to cover the client rect area as well, and I don't see any other obvious candiates in the doucumentation.
I'm pretty sure the Cwnd::RepositionBars method should provide the information you need. Does the following code work for you?
CRect rcClient;
pFrameWnd->RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, CWnd::reposQuery, &rcClient);
I had a similar problem with CDockablePane which I solved with
CRect rc;
GetDockingManager()->GetClientAreaBounds(rc);
but I also ran into the problem of not being able to find a message or callback to know when to trigger it.

How can I stop window rendering and later resume?

I'd like to prevent my window from being updated until I finish receiving data from the server and render it. Can I hook on the WM_PAINT event, or better still call some Win32API method to prevent the window from being updated and unfreeze it later?
More info:
In the context of an MMC snapin written in C#, our application suffers from annoying flickering and double sorting behaviour:
We use MMC's listViews, but since we subscribe to the sort event.
MMC does it's own magic and sorts the page being displayed (and we can't override that), and when we receive a reply from our server we change the listView again.
each row change is done sequentially, there's no beginUpdate etc. (AFAIK).
Normally hooking into WM_PAINT is the way to go, but make sure you also ignore all WM_ERASEBKGND notifcations, otherwise you'll still get flicker, because Windows erases the Windows area for you. (Return non-zero to prevent Windows from doing that)
One other possibility is to use the LockWindowUpdate function, but it has some drawbacks:
Only one window can be locked
Upon unlock the whole desktop and all sub-windows (i.e. everything) is repainted, resulting in short flash of the whole desktop. (It's worse on XP than on Vista)
Some controls have BeginUpdate and EndUpdate APIs for this purpose.
If you do something (e.g. hook and ignore paint events) do disable painting, then a way to force a repaint later is to call the Invalidate method.
OK, after all searching and checking I've found that LockUpdateWindow is bad idea - see for example articles of Raimond Chen OldNewThing. But even to implement the idea of SetRedrawWindow wasn't so simple - because what I had was only received from IConsole2* pConsole->GetMainWindow() HWND handler of main window. By setting it to SetRedraw = FALSE it was disappeared in very strange manner. Though to make the procedure run only for the TreeView and not for the whole application (ours left panel) I ran
EnumChildWindows(hWnd, SetChildRedraw, FALSE); //stopping redraw
//... here you do your operations
EnumChildWindows(hWnd, SetChildRedraw, TRUE); //restarting redraw
where SetChildRedraw callback was defined in next way:
#define DECLARE_STRING(str) TCHAR str[MAX_PATH]; ZeroMemory(str, sizeof(str));
BOOL CALLBACK SetChildRedraw(HWND hwndChild, LPARAM lParam)
{
RECT rcChildRect; ZeroMemory(&rcChildRect, sizeof(rcChildRect));
DECLARE_STRING(sText)
GetClassName(hwndChild, sText, MAX_PATH);
if (wcsstr(sText, L"SysTreeView32") != NULL)
{
SetWindowRedraw(hwndChild, lParam);
if (lParam == TRUE)
{
GetWindowRect(hwndChild, &rcChildRect);
InvalidateRect(hwndChild, &rcChildRect, TRUE);
}
}
return TRUE;
}

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.

Resources