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.
Related
I'm working on an on-screen Annotation flutter desktop app where I want to let the mouse click pass through to the window beneath my window. Now Basically I know that there is a win32api window that hosting the flutter content.
Also, I was able to find a way by using SetWindowLong and applying GWL_EXSTYLE to my window.
int extendedStyleSettings = GetWindowLong(window,GWL_EXSTYLE);
SetWindowLong(window,GWL_EXSTYLE, extendedStyleSettings | WS_EX_LAYERED | WS_EX_TRANSPARENT);
SetLayeredWindowAttributes(window, 0, 255, LWA_ALPHA);
The problem with this solution is I don't know how to restore mouse event on windows or keep it active on my window because I can't do anything I can't click on the border, caption bar, system menu, or minimize/maximize/close buttons.
It just completely disables my window
I'm trying to create a dynamic custom control on a resource dialog at runtime which overlaps an already existing control. However, when I do that, it causes a weird artifact.
If I position the new control after the other control in the z-order, my dynamic control is drawn over top of the resource control, as I expected. However, if I click on a spot that is shared between the two controls, it will select the resource control.
If I position the new control before the other control in the z-order, my dynamic control is drawn over by the resource control, again as expected. However, if I again click on the spot that is shared between them, it will select the new control.
What I would have expected is that the control in the top of the z-order would have any clicks directed at them. The actual results are counter intuitive. Why is this happening?
As a code example, I've created an MFC dialog application, where the dialog useds two listboxes to remove any issues with any custom control errors. One listbox is added to the resource with id IDC_LIST1 with member variable name m_dlgResCtrl. The second has the member variable name m_dlgAddedCtrl. The following code is added to the OnInitDialog() member function:
CRect rect;
m_dlgResCtrl.GetWindowRect(rect);
ScreenToClient(rect);
rect += CPoint(20, 20);
m_dlgAddedCtrl.Create(LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
, rect, this, IDC_LIST1 + 1);
m_dlgAddedCtrl.SetFont(GetFont());
// place before resource control in z-order
//m_dlgAddedCtrl.SetWindowPos(m_dlgResCtrl.GetWindow(GW_HWNDPREV), 0, 0, 0, 0
, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// place after resource control in z-order
m_dlgAddedCtrl.SetWindowPos(&m_dlgResCtrl, 0, 0, 0, 0
, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// added some text to show overlap
m_dlgResCtrl.AddString(L"Res ctrl");
m_dlgAddedCtrl.AddString(L"Added ctrl");
When placed after res control:
After clicking on shared space:
When placed before res control:
After clicking on shared space:
NOTE: this behavour is not limited to dynamic controls. Just moving the OK button control to overlap the Cancel button control shows the same problem. The OK has a z-order number of 1 and the Cancel of 2. The Cancel shows over top of the OK but when clicked in the overlapping region, the OK is the one that is clicked.
You are confusing z-order and drawing order. They are not necessarily related.
A window that is before another window in the z-order, is above the other window. Reference.
So this code actually positions m_dlgAddedCtrl below m_dlgResCtrl:
// place after resource control in z-order
m_dlgAddedCtrl.SetWindowPos(&m_dlgResCtrl, 0, 0, 0, 0
, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
This code actually positions m_dlgAddedCtrl above m_dlgResCtrl:
// place before resource control in z-order
//m_dlgAddedCtrl.SetWindowPos(m_dlgResCtrl.GetWindow(GW_HWNDPREV), 0, 0, 0, 0
, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
Considering this, the click behaviour in both cases is correct. In the overlapping area, the topmost child window receives the mouse click and gets focused.
Only the drawing order appears incorrect. It may come as a surprise that Windows doesn't automatically respect the z-order when drawing child windows! It just sends WM_PAINT messages to all child windows with a non-empty update region which are then free to draw over each other in whatever order the WM_PAINT messages arrive.
To fix that, simply add the WS_CLIPSIBLINGS style to each child window that may overlap other child windows:
From MSDN:
when a particular
child window receives a WM_PAINT message, the WS_CLIPSIBLINGS style
clips all other overlapping child windows out of the region of the
child window to be updated. If WS_CLIPSIBLINGS is not specified and
child windows overlap, it is possible, when drawing within the client
area of a child window, to draw within the client area of a
neighboring child window.
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:
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.