I'm make window in c++ with supporting winapi(not MFC), and I made gif animation on window with gdi++, and I create TextBox, it showing, but some part is behind gif. Image in this link : 1
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE:
hMWDC = GetDC(hwnd);
pGphcs = new Graphics(hMWDC);
WCHAR path[MAX_PATH];
GetModuleFileNameW(NULL, path, MAX_PATH);
PathRemoveFileSpecW(path);
PathAppendW(path, L"gifs\\test.gif");
pImg = new Image(path);
if (pImg) {
nFrmCnt = pImg->GetFrameCount(&FrameDimensionTime);
SetTimer(hwnd, DRAW_ANIM, 100, NULL);
}
break;
case WM_TIMER:
if (wParam == DRAW_ANIM)
{
pImg->SelectActiveFrame(&FrameDimensionTime, nFrm);
Rect DRC(0, 0, pImg->GetWidth(), pImg->GetHeight());
pGphcs->Clear(Color(128, 128, 128));
pGphcs->DrawImage(pImg, DRC);
if (nFrm < (nFrmCnt - 1)) nFrm++; else nFrm = 0;
}
break;
hwndText = CreateWindow(L"EDIT",
L"",
WS_CHILD | WS_VISIBLE,
350, 480, 55, 20,
hWnd, NULL, NULL, NULL);
I expected gif as background and TextBox will be front of it.
But nothing, :(
For performance reasons by default device context for given HWND does not clip a child window from the parent window's client area.
If parent window draws something in the same location as the child window, it draws also over child window. Accepting some (nowadays minimal) performance hit you can automatically clip children windows form main frame window painting area, by adding WS_CLIPCHILDREN style when creating main frame window. With this style any HDC obtained for main frame window will have area occupied by child control excluded form painting area. This protects child window from overpainting by it's parent window activity.
Setting WS_CLIPCHILDREN for main frame window should solve your problem.
hwnd = CreateWindow( class_name, title, some_styles | WS_CLIPCHILDREN, ... );
Sometimes child windows overlap each other and then they can overdraw each other. In this case WS_CLIPSIBLINGS applied to child windows (of the same parent window), protects one child form another painting.
Related
I am working on a WinAPI only application. I want to create a STATIC child window that will be used as a "container" to child controls. In the examples that I have found so far, the "container" window is created catching the WM_CREATE in the main window procedure. I assumed the best place to create the child controls of the child window was by catching the WM_CREATE for the child window. To do this, I first need to subclass the child window to point to the new window procedure. The thing is the window is created before it is subclassed, therefore the WM_CREATE is sent to the original procedure rather than to my user created procedure. I can of course create the child controls after creating the child static window inside the main window procedure, but I do not believe this to be the best way to do it. What is the best option?
Working sample code:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
HWND hContainer = CreateWindow(WC_STATIC, L"Container", WS_CHILD, 0, 0, 100, 50, hWnd, (HMENU)ID_CONTAINER, NULL, NULL);
WNDPROC wpOldProc = (WNDPROC)SetWindowLongPtr(hContainer, GWLP_WNDPROC, (LONG_PTR)ChildWindowProc);
HWND hButton = CreateWindow(WC_BUTTON, L"Button", WS_CHILD | BS_PUSHBUTTON, 0, 0, 20, 10, hContainer, (HMENU)ID_BUTTON, NULL, NULL); // This works well
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK ChildWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
// HWND hButton = CreateWindow(WC_BUTTON, L"Button", WS_CHILD | BS_PUSHBUTTON, 0, 0, 20, 10, hContainer, (HMENU)ID_BUTTON, NULL, NULL);
// This point is never reached because the WM_CREATE message is sent before subclassing of the window
}
break;
default:
break;
}
return CallWindowProc(gsHdl.wpTE, hWnd, message, wParam, lParam);
}
The main principal here is separation of concerns: A window will typically create its children in response to its WM_CREATE message as:
This ensures the window "owns" the creation of its children. It also guarantees that when the parent window is created directly by classname, all the children get automatically created. And lastly, because WM_CREATE is sent before a window is made visible/painted, it ensures that the child windows are all created by the time the window is first painted.
A lot of these considerations fall away when using child windows to organise child windows :- If you are making a proper abstracted control window that is one thing, but if you are just arranging some relatively standard windows controls that you want the main window to be able to interact with then deep hierarchies are annoying to navigate, there are some weirdness in how mouse and painting happen where you might want to actually arrange the container and its children in the "wrong" z-order that you could not achieve if they were strictly arranged as child/parents.
Which is to say there is nothing wrong with creating a static window, and using the returned HWND as a parent window on subsequent lines - especially if that reduces the number of window classes, lines of code / creation convolution - and simplifies the parent windows relationship to its controls.
Creating sub-windows/controls under WM_CREATE messages does ensure that sub-windows/controls are created after successful window creation and that all sub-windows/sub-controls are automatically created when creating the main window.
But I don't think it's much different from the below:
HWND hContainer = CreateWindow (WC_STATIC, L "Container", WS_CHILD, 0, 0, 100, 50, hWnd, (HMENU) ID_CONTAINER, NULL, NULL);
if(hContainer)
{
HWND hButton = CreateWindow (WC_BUTTON, L "Button", WS_CHILD | BS_PUSHBUTTON, 0, 0, 20, 10, hContainer, (HMENU) ID_BUTTON, NULL, NULL);
}
It also ensures the creation order and preconditions.
If you really need to modify WinProc and capture WM_CREATE messages in a standard windows controls, SetWindowsLongPtr does not meet your requirements. Use SetClassLongPtr before creating "Container" instead.
I'm trying to change text/background color for the static control. I can do this just fine the following way:
// This is the 'main' window
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW &~WS_MAXIMIZEBOX | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
CW_USEDEFAULT, 0, 1035, 764, nullptr, nullptr, hInstance, nullptr);
...
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
case WM_CTLCOLORSTATIC:
{
MessageBox( NULL, "CTLCOLORSTATIC called", "", MB_OK );
HDC hdcStatic = (HDC)wParam;
SetTextColor(hdcStatic, RGB(200, 200, 20));
SetBkColor(hdcStatic, RGB(10, 10, 10));
return (INT_PTR)CreateSolidBrush(RGB(30, 30, 30));
}
default:
return DefWindowProc( hWnd, message, wParam, lParam );
}
But if I place the window inside another window, the child control text/background color stays default:
// This is the 'parent' window, which resides in the 'main' window
HWND parent = CreateWindowEx
(
0,
_TEXT("STATIC"),
"",
WS_TABSTOP | WS_VISIBLE | BS_SOLID | WS_CLIPCHILDREN,
10, 10, 500, 500,
hwnd,
NULL,
(HINSTANCE)GetWindowLong(hwnd, GWLP_HINSTANCE),
NULL
);
// This is the 'child' window which resides in the 'parent' window
HWND child = CreateWindowEx
(
0,
_TEXT("STATIC"),
"SubItem",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_SOLID,
10, 10, 100, 100,
parent,
NULL,
(HINSTANCE)GetWindowLong(parent, GWLP_HINSTANCE),
NULL
);
To conclude, I have 3 windows:
HWND hwnd; // the 'main' application window (color changes fine)
HWND parent; // the 'parent/container' window which is inside the 'main' window (color changes fine)
HWND child; // the 'child' window which is inside the 'parent' window (color DOES NOT change)
Even though if I put MessageBox inside the WM_CTLCOLORSTATIC, I see it triggering every time the children is drawn, yet the color is not being changed for the child, only for the parent.
As far as I understood I need to handle the message in the main window procedure, but I'm not entirelly clear how to do this. if I compare the (HWND)lParam to the childrens HWND, they're the same (within the default switch case), so I can get the reference in the 'default' section, but I'm not sure how I should handle it from there..
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
case WM_CTLCOLORSTATIC:
{
if ((HWND)lParam == child )
MessageBox( NULL, "Reference Match for CTLCOLORSTATIC", "", MB_OK ); // <-- THIS NEVER TRIGGERS
}
....
default:
if ((HWND)lParam == child )
MessageBox( NULL, "Reference Match for DEFAULT", "", MB_OK ); // <-- THIS DOES TRIGGER
return DefWindowProc( hWnd, message, wParam, lParam );
}
return 0;
}
The reason I'm placing one window inside another is so that I can use WS_CLIPCHILDREN property in the parent (becase the child will move/scroll).
WM_CTLCOLORSTATIC is sent by a static control to its parent window, not the top level window.
hwndMain: WM_CTLCOLORSTATIC for hwnd1
|
\--hwnd1: WM_CTLCOLORSTATIC for hwnd2 (You might have to subclass hwnd1)
|
\--hwnd2
You are also leaking brushes, store the brush from CreateSolidBrush somewhere when you create the window and delete it when the window is destroyed.
By reading the MSDN document, I know a function, SetWindowsLongPtr, whose parameter GWLP_WNDPROC can set a new address for the window procedure.
This function can change the text color and background color of the child window, that is to say, it can trigger WM_CTLCOLORSTATIC.
But after testing, I found that it is invalid to the secondary window, that is to say, it can not change the text color of the parent window.
I also consulted a lot of information, and very few documents related to three-tier windows.
So, I think to solve this problem and make all three windows change the color of the text, you may have to rewrite WndProc by yourself, but this is very complicated and involves a lot of things.
Edit: If you just need to change the text color and background color
of the static control, you can customize a control so that you can
handle all its operations.
Hope to help you.
I need a window featuring no title bar, none of the control boxes, no system menu, and no frames (all that functionality is provided with separate controls).
I suspect that this should be possible to do with CreateWindowExA's window styles argument dwStyle and possibly lpWindowName, as described here: https://learn.microsoft.com/en-us/windows/desktop/winmsg/window-styles
This is how the arguments look like originally:
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class.
L"", // No window name (title text).
WS_OVERLAPPEDWINDOW, // Window style.
// Size and position.
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window.
NULL, // Menu.
hInstance, // Instance handle.
NULL // Additional application data.
);
However, in dwStyle, the normal window style WS_OVERLAPPEDWINDOW is defined as
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
with WS_OVERLAPPED being 0x00000000L.
Simply providing 0 and omitting the rest doesn't work, as also the documentation implies: "The window is an overlapped window. An overlapped window has a title bar and a border."
(The funny thing is, I am perfectly able to do this task in VB.NET (and even in VB6) by setting the ControlBox property to False and then by removing the titlebar using Text = "", so I strongly suspect that when possible in VB...)
How would I do my task in C++?
Just in case the WindowProc is needed in order to process a different message, here it is in its minimalistic version:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
(Compiling with VS 2017.)
The non-client area of a top-level window can be removed by using only the WS_POPUP style:
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class.
L"", // No window name (title text).
WS_POPUP, // Window style.
// Size and position.
100, 100, 400, 300,
NULL, // Parent window.
NULL, // Menu.
hInstance, // Instance handle.
NULL // Additional application data.
);
Note that CW_USEDEFAULT for size and position is only valid for overlapped windows. For popup windows you have to be explicit.
Depending on your use case, the technique described by this answer might be better suitable. Using the DWM API, it allows you to remove the non-client area, but keep the drop shadow to make the window stand out better from the background.
I am working on Zoom SDK which is based on win32 gui.
I have created 3 buttons using CreateWindow method on the window handle, which is provided by the ZoomSDK.
Code + Screenshot - 1
Now there are two problems with this.
As soon as I click the buttons, they disappear.
See the Screen Shots BEFORE
See the Screen Shots AFTER
I want to know the reason why this is happening and how can I fix this?
HWND hFirstView, hSecondView;
cntrl->GetMeetingUIWnd(hFirstView, hSecondView);
cntrl->MoveFloatVideoWnd(100, 100);
HWND btnHwnd = CreateWindow(
TEXT("button"),
L"Open App",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
0, 0,
50, 25,
hFirstView,
(HMENU)100,
hInst,
NULL);
HWND btnHwnd2 = CreateWindow(
TEXT("button"),
L"Other",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
50, 0,
50, 25,
hFirstView,
(HMENU)101,
hInst,
NULL);
HWND btnHwnd3 = CreateWindow(
TEXT("button"),
L"Raise Hand",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
100, 0,
50, 25,
hFirstView,
(HMENU)103,
hInst,
NULL);
HDC hdc = GetDC(btnHwnd);
SetBkColor(hdc, GetSysColor(COLOR_BTNSHADOW));
SetTextColor(hdc, GetSysColor(COLOR_BACKGROUND));
ReleaseDC(btnHwnd, hdc);
int btnId = GetDlgCtrlID(btnHwnd);
//oldWndProc = (WNDPROC) GetWindowLong(hFirstView, GWL_WNDPROC);
oldWndProc = (WNDPROC) SetWindowLong(hFirstView,
GWL_WNDPROC, (LONG)WndProc);
SendMessage(btnHwnd, BM_SETSTATE, 1, 0);
SetWindowText(hFirstView, L"Title");
I want to handle click event for these buttons. I have tried to use SetWindowsLong to set another WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int id = GetWindowLong(hWnd, GWL_ID);
switch (message)
{
case WM_COMMAND: {
MessageBox(NULL, L"Sign of releaf!", L"Whoaa!", 0);
if (wParam == 1023) {
MessageBox(NULL, L"Sign of releaf!", L"Whoaa!", 0);
}
}
break;
}
return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
}
Now, this WndProc gets called for other events such mouse move etc. It is not working for my three buttons. I want to handle click event i.e WM_COMMAND or any other technique possible.
Because I can not go inside the sdk (they don't provide sources, only .lib) so I can not change their WndProc, nor their internal WM_PAINT. The buttons are sort of overlay on top.
You should not be calling SetBkColor() and SetTextColor() from outside a WM_PAINT handler. The correct way to color a button is to either:
have the parent window handle the WM_CTLCOLORBTN notification.
The WM_CTLCOLORBTN message is sent to the parent window of a button before drawing the button. The parent window can change the button's text and background colors.
give the button the BS_OWNERDRAW style, and then have the parent window handle the WM_DRAWITEM notification.
Sent to the parent window of an owner-drawn button, combo box, list box, or menu when a visual aspect of the button, combo box, list box, or menu has changed.
Also, when a button sends a BN_CLICKED notification to its parent window, your subclass WndProc() doesn't need to use GetWindowLong(GWL_ID). First, you are calling it on the wrong HWND. And second, the button ID is carried in the message's wParam data.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case 100:
case 101:
case 103:
MessageBox(NULL, L"Sign of relief!", L"Whoaa!", 0);
break;
}
}
break;
}
}
return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
}
I am trying to create a child window within an MDI MainWindow...The main window is 1024 x 768 in size...when I try to use CreateWindowEx() to create a child window the size seems to be offset somehow...in other words a 1024 x 768 child window is smaller than its parent...how can I account for this difference? When I use GetCLientRect() with the main window's handle...the size is still smaller...I don't care to make the child window the same size. I want the child window to encompass the exact size of an image that is 750x563. When I set that size within the parameters of CreateWindowEx(), the child window is too small...
Here is the code I use within the MainWndProc to create a child window:
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT Msg,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch(Msg)
{
case WM_CREATE:
{
//OnCreate(hWnd, NULL);
CLIENTCREATESTRUCT ccs;
ccs.hWindowMenu = GetSubMenu(GetMenu(hWnd), 2);
ccs.idFirstChild = StartChildrenNo;
hWndChildFrame = CreateWindowEx(WS_EX_CLIENTEDGE,
L"MDICLIENT",
NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL
| WS_HSCROLL | WS_VISIBLE,
0,
0,
1024,
768,
hWnd,
(HMENU)IDM_FILE_NEW,
GetModuleHandle(NULL),
(LPVOID)&ccs);
if(hWndChildFrame == NULL)
MessageBox(hWnd, L"Could not create MDI client.", L"Error", MB_OK | MB_ICONERROR);
CreateNewMDIChild(hWndChildFrame);
return 0;
}
...
}
To determine size how the MDI child window can be large, use GetClientRect() of the MDI client window.
To compute needed window size for the MDI child window use AdjustWindowRect() or AdjustWindowRectEx(). Those can convert between window and client size.