Sometimes, when I use ShowWindow(SW_SHOWMAXIMIZED), my MFC window is maximized, but it is not positioned at coordinates (0, 0), as it should, it is moved down, for around 30-40 pixels. Any tip on what might be wrong?
I found the solution. In PreCreateWindow(CREATESTRUCT& cs), CREATESTRUCT was modified, like this
cs.style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE;
Apparently, this was causing wrong WINDOWPLACEMENT coordinates. When changed to:
cs.style = WS_OVERLAPPEDWINDOW;
with call to
ShowWindows(SW_MAXIMIZE)
Everything worked fine.
Related
When I remove the sizing border of a window, it leaves black areas around it.
Is it possible to somehow tell the window to occupy/paint these areas, as there's no sizing border anymore?
A reproducible example: launch a instance of chrome browser with:
chrome.exe -app=https://www.google.com
Remove the sizing border:
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle &= ~(WS_THICKFRAME | WS_DLGFRAME);
SetWindowLong(hwnd, GWL_STYLE, lStyle);
WS_THICKFRAME
0x00040000L
The window has a sizing border. Same as the WS_SIZEBOX style.
WS_DLGFRAME
0x00400000L
The window has a border of a style typically used with dialog boxes. A window with this style cannot have a title bar.
The black areas im talking about: left, right, bottom.
As suggested, I use SetWindowPos() with SWP_FRAMECHANGED, but it didn't result in any visible change.
int flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED;
SetWindowPos(hWnd, 0, 0, 0, 0, 0, flags);
Code:
#include <iostream>
#include <windows.h>
int main()
{
HWND hwnd = FindWindowW( L"Chrome_WidgetWin_1", nullptr );
//HWND hwnd = FindWindowW( L"Notepad", nullptr );
std::cout << "hwnd: " << hwnd << "\n";
LONG lStyle = GetWindowLong( hwnd, GWL_STYLE );
lStyle &= ~( WS_THICKFRAME | WS_DLGFRAME );
SetWindowLong( hwnd, GWL_STYLE, lStyle );
int flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED;
auto swp = SetWindowPos( hwnd, 0, 0, 0, 0, 0, flags );
std::cout << "swp: " << swp << "\n";
}
TL;DR
Considering that it works for notepad, it must be a specialty of Google Chrome. The difference should be that Chrome is drawing into the non-client area of the window (i.e. the tabs appear in the title bar, which is kind of special). That means that Chrome is doing a lot of drawing and computations by itself. Apparently, the code does not handle the case that the window has no sizing borders (because Chrome by itself does not support this). Instead, they hardcoded that the client rectangle is always smaller by the sizing border width, even if there is no sizing border.
A workaround would be to start Chrome with --disable-dwm-composition. But I do not really know if there are any unintended side effects when using this option.
Details
Digging through the source code of Chromium, one can see:
First note that the WinAPI function DwmExtendFrameIntoClientArea() is called, which means that Chromium has indeed the special drawing logic to draw the window frame by itself.
To remove the standard frame, the WM_NCCALCSIZE message is handled. Therein, we can find a call to GetClientAreaInsets(), which returns the "insets", i.e. 4 values (for left, top, bottom and right) by which the client rectangle becomes smaller.
Searching for GetClientAreaInsets(), we see that its implementations essentially calls ui::GetFrameThickness() and uses its return value as the inset value. But only if the window is not in fullscreen; indeed, the black border effect does not appear in fullscreen.
ui::GetFrameThickness() calls (indirectly) the WinAPI function GetSystemMetricsForDpi() to get the values of SM_CXSIZEFRAME and SM_CXPADDEDBORDER, i.e. the size of the sizing border. Their sum is returned.
The SM_CXSIZEFRAME and SM_CXPADDEDBORDER apparently map to the registry values BorderWidth and PaddedBorderWidth in HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics. I.e. they are system settings. Changing these values (to a notable more negative value) and restarting the PC does not make the drawn border of windows larger on Windows 10 (although it did in earlier Windows versions). But they do change the invisible "capture" area of the sizing borders. And, in our case here, the black borders shown by Chrome also become larger.
I think the former sizing border areas are drawn black after removing them because of the BLACK_BRUSH in HWNDMessageHandler::OnPaint.
So, in a nutshell, Chrome is simply making the client area smaller by the sizing borders, regardless if the sizing borders actually exist or not. And then draws everything in there. If Chrome were supporting removed sizing borders officially, it could be considered as a bug. But I don't think they do.
How do we work around this behavior from the "outside"?
Considering that SM_CXSIZEFRAME and SM_CXPADDEDBORDER are global settings, changing them (by means of the registry or the SystemParametersInfo(SPI_GETBORDER,...) function is probably out of the question for practical uses.
But looking through the source code, we note that in HWNDMessageHandler::UpdateDwmFrame() the function ui::win::IsAeroGlassEnabled() is called, which in turn returns false if the disable-dwm-composition commandline flag is specified. Hence, starting Chrome with the --disable-dwm-composition flag and then running the code in the OP (removing WS_THICKFRAME | WS_DLGFRAME) does the trick for me. The invisible sizing borders are truly gone, i.e. clicks onto the space where they were are no longer registered by Chrome but by the window below it (e.g. the Desktop).
I'm working on an Windows Application which has to show an overlaying fixed positioned window ("PopUp") in the left corner of the MainFrame which will receive some Information if a user missed some input or if certain actions have been successfully.
The "PopUp" Titlebar shall have an Icon next to the Title (e.g. ->Icon<- "Error") and the standard X - Close-Button. The ClientArea will have an descriptive text of the occurred Message.
Additionally the standard Border of the PopUp shall be set to 1px(smaller than the default windows border)
The "PopUp" is derived from CWnd and created with WS_VISLBE | WS_CLIPSIBLINGS | WS_CHILD | WS_CAPTION in the OnCreate-Method of the Applications MainFrame Window
Now I need to set/shrink the default Border of my PopUp and add the Icon to the Titlebar of the PopUp.
Can someone give me some example code of how i can solve my issues?
I'm pretty new to c++ and MFC so far my research brought me to https://msdn.microsoft.com/en-us/library/windows/desktop/bb688195(v=vs.85).aspx
but i dont know where and how to use DwmExtendFrameIntoClientArea() but so far I've read I assume Dwm is the way to go to be able to solve both problems or is there another/totally different way? Am I on the right track?
Finally I was able to shrinkthe default Windows Border by overriding the handling of WM_NCCALCSIZE.
I will update this answer as soon as I solved how to put my Icon in the Titlebar.
As of now I'll explain how I shrink the windows border:
Add ON_WM_NCCALCSIZE() to your MessageMap of the desired Window and Implement OnNcCalcSize() (Class Wizard will help to set this up) as followed:
void YourCWndClass::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
if (bCalcValidRects){
CRect rcClient, rcWind;
GetClientRect(&rcClient);
GetWindowRect(&rcWind);
int border = (rcWind.right - rcWind.left - rcClient.right) / 2 - 1;
//-1: leaves 1px of the Windows Default Border Width erase to have no border
lpncsp->rgrc->left -= border;
lpncsp->rgrc->right += border;
lpncsp->rgrc->bottom += border;
}
CWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
}
The WM_NCCALCSIZE Message is sent up on the Window Creation (when you call Create()/CreateEx() ) but at this point of time GetClientRect() and GetWindowRect() will not return the proper values therefore you need to check the Bool Parameter!!!
To trigger another WM_NCCALCSIZE to be able to work with the proper Window Rectangles call SetWindowPos() right after the window creation
if (!m_MessagePopOver->Create(NULL, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CAPTION, rect, this, NULL, NULL)){
TRACE0("failed to create MessagePopOver");
}
m_MessagePopOver->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
This will result in a window like this:
In using layered windows in win32 or atl/wtl c++ if I set the main window's alpha to 0 and paint on the child, fake window so that it is viewable and click the window, the entire window is a click through.
I want to be able to make only regions of the window click through, not the entire window, let's say if I want to paint a rounded corner window, I make the bottom/main window to be click through but I don't want the upper "fake" window to be click through, i want to be able to click on it. How do I do that?
Where I am so far:
In the OnInitDialog function of the main window :
::SetWindowLong( m_hWnd, GWL_EXSTYLE, ::GetWindowLong(m_hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
BYTE bTran = 0;
::SetLayeredWindowAttributes( m_hWnd, 0, bTran, LWA_ALPHA);
and when I create the fake window:
m_hFakeWnd = ::CreateWindowEx( WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE | WS_EX_LEFT
, m_strWndClassName
, NULL
, WS_VISIBLE | WS_OVERLAPPED
, rc.left
, rc.top
, rc.Width()
, rc.Height()
, GetSafeHwnd()
, NULL
, ::GetModuleHandle(NULL)
, NULL
);
IF, I set eliminate the WS_EX_TRANSPARENT flag the fake window is click-able while the main is click through, but! it doesn't respond to anything! click/drag. none.
It sounds like you are covering another window solely for the purpose of intercepting clicks?
Anyway, you need to handle window's WM_NCHITTEST message in order to be able to let system know that particular position is transparent, in which case you return HTTRANSPARENT:
In a window currently covered by another window in the same thread (the message will be sent to underlying windows in the same thread until one of them returns a code that is not HTTRANSPARENT).
Use alpha 1 instead of 0 in the regions you want to accept clicks. The window will still be completely invisible but the areas of alpha 1 will register clicks and mouse movements as normal.
Note that to get per-pixel alpha you'll need to use UpdateLayeredWindow rather than SetLayeredWindowAttributes.
Make two windows, one with click-through properties and another with normal ones.
I am quite desperate to resolve this very annoying issue :(
I am trying to display a child window on parent window. Some time the window need to be resized. But for some reason, when I using MoveWindow function it leaves blank space on the top of the parent window. I would like to present a picture here but I can not post a picture.
Here is the code example:
HWND hwnd // Comes from external function. Was defined as WS_CHILD previously
HWND hwndParent = ::GetParent(hwnd);
RECT parentRect = {0,0,0,0};
RECT childRect = {0,0,0,0};
::GetClientRect(hwndParent, &parentRect); // Suppose it returns {0,0,600,300}
BOOL ok = ::MoveWindow(hwnd, 0, 0, 600, 300, true);
::GetClientRect(hwnd, &childRect); // Will return {0,0,584,297}
WHY ?????
What am I doing wrong? Did I forgot some flags with window initialization?!
Rather than use GetClientRect, use GetWindowRect and MapWindowPoints(NULL,hwndParent,&parentRect,2) to adjust it to the parent window coordinates. GetWindowRect will include the non-client area that MoveWindow requires.
Edit: If you want a window that doesn't have a non-client area so the window rect and the client rect are the same size, you need to trim the window styles that you apply to the window. Avoid the WS_BORDER, WS_CAPTION, WS_DLGFRAME, WS_OVERLAPPED, WS_SIZEBOX, and WS_THICKFRAME styles.
MoveWindow updates window position, while GetClientRect gets a client-area part of the window, which does not have to be the same. If your window has non-client area, then everything is fine and works as expected.
If you are still under impression that child window does not fully cover parent's client area, then the spacing belongs to the child control/window, and you need to look for ways to remove it there (control flags, parameters etc).
MoveWindow operates on window coordinates -- including non-client area (borders, title bar, etc).
GetClientRect gets the area of the client portion of the window, ignoring borders, title bar, etc.
This is where the mismatch is. If you want to MoveWindow to a desired client size, you need to just AdjustWindowRect to try and predict what to pass into MoveWindow. Note that it's not always possible, and not always accurate. For example minimum / maximum sizes of windows, menus (which can wrap to multiple lines), etc.
The problem was WS_POPUP flag to the parent window.
Very strange. As far as I know it was not suppose to have such an effect.
Thanks for everyone!
I will try to explain my problem the best i can,
I'm creating a layered window in c++ (using windowsXP), all works fine until i drag my created window near the windows start button, and then when i press the star button of windows taskbar and close it again all the windows beneath of my layered window aren't being painted (only in the area of the start window that pops over my window).
My create window is like this:
CWnd::CreateEx( WS_EX_TOOLWINDOW |
WS_EX_LAYERED,
AfxRegisterWndClass(0),
lpstr_name, WS_POPUP, 0,0,0,0,
pc_parent->GetSafeHwnd(), 0);
...
When i create the window with this styles the problem ocurrs, but if i create with the extended style WS_EX_TRANSPARENT and all the others the problem does not occur any more. And if instead of a WS_POPUP window is a WS_CHILD or WS_OVERLAPPED then this also doesn't occur...
Can anyone please explain why when i create a WS_POPUP window with the WS_EX_LAYERED style all the beneath windows aren't updated, and if i add the style WS_EX_TRANSPARENT this works fine.
Note: why i do not use the WS_EX_TRANSPARENT style if it works right? if i use it then my window can not be dragged and i need it to do it :)
Updated:
alt text http://img17.imageshack.us/img17/586/clipboard01il.jpg
The image above is to describe better what is happening:
The first part of the image you can see my leyered window and beneath is the vs, in the second img i press the start button and then in the last image i already drag my layered window to the right and you can see that the vs window does not updates the affected area.
Note that this situation until now only occurs with the start window?! with other windows it does not happen!?...
Thanks
only in the area of the start window that pops over my window
That's expected. Only that clipping rectangle is obscured by the start menu so only that region will be repainted. What behavior are you expecting? If there are windows covered by more upper level windows, then they won't be repainted either -- why repaint something just to paint over it?
All underneath windows need to get repainted though if you use transparent because GDI can't calculate the final color of the pixel without knowing the area below the window's color.