Flickering using WM_DRAWITEM - c++

It seems like I can't get pass this issue with owner-draw controls. I've super-classed a status control. I am trying to customize but still retain the same functionality. Basically, I want to change the background and text. I'm using Direct2d (or ID2D1DCRenderTarget interface) for the drawing. I have successful changed the background by using WM_NCPAINT; although, you can use WM_ERASEBKGRND if you want. However, both methods acted as a control in my experiment and flickering still occurred. Moreover, flickering doesn't occur when the WPARAM of SB_SETTEXT is NOT SET to SBT_OWNERDRAW. Therefore, I came to a conclusion that WM_DRAWITEM is the culprit. Is there anyway I can fix this flickering issue with owner-draw statusbar?

You can avoid flickering if you turn on double buffering for you control.
Set the WS_EX_COMPOSITED extended style:
http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543(v=vs.85).aspx
e.g. when handling WM_CREATE, call (WTL or MFC):
ModifyStyleEx(0, WS_EX_COMPOSITED);

Well, seems like I figured it out. When super-classing a status-bar follow these sets to avoid flickering.
**Note: This has only been tested with visual styles turned off. SetWindowTheme(hWndStatus, L"", L"");
Also, the parent window must have WS_CLIPCHILDREN set in the style parameter during window creation.
1: Override WM_SIZE. Make a call to InvalidateRect(m_hWnd, NULL, TRUE) and return 0 unless you want the default sizing; in this case, call CallWindowProc.
2: Override WM_ERASEBKGND and return -1.
3: Override WM_NCPAINT and place your drawing code here.
Handling WM_NCPAINT. People seem to have trouble understanding how to handle WM_NCPAINT. Here is how I do it.
if (wParam == 1) {
hdc = GetWindowDC(m_hWnd);
} else {
hdc = GetDCEx(m_hWnd, (HRGN) wParam, DCX_WINDOW | DCX_INTERSECTRGN | DCX_CACHE);
}
Then do drawing with the DC.
4: In the parent procedure (WndProc or whatever) call SetWindowPos(..., SWP_DRAWFRAME) with the handle to the statusbar. This will resize your statusbar.
5: Send a message via SendMessage(hWndStatusbar, SB_SETPARTS, 1, (LPARAM) &parts);
6: Send a message via SendMessage(hWndStatusbar, SB_SETTEXT, LOBYTE(0) | SBT_OWNERDRAW, L"Ready"). Sample code for WM_DRAWITEM:
...
WM_DRAWITEM:
LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) lParam;
m_pFramework->m_pD2D1RenderTarget->BindDC(lpDIS->hDC, &lpDIS->rcItem);
m_pFramework->m_pD2D1RenderTarget->BeginDraw();
m_pFramework->m_pD2D1RenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::CadetBlue));
D2D1_RECT_F rf = D2D1::RectF(
PixeltoDipX(lpDIS->rcItem.left),
PixeltoDipY(lpDIS->rcItem.top),
PixeltoDipX(lpDIS->rcItem.right),
PixeltoDipY(lpDIS->rcItem.bottom)
);
m_pFramework->m_pD2D1RenderTarget->DrawText(
(LPCWSTR) lpDIS->itemData,
wcslen((WCHAR*) lpDIS->itemData) + 1,
m_pFramework->m_pTextFormat,
rf,
m_d2dCaptionTextColor
);
m_pFramework->m_pD2D1RenderTarget->EndDraw();
break;
....
This should stop flickering. Also, do not call InvalidateRect(hWndStatus, NULL, TRUE) in the parent's WM_SIZE. This was the main reason it flickered.

Related

Can't show/hide buttons in the main window of my C++ Win32 app [duplicate]

