Suppose hCtl is a handle to a control created without the WS_VISIBLE flag such as:
HWND hCtl = CreateWindowEx(0, WC_STATIC, L"some text",
WS_CHILD | SS_NOTIFY, // no WS_VISIBLE flag
0, 0, 0, 0, hWndParent, (HMENU)IDC_STATIC1, g_hInst, 0);
Is there a more straightforward way to make it visible than the following?
void make_visible(HWND hCtl, HWND hWndParent) {
SetWindowLongPtr(hCtl, GWL_STYLE,
GetWindowLongPtr(hCtl, GWL_STYLE) | WS_VISIBLE);
RECT rc{};
GetClientRect(hCtl, &rc);
MapWindowRect(hCtl, hWndParent, &rc);
InvalidateRect(hWndParent, &rc, TRUE);
UpdateWindow(hWndParent);
//ShowWindow(hCtl, SW_SHOW); // no use: does not update window
//SetWindowPos(hCtl, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); // no use: does not update window
}
To make a child control visible, call SetWindowPos like this:
SetWindowPos(hCtl, 0, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
Your code to call MapWindowRect, InvalidateRect, UpdateRect etc. should be removed.
Perhaps the real problem you have is that you have created the static control with zero width and height.
The normal way to make a window visible is only the function ShowWindow. There is no need dealing with flags and so on. Usually you use SW_SHOW as a parameter for a child window. Check the other values and use what you think is appropriate.
If the window has a visible rectangle and isn't covered by another window it will show up. Even UpdateWindow calls are not needed. The window will show up in the next paint cycle. If your control has a size of 0,0,0,0 (as it was created) it will never show up.
There is also a ShowWindowAsync function for the use if the window is on a different thread to avoid blocking.
BTW: I don't understand what you try with invalidating the parent window area. If there is a child window clipping (WS_CLIPCHILDREN) it has no effect.
I believe your problem is that you're explicitly setting the WS_VISIBLE style yourself and then calling ShowWindow, which confuses Windows into believing that the window is already visible and doesn't need to be repainted.
Just call ShowWindow. There should be no need to explicitly set WS_VISIBLE yourself because ShowWindow already does it. You should not need to forcibly repaint your control.
Additionally, if you find some need to explicitly invalidate your control, it should be sufficient to just do InvalidateRect(hCtl, NULL) and not bother with GetClientRect and MapWindowRect.
Related
I am trying to move a fullscreen window from the primary monitor to the secondary monitor. However, while the window appears as it should on the secondary monitor, it also still appears on the primary monitor. This is just a visual artifact as clicking the 'window' on the primary monitor causes the program to minimize just like clicking outside of a program normally would.
After maximizing the program the function works as intended and is able to move the program between the two monitors problem free.
What is causing this visual artifact and more importantly, how can it be fixed?
This is the code I am using, mostly found through trial and error by looking at what decreases the visual artifacts and what doesn't.
void MoveFullscreenWindowToMonitor(HWND hwnd, HMONITOR fromMonitor, HMONITOR hMonitor) {
MONITORINFO monitorInfo = { sizeof(MONITORINFO) };
if (GetMonitorInfo(hMonitor, &monitorInfo)) {
RECT monitorRect = monitorInfo.rcMonitor;
int width = monitorRect.right - monitorRect.left;
int height = monitorRect.bottom - monitorRect.top;
// Get the current window style
LONG style = GetWindowLong(hwnd, GWL_STYLE);
// Temporarily remove the WS_POPUP style to exit fullscreen mode
SetWindowLong(hwnd, GWL_STYLE, style & ~WS_POPUP);
// Move the window off the screen before moving it to the new monitor
SetWindowPos(hwnd, NULL, -width, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
// Move the window to the new monitor and resize it to fit the monitor
SetWindowPos(hwnd, NULL, monitorRect.left, monitorRect.top, width, height, SWP_FRAMECHANGED);
// Add the WS_POPUP style back to enter fullscreen mode
SetWindowLong(hwnd, GWL_STYLE, style);
// Move the window to the new monitor to fix the visual artifact
SetWindowPos(hwnd, NULL, monitorRect.left, monitorRect.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
// Create a window that spans the entire work area of the original monitor
//This part fixes another weird visual artifact on the target monitor
MONITORINFOEX mi;
mi.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(fromMonitor, &mi);
RECT rect = mi.rcWork;
HWND extrahwnd = CreateWindowEx(0, L"STATIC", NULL, WS_VISIBLE | WS_POPUP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, NULL, NULL);
// Force a screen refresh on the original monitor
UpdateWindow(extrahwnd);
// Destroy the window
DestroyWindow(extrahwnd);
// Restore the window and move it to the new monitor to fix the visual artifact
ShowWindow(hwnd, SW_RESTORE);
SetWindowPos(hwnd, NULL, monitorRect.left, monitorRect.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
// Give the window focus
SetForegroundWindow(hwnd);
// Invalidate the window to force a redraw on the original monitor
RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
}
}
EDIT: Everything works perfectly fine if I minimize and then maximize the application before running the code.
I have a button and an EDIT window i named "textbox" initially. What i want to achieve is: when i press the button, the EDIT window's border will be remove and it's text is also changed. Here is how i initial them:
HWND textbox; //global variable
//in WM_CREATE:
CreateWindowEx(NULL, L"BUTTON", L"Remove border",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 10, 150, 40, hwnd,
(HMENU)IDC_TEXTBOX, NULL, NULL);
textbox = CreateWindowEx(
NULL, L"EDIT", NULL,
WS_CHILD | WS_BORDER | WS_VISIBLE | ES_MULTILINE,
100, 100, 200, 100, hwnd, (HMENU) 0, NULL, NULL
);
SetWindowText(textbox, L"the initial text");
Since there is WS_BORDER in its style at the start, i thought removing it from window style will remove the border so this is my first attempt:
//In WM_COMMAND
case IDC_TEXTBOX: //if button is pressed
lStyle = GetWindowLongPtr(textbox, GWL_STYLE);
lStyle &= ~(WS_CHILD | WS_VISIBLE | ES_MULTILINE);
SetWindowLongPtr(textbox, GWL_STYLE, lStyle);
SetWindowPos(textbox, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE |
SWP_NOZORDER | SWP_NOOWNERZORDER);
SetWindowText(textbox, L"how to remove the border around this text???");
break;
The code above didn't work. The window disappeared after i pressed the button. In the second attempt i followed the answer in this question:
case IDC_TEXTBOX:
lStyle = GetWindowLongPtr(textbox, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
SetWindowLongPtr(textbox, GWL_STYLE, lStyle);
lExStyle = GetWindowLongPtr(textbox, GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLongPtr(textbox, GWL_EXSTYLE, lExStyle);
SetWindowPos(textbox, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE |
SWP_NOZORDER | SWP_NOOWNERZORDER);
SetWindowText(textbox, L"how to remove the border around this text???");
break;
This time, the text has changed but the border is still around after pressing the button:
so the question is: What did i do wrong in the 1st and 2nd attempt? And what should i do to remove the EDIT window border?
I would have made this a comment under your answer, but I don't have enough reputation.
Others in the comments have said that "Many of the system controls cache their initial styles and never update them" but strangely you can add a border after creation but just not remove it. So it seems that the border style is actually not cached.
My current solution is to recreate the control. Even though I can live with this solution (at the moment), I do not like it.
-Edit-
Never mind the above. I found that adding WS_BORDER after creation is actually a different border than the border added with WS_BORDER at creation. When you create the edit control with WS_BORDER and add WS_BORDER to it again with SetWindowLong() then the edit control now has 2 visible borders appearing as a single 2 pixel border. The WS_BORDER added after creation can be removed after creation but the initial WS_BORDER added at creation cannot be removed. So it appears that the initial edit border actually does get cached.
I want to change the background color of a STATIC window, both - on load and change it on runtime. So far I have been able to change the color the following way:
case WM_CTLCOLORSTATIC:
{
HDC hdcStatic = (HDC)wParam;
SetTextColor(hdcStatic, RGB(200, 200, 20));
SetBkColor(hdcStatic, RGB(10, 10, 10));
SetBkMode(hdcStatic, TRANSPARENT);
return (INT_PTR)CreateSolidBrush(RGB(30, 30, 30));
}
Everything works fine and the background color gets changed, except for any STATIC windows, which are inside another static window:
HWND mainContainer = CreateWindowEx
(
0,
_TEXT("STATIC"),
"",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_SOLID,
10, 10, 500, 500,
hwnd,
NULL,
(HINSTANCE)GetWindowLong(hwnd, GWLP_HINSTANCE),
NULL
);
HWND subItem = CreateWindowEx
(
0,
_TEXT("STATIC"),
"SubItem",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_SOLID,
10, 10, 100, 100,
mainContainer,
NULL,
(HINSTANCE)GetWindowLong(mainContainer, GWLP_HINSTANCE),
NULL
);
In this case the mainContainer color gets changed, but not the background color for subItem. Any ideas why this is happening? Thank you!
The message WM_CTLCOLORSTATIC will be sent only to the parent window, but not parent's parent window.
According to About Static Controls :
The window procedure for the predefined static control window class
performs default processing for all messages that the static control
procedure does not process.
The WM_CTLCOLORSTATIC is not in the list that it process. So The predefined window procedure will passes the message to DefWindowProc for default processing.
(We really don't often put a static window inside another static window. This is not a common operation. So you should reset the parent window of subItem to hwnd.)
I'm trying to create an window by CreateWindowEx, but seams even I give both dwExStyle dwStyle value 0, the window still have WS_CAPTION style.
Code snippet as following:
_hWnd = CreateWindowExW(iExStyle, pszClassName, pszTitle, iStyle | WS_CLIPCHILDREN, dX, dY, dWidth, dHeight,
hWndParent, 0, hInstance, NULL);
ASSERT(GetWindowLong(_hWnd, GWL_STYLE) & WS_CAPTION == 0); //<---- This will failed.
dwStyle = 0x00000000L means WS_OVERLAPPED or WS_TILED, this window has a title bar and a border.
Window Styles
As emax says, WS_OVERLAPPED (0) is the default and results in:
The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_TILED style.
If you are creating a child window you must specify WS_CHILD and if you are creating a "popup" window you must use WS_POPUP or WS_POPUPWINDOW.
A tooltip for example would use WS_POPUP and WS_EX_TOOLWINDOW + WS_EX_TOPMOST...
I am trying to draw a hbitmap with a layeredwindow directly onto the screen. this also works now how it should, yet the left upper corner of the image is always rounded.
I'm creating the window with:
HWND hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT
, szWindowClass, 0,
WS_VISIBLE
, 150,250, width, height, 0, NULL, hInstance, NULL);
Could anyone help me please to solve this problem?
You are using WS_VISIBLE as the window style, which is equivalent to WS_OVERLAPPED | WS_VISIBLE. Overlapped windows have rounded corners at the top.
Use WS_POPUP | WS_VISIBLE instead.