I have a parent window (MFC dialog) with some controls on it (editbox, button, etc.), which looks like this:
At runtime (OnInitDialog), I create another child window which covers and hides all the other controls in the dialog, using this code:
RECT r;
GetClientRect(&r);
m_layer.Create(NULL, NULL, WS_CHILD | WS_VISIBLE, r, this, 0);
The m_layer object is an instance of a class CLaywerWnd inherited from CWnd. In this class I override the following method:
BOOL CLayerWnd::OnEraseBkgnd(CDC* pDC)
{
CBrush b(RGB(0, 100, 100));
RECT r;
GetClientRect(&r);
pDC->FillRect(&r, &b);
return TRUE;
}
Now my window looks like this:
The problem is that when I move the mouse cursor or click on this new child window the messages are forwarded to the parent window (I checked this using Spy++), and the other child controls are redrawn over the new child window, like bellow.
I don't understand why this happens and I want to know how to avoid this behavior.
I may well be wrong, but I wonder if you've mixed up in your code the handles to the two dialogs, such that you're posting messages to the wrong dialog?
One easy way to avoid it takes two steps:
Change the state of all the hidden controls to disabled, so they won't react to any messages.
Make sure the overlay window is at the top of the Z-order. You can do this when you create the window or later using SetWindowPos.
Related
I have such 3D scene in a window which was created using GLFW:
Now I'd like to have some buttons and lists and input fields in here too. WinAPI provides those. How do I achieve this and add controls to my window?
I checked the Internet, and I don't see much questions about using WinAPI controls with GLFW window. As far as I remember, GLFW does not appreciate when someone tries to snatch HWND of its window (I think, it's protected, since they strive for cross-platform implementation of their library). I have seen a question when someone tried to embed a GLFW window into another window, which does not fit my idea. I completely appreciate if GLFW handles an input on scene - mouse clicks on scene, drags, key presses and other events, but controls, of course, should be reachable as well. Alternatively I may go with Dear ImGui, and use those controls in my window, if adding WinAPI controls to GLFW window appears too complicated.
You have to do a better research. Instead of checking the Internet, why didn't you just visited the most relevant sites: WinAPI and GLFW
As far as I remember, GLFW provides a Native access.
For example, to create a button, you'll need the HWND of the parent window (see here):
HWND glfwGetWin32Window(GLFWwindow *window);
and then (see here)
HWND hwndButton = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
10, // x position
10, // y position
100, // Button width
100, // Button height
m_hwnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLongPtr(m_hwnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
After you've created the button, listen to the incoming events and act properly.
I have a main window which allows to display a child modal dialog. That dialog window is created based on the resource file using DialogBox function. It contains a few controls and a horizontal splitter which are repositioned/resized when handling WM_SIZE and WM_MOUSEMOVE messages. I use BeginDeferWindowPos, DeferWindowPos and EndDeferWindowPos functions. It looks like that everything is repainted correctly. Should I also call InvalidateRect after EndDeferWindowPos ? Is there any scenario where it may be necessary ? I don't handle WM_PAINT message.
The documentation for the DeferWindowPos function would strongly suggest that, so long as you don't have the SWP_NOREDRAW bit set in the uFlags argument, you do not need to call InvalidateRect after you have called EndDeferWindowPos:
SWP_NOREDRAW 0x0008 Does not redraw changes. If this flag is set,
no repainting of any kind occurs. This applies to the client area, the
nonclient area (including the title bar and scroll bars), and any part
of the parent window uncovered as a result of the window being moved.
When this flag is set, the application must explicitly invalidate or
redraw any parts of the window and parent window that need redrawing.
I have seen the following TVN_BEGINLABELEDIT event handling:
RECT rect={0};
TreeView_GetItemRect(hwnd, hitem, &rect, FALSE);
InvalidateRect(hwnd, &rect, TRUE);
Editing a tree view label without the above code works fine. Is it redundant or there may be a situation where such handling is necessary ?
The Remarks section of the TVN_BEGINLABELEDIT contains hints about a treeview control's internals:
When label editing begins, an edit control is created [...]
By default, that edit control is positioned and sized to cover the item that is to be edited. There is no immediately obvious reason to invalidate an area that's going to be covered by another control right away.
It doesn't even make sense to invalidate the area covered by the item, if the implementation chooses to provide a custom size and position for the edit control. By the time the WM_PAINT message gets handled, the treeview control still holds the initial value for that item, so parts not covered by the edit control merely get redrawn as they were.
I have a function which takes a rectangular region of a bitmap image, rescales it to different dimensions, and draws it at some offset inside of a window within my dialog-box application:
void DrawImage(HANDLE hImageBitmap,
CDC* pDstDC,const CRect& dstRect,
CDC* pSrcDC,const CRect& srcRect)
{
pSrcDC->SelectObject(hImageBitmap);
pDstDC->SetStretchBltMode(HALFTONE);
pDstDC->StretchBlt
(
dstRect.left,dstRect.top,dstRect.Width(),dstRect.Height(),pSrcDC,
srcRect.left,srcRect.top,srcRect.Width(),srcRect.Height(),SRCCOPY
);
}
I create and maintain the window using a CWnd m_cImageWindow member variable.
I perform the drawing from the dialog-box's OnPaint handler as follows:
CDC* pDC = m_cImageWindow.GetDC();
CDC cDC;
cDC.CreateCompatibleDC(pDC);
CRect srcRect = ...;
CRect dstRect = ...;
DrawImage(m_hImageBitmap,pDC,dstRect,&cDC,srcRect);
cDC.DeleteDC();
m_cImageWindow.ReleaseDC(pDC);
I have two problems:
I see flickering whenever I change the drawing parameters. The standard way to solve this, from what I have read here and there, is by using a temporary DC for double-buffering. But as far as I understand, this is exactly what I am already doing.
If some of the destination region falls outside the window, it is painted over other controls within the dialog box. I am able to partially solve this by calling MoveWindow or SetWindowPos for each one of these controls. But I can still see the image flickering behind them. I have tried calling SetWindowPos in various different ways, hoping in vain that it would dictate a strict Z-order of the controls.
Thank you.
The painting of the image into the child window should be done in the WM_PAINT handler for that child window, not for the dialog. Your child window may need remember information provided by the parent dialog so that it can paint independently. By painting the window from the dialog's WM_PAINT handler, you're possibly painting more often than necessary (and possibly aren't causing a validation to occur in the image window).
The dialog should probably have the WS_CLIPCHILDREN window style and your image window should probably have WS_CLIPSIBLINGS. This will prevent the dialog controls from drawing over each other, and it can reduce flicker by allowing for more minimal updates.
If the image will always completely cover the entire image window, then you want to make sure there's no background erasing happening for the image window, as that can cause a flash of the background color which looks like painting. There are several ways to do this, but the easiest is probably to provide a WM_ERASEBKGND handler that just returns TRUE.
I found OnEraseBkgnd to be the right place to minimize flickering of drawn bitmaps.
I have created my own custom drawn list with checkboxesin WTL, I want to make it scrollable now, the thing is that I am subclassing a Static Text control over which I draw.. And I don't know if static controls support scrolling in any way..
Anyway my question is how do I make my custom made controll scrollable, do I have to impement the mechanism myself?
Yes, you'll have to implement it entirely by hand. That's the drawback of not using a built-in control. It probably would have been a better idea to start off with a ListBox and then customize that to your desire. That way, you would get all of the scrolling, selection, and other logic for free.
The steps are roughly as follows (there are probably ATL/WTL idioms for some or all of these, but any ATL/WTL programmer can convert back and forth from raw Win32):
Add the WS_HSCROLL and/or WS_VSCROLL window styles to your custom static control, depending on if you want a horizontal, vertical, or both scroll bars. You would add these to list of window styles passed in to the CreateWindow/CreateWindowEx function.
By default, those scroll bars won't do anything at all. You need to tell them what to do using the SetScrollInfo function. In your case:
The first parameter (hwnd) would be the handle to your control window.
The second parameter (fnBar) should be either SB_HORZ to adjust the horizontal scroll bar, or SB_VERT to adjust the vertical scroll bar.
The third parameter (lpsi) is a pointer to a SCROLLINFO structure, filled in with the desired scrolling parameters, including the current position of the thumb, the minimum and maximum values, and the "page" size used to set up the proportional scroll bar.
The fourth parameter (fRedraw) should probably be set to TRUE.
You will also need the EnableScrollBar function to enable/disable the scroll bar as appropriate. Like the previous function,
hwnd is a handle to your control window
wSBflags is either SB_HORZ, SB_VERT, or SB_BOTH
wArrows is one of the ESB_* values, depending on what you want
Finally, you will want to write code in your custom control's window procedure to handle the WM_HSCROLL and/or WM_VSCROLL messages. These are sent to the window whenever the scroll bar is moved. Inside of the handler for these messages, you will want to do the following things to update the control's state:
Call the SetScrollInfo function to update the thumb to its new position
Redraw the contents of your control in accordance with the scrolled distance. There are multiple ways of doing this, but I'd probably use the ScrollWindowEx function.
The custom control's window procedure will also need to handle the WM_SIZE message to update the scroll bar state (by calling SetScrollInfo and/or EnableScrollBar) in response to changes in the window's size.
Cody Gray provided excellent introduction into adding support for scrolling, however what you also have is the help from the WTL itself.
WTL's atlscrl.h offers you classes to inherit from and implement a custom window/control with scrolling.
// Classes in this file:
//
// CScrollImpl<T>
// CScrollWindowImpl<T, TBase, TWinTraits>
// CMapScrollImpl<T>
// CMapScrollWindowImpl<T, TBase, TWinTraits>
// CFSBWindowT<TBase>
// CZoomScrollImpl<T>
// CZoomScrollWindowImpl<T, TBase, TWinTraits>
// CScrollContainerImpl<T, TBase, TWinTraits>
// CScrollContainer
Not so much code/snippets around to demo the use, but there is still one WTL sample that covers the basics and it should be a good starting point for you as well. \Samples\BmpView has a class for scrollable bitmap:
class CBitmapView :
public CScrollWindowImpl<CBitmapView>
{
public:
You will see it's really small and it covers most of the complexity.