This question already has answers here:
Access a variable from a different switch case (from WM_CREATE to WM_CTLCOLORSTATIC in the WinApi)
(2 answers)
Closed last month.
I have created a basic button in case: WM_CREATE in the windows procedure with the following.
/*The "new_game_button" is declared as type HWND at the
start of the windows procedure function but not initialized.*/
new_game_button = CreateWindow ( "BUTTON", "New Game",
WS_CHILD | WS_BORDER ,
50, 50, 100, 100,
hwnd, NULL, NULL, NULL);
My intent is to create an instructions and "start new game" button as the first thing in my simple tictactoe app. It will immediately show as expected if I give the parameter WS_VISIBLE.
Further in the same case:WM_CREATE if I use the lines
if (!start_Game){ //global variable default is false
ShowWindow( new_game_button, SW_SHOW);
}
The button will show as expected.
Outside of those two cases I cannot get the button to show at a later stage.
Further, if I use one of those two methods to show the button I can never get it to go away using
ShowWindow ( new_game_button, SW_HIDE);
Once the button is showing, it stays for the duration of the programs execution. Doesn't matter which case. Command/Create/Paint
I have tried using
if (start_Game){
ShowWindow( new_game_button, SW_HIDE);
UpdateWindow ( new_game_button );
//UpdateWindow ( hwnd ); tried this as well
}
inside case WM_CREATE.
I have also tried the same SW_HIDE line inside case: WM_COMMAND where a new game is generated (compiles but doesn't hide the button.)
I have tried declaring the button child window outside the WM_CREATE inside the windows procedure function. Then using WM_CREATE to show the window - works -- still WM_COMMAND will not hide the window.
I have also tried creating the button window inside of case:WM_PAINT which works to show the button but not to get rid of it. I have even tried DestroyWindow which just fails. [returns 0]
In trying to understand the behaviour of the button window - I have found that I cannot get
ShowWindow( new_game_button, SW_SHOW);
to work in the case:WM_COMMAND.
You said (in a code comment in the question).
new_game_button is declared as type HWND at the start of the windows procedure function but not initialized.
Each incoming message means a new call to your window procedure. Variables which are local to a function don't retain their value between calls unless they are marked static.
When your window procedure returned from processing WM_CREATE, you lost the value of new_game_button. When you try to use it during WM_COMMAND processing later, it is uninitialized and your program causes undefined behavior by passing it to ShowWindow.
Every comment helped me solve this which I appreciate. I am too new to give reputation sadly.
case WM_CREATE:
{
new_game_button = CreateWindow ("BUTTON", "New Game",
WS_CHILD | WS_BORDER ,
50, 50, 100, 100,
hwnd, (HMENU) 1, NULL, NULL);
if (!start_Game){
ShowWindow( new_game_button, SW_SHOW);
}
}
break;
After I had an ID for the dialog I am able to use GetDlgItem function to show or hide as I please.
new_game_button = GetDlgItem (hwnd, 1);
ShowWindow( new_game_button, SW_HIDE);

WS_EX_TOOLWINDOW tool windows doesn't work as expected

In our legacy code Windows extended style WS_EX_TOOLWINDOW is being used.This is basically for showing the title bar narrow.But recently in the later winodws versions the title bar is not drawn as narrow.That is WS_EX_TOOLWINDOW doesnt give a narrow title bar in the newer windows versions.Making the title bar narrow is done on a click event.Let me know if there is another way of achieving this?
I have read that we need to handle WM_NCCALCSIZE.But is there any other way of doing it?.Or if this is the only way,how can I handle it in a button click?
Code Snippet:
HWND hwnd = m_hWnd;
......
DWORD dwStylesEx = ::GetWindowLong( hwnd, GWL_EXSTYLE );
if ( bNarrowTitle == true)
{
dwStylesEx |= WS_EX_TOOLWINDOW;
}
else
{
dwStylesEx &= ~WS_EX_TOOLWINDOW;
}
...
::SetWindowLong( hwnd, GWL_EXSTYLE, dwStylesEx );
MSDN says:
Certain window data is cached, so changes you make using SetWindowLong will not take effect until you call the SetWindowPos function. Specifically, if you change any of the frame styles, you must call SetWindowPos with the SWP_FRAMECHANGED flag for the cache to be updated properly.
The default look just doesn't distinguish it in any way. Which suggests that you will just have to live with it.
It's probably been changed due to not being finger friendly if smaller!
Refer : WS_EX_TOOLWINDOW doesn't give look I want
As you said, handle WM_NCCALCSIZE may be the only way to handle the size of non-client areas.
Refer: How to set the size of the Non-client area of a Win32 window (native)

Switch between edit controls using Tab?

The Window is non DialogBox based so WS_TABSTOP doesn't work. Moreover I don't want to Tab through all the controls, I just want to Tab through few Edit controls.
What I did is I superclassed the Edit control and handled the WM_KEYDOWN message, switching between edit controls, by getting next window in the line thorugh ::GetWindow(hwnd,GW_HWNDNEXT); Also I would like to switch focus back to the first Edit control when I have reached the last one.
The Code doesn't work for when I have reached the last Edit control, the ::GetWindow simply returns the next window in the line(?), which happens to be a non superclassed edit control. And there are more hidden child windows(SW_HIDE).
Maybe if I know how to know the class name of the window's HWND ?
Note: Pure Win32 api, c++ oop.
else if ( ( int ) wParam == VK_TAB )
{
HWND nextInLine;
nextInLine = ::GetWindow ( hwnd, GW_HWNDNEXT );
if ( hwnd == NULL ) nextInLine = ::GetWindow ( hwnd, GW_HWNDPREV );
::SendMessage ( nextInLine, EM_SETSEL, ( WPARAM ) 0, ( LPARAM ) -1 );
::SetFocus ( nextInLine );
return 0;
}
You get keyboard navigation for free in any window by using the IsDialogMessage API call. To consume the service a window message loop has to be modified to include a call to IsDialogMessage and only pass the message on to regular message handling if it hasn't been handled by the dialog manager already.
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsDialogMessage(hwnd, &msg)) {
/* Already handled by dialog manager */
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Don't forget to set the WS_EX_CONTROLPARENT extended window style on the parent window, so that the dialog manager recurses into child windows.
It's possible to get away with just calling IsDialogMessage, but the result isn't quite 100% dialog-like. To make an ordinary window behave like a dialog:
Specify DLGWINDOWEXTRA as the cbWndExtra field of your WNDCLASS (don't forget to add on extra space you might already be using and offset your data's indexes)
Call DefDlgProc rather than DefWindowProc
Since this makes your window a dialog, you need to use the DWLP_USER window long instead of GWLP_USERDATA, if you're using that, when calling GetWindowLongPtr or SetWindowLongPtr.
(From memory, the main thing you get from doing the above is support for WM_NEXTDLGCTL, which I've found useful to use for supporting changing focus using the Enter key, using Method I described in http://support.microsoft.com/kb/102589.)
Then in your message pump, call IsDialogMessage for each dialog-like window in your message pump.
Finally, when creating controls for your dialog-like window, set the WS_TABSTOP window style for each window you want to participate in the tabbing, and set the WS_EX_CONTROLPARENT window exstyle (aka Control Parent in the resource editor) for child windows that contain dialog controls.

Trap a special click event on an Edit/Textbox control with C++/WinAPI/MFC

I'm coding in C++/MFC on a Windows platform (using MS VS2008.) I have this Edit control (which is basically a text box) that is set to be read-only. The control displays some basic information. I want to add an "Easter Egg" to my app, i.e. when a user Ctrl+Shift clicks on this edit control it must display some additional info. The question is how to trap such a click event using MFC/native WinAPIs?
The most straight forward way is to subclass the edit control using SetWindowLong and catch WM_LBUTTONDOWN event. You'd then want to call GetAsyncKeyState or equivalent to check whether the specific key is being pressed or not, and show the message.
There is no need to do subclassing. Just catch WM_PARENTNOTIFY
case WM_PARENTNOTIFY: {
if (LOWORD(wParam) == WM_LBUTTONDOWN)
printf("x: %i, y: %i\n", LOWORD(lParam), HIWORD(lParam));
}
break;
By default, child windows in a dialog box have the WS_EX_NOPARENTNOTIFY style and so doesn't notify the parent window. You should remove this style.
case WM_INITDIALOG: {
HWND hChildWnd = GetDlgItem(hWnd, IDC_CHILD);
LONG style = GetWindowLong(hChildWnd, GWL_EXSTYLE);
style &= ~WS_EX_NOPARENTNOTIFY;
SetWindowLong(hChildWnd, GWL_EXSTYLE, style);
...
P.S. I hope it's not too late :D

C++: How to center MessageBox?

Using Visual Studio C++ with MFC. How do I center a MessageBox to it's parent window? Currently it centers to the desktop.
You need to install a hook and change the dialog box position on creation.
int MessageBoxCentered(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
// Center message box at its parent window
static HHOOK hHookCBT{};
hHookCBT = SetWindowsHookEx(WH_CBT,
[](int nCode, WPARAM wParam, LPARAM lParam) -> LRESULT
{
if (nCode == HCBT_CREATEWND)
{
if (((LPCBT_CREATEWND)lParam)->lpcs->lpszClass == (LPWSTR)(ATOM)32770) // #32770 = dialog box class
{
RECT rcParent{};
GetWindowRect(((LPCBT_CREATEWND)lParam)->lpcs->hwndParent, &rcParent);
((LPCBT_CREATEWND)lParam)->lpcs->x = rcParent.left + ((rcParent.right - rcParent.left) - ((LPCBT_CREATEWND)lParam)->lpcs->cx) / 2;
((LPCBT_CREATEWND)lParam)->lpcs->y = rcParent.top + ((rcParent.bottom - rcParent.top) - ((LPCBT_CREATEWND)lParam)->lpcs->cy) / 2;
}
}
return CallNextHookEx(hHookCBT, nCode, wParam, lParam);
},
0, GetCurrentThreadId());
int iRet{ MessageBox(hWnd, lpText, lpCaption, uType) };
UnhookWindowsHookEx(hHookCBT);
return iRet;
}
::AfxMessageBox() appears on the center of the MainFrame for me. Which is basically a call to ::MessageBox() with a handle to the MainFrame as the first parameter. Isn't that working for you?
You can't. That's why a lot of people write their own MessageBox classes.
Who said "can't"?
Try this:
This is for Win32 API, written in C. Translate it as you need...
case WM_NOTIFY:{
HWND X=FindWindow("#32770",NULL);
if(GetParent(X)==H_frame){int Px,Py,Sx,Sy; RECT R1,R2;
GetWindowRect(hwnd,&R1); GetWindowRect(X,&R2);
Sx=R2.right-R2.left,Px=R1.left+(R1.right-R1.left)/2-Sx/2;
Sy=R2.bottom-R2.top,Py=R1.top+(R1.bottom-R1.top)/2-Sy/2;
MoveWindow(X,Px,Py,Sx,Sy,1);
}
} break;
Add that to the WndProc code... You can set position as you like, in this case it just centres over the main program window. It will do this for any messagebox, or file open/save dialog, and likely some other native controls. I'm not sure, but I think you may need to include COMMCTRL or COMMDLG to use this, at least, you will if you want open/save dialogs.
I experimented with looking at the notify codes and hwndFrom of NMHDR, then decided it was just as effective, and far easier, not to. If you really want to be very specific, tell FindWindow to look for a unique caption (title) you give to the window you want it to find.
This fires before the messagebox is drawn onscreen, so if you set a global flag to indicate when action is done by your code, and look for a unique caption, you be sure that actions you take will only occur once (there will likely be multiple notifiers). I haven't explored this in detail, but I managed get CreateWindow to put an edit box on a messagebox dialog. It looked as out of place as a rat's ear grafted onto the spine of a cloned pig, but it works. Doing things this way may be far easier than having to roll your own.
Crow.
EDIT: Small correction to handle the problem raised by Raymond Chen. Make sure that parent handles agree throughout, and this should work ok. It does for me, even with two instances of the same program...