I'm using CreateWindowEx() function to create an "EDIT" window, i.e. where a user can type.
g_hwndMain = CreateWindowEx(0,
WC_TEXT,
NULL,
WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
0, 0, 400, 200,
phwnd, NULL,
g_hInstance, NULL);
But I would also like the window to be static. Is there a way to do it during the creation of the window? Or any other function that may be used after the creation of the window? I tried using SetWindowPos function after creating the window using SWP_NOSENDCHANGING and SWP_NOREPOSITION, but that didn't o the trick. ANy ideas?
No, I mean Immovable Window. Basically, the window I create should be able to accept text and be immovable at the same time.
You need to handle the WM_WINDOWPOSCHANGING message for that window and then set the SWP_NOMOVE flag of the flags member of the WINDOWPOS structure before you forward it along.
This blog post has an example (though he's preventing size changes, the technique is the same).
Thanks for your help. Ok So far I've done this to handle WM_WINDOWPOSCHANGING message
BOOL OnWindowPosChanging(HWND hwnd, WINDOWPOS *pwp)
{
return 0;
}
LRESULT CALLBACK
WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg) {
HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, OnWindowPosChanging);
}
return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}
and when I create my window, I do this:
g_hwndMain = CreateWindowEx(0,
TEXT("EDIT"),
NULL,
WS_BORDER,
0, 0, 400, 200,
phwnd, NULL,
g_hInstance, NULL);
if (!g_hwndMain) {
RemoveImages(spHTMLDoc);//Just so I know that the window has been created properly
}
else{
SetWindowPos(g_hwndMain, HWND_TOP, 500, 500, 300, 300, SWP_NOSENDCHANGING | SWP_SHOWWINDOW );
}
The SWP_NOMOVE flag does not let the code change the position of the window, but the user is still able to change the window's position by moving it using a mouse. But this is exactly what I want to prevent. The window should be static. Any thing missing in my code, or any more suggestions?
Related
I am trying to create a simple dropdown menu in a dialog box. Here is the bit of code that actually does it:
BOOL CALLBACK Remove(HWND hDlgc, UINT message, WPARAM wParam, LPARAM lParam)
//message handler for remove category box
{
//UNREFERENCED_PARAMETER(lParam);
HINSTANCE current = GetModuleHandle(NULL);
//GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_PIN, "comctl32.dll", NULL);
CreateWindow(WC_COMBOBOXW, _TEXT(""), CBS_DROPDOWN | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, 100, 100, 200, 200, hDlgc, NULL, NULL, NULL, NULL);
This will work and it will show the combo box, but only after waiting for 2 minutes or so... very undesirable! my program will go into a not responding state before the combo box shows up. The output shows that comctl32.dll get loaded and unloaded about 1500 times before the combo box shows up. When it does, it is still unresponsive and I have to wait more until it begins to work. I tried pinning the module to stop the loading and unloading but that did not do anything. Any help appreciated. As you can see I am very new to win32 programming. I got the backend of my program to work nicely, its just this gui that is bugging me.
EDIT: here is the as short as i could get it code. Just create a blank desktop project in VS, and then replace the "about" function in the bottom with the following: (and also include commctrl.h)
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
HWND dd_Hand = CreateWindow(WC_COMBOBOXW, _TEXT(""), CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE,
20, 20, 200, 200, hDlg, NULL, NULL, NULL);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
If I do this, I get the symptoms described previously.
EDIT AGAIN: I put the createwindow function for the combobox into the WM_CREATE case of WndProc, and everything works as it should, loads instantly. I am starting to doubt that this is the right way to create a combobox within a dialog box. Any suggestions for doing this another way (havent been able to find a way to do this with a splitbutton resource) are also welcome.
Solution was simple. just put this code:
HWND dd_Hand = CreateWindow(WC_COMBOBOXW, _TEXT(""), CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE,
20, 20, 200, 200, hDlg, NULL, NULL, NULL);
and the code that loads the combobox so it runs only once. No more problems. Also another even simpler way to do this would be to create a combobox resource and use the SendMessage() function.
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.
How do I get my window to register mouseclicks even tho ws_ex_transparent is on?
HWND hWnd = CreateWindowEx(WS_EX_LAYERED| WS_EX_TRANSPARENT, szAppName, wcWndName,
WS_VISIBLE | WS_POPUP, 255, 150, w, h,
NULL, NULL, hThisInst, NULL);
The window is flagged to be layered and transparent, along with
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_FRAMECHANGED);
placing it "topmost" (infront) makes it work as an overlay, but sadly, it doesnt register mouseclicks at all.
case WM_LBUTTONDOWN:
PostQuitMessage(0); //does it listen?
So my question is easy: How do I get my layered, topmost, transparent window to register me clicking the mouse?
any help would be, well, helpful. thanks
The solution turns out to be using a fairly standard hook feature, included in the windows header.
The keyboard hook looks a bit different than the mousehook, but since im after the mousehook, this is the solution im posting. (if u happen to look for a keyboard hook)
mousehook; //global declaration
LRESULT CALLBACK HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
PKBDLLHOOKSTRUCT k = (PKBDLLHOOKSTRUCT)(lParam);
if (wParam == WM_LBUTTONDOWN)
{
MessageBox(NULL, "LM is pressed", "key pressed", MB_ICONINFORMATION);
}
if (wParam == WM_RBUTTONDOWN)
{
MessageBox(NULL, "RM is pressed", "key pressed", MB_ICONINFORMATION);
}
return CallNextHookEx(Mousehook, nCode, wParam, lParam);
}
and calling it in winmain (before msg)
Mousehook = SetWindowsHookEx(WH_MOUSE_LL, HookCallback, NULL, 0);
Why are you mixing WS_EX_LAYARED and WS_EX_TRANSPARENT together, instead of using Layered Transparency?
In any case, see this article for some info about what WS_EX_TRANSPARENT actually does:
Like the cake, WS_EX_TRANSPARENT is a lie, or at least not the entire truth
For what you are asking, you need to handle WM_NCHITTEST and have it return HTCLIENT instead of HTTRANSPARENT for any area that you want to be clickable on a transparent window. However, WM_NCHITTEST does not work with WS_EX_LAYERED, so you will have to remove WS_EX_LAYERED and use WS_EX_TRANSPARENT by itself, and then process WM_ERASEBKGND and have it return 1 without drawing anything to get the transparent effect. Then you can use WM_NCHITTEST.
I'm trying to receive a message from a button when clicked.
But instead of receiving it from the main window, I would like to receive it from a tab control, which is a child of the main window. But I'm not sure how to go about this.
This is the creation of the button:
deAll = CreateWindowEx(0, "BUTTON", "Disable All", WS_CHILD | BS_PUSHBUTTON, 135, 49, 95, 26, tabs, (HMENU)204, instance, NULL);
Obviously, tabs is a tab control.
This is the creation of the tab control:
tabs = CreateWindowEx(0, WC_TABCONTROL, 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, rc.right + 2, rc.bottom - 22, hwnd, NULL, instance, NULL);
And hwnd is the main window.
Any ideas? Or do I have to make every control a child of the main window?
This was made using the Win32 API in C++.
The tabs window is the parent window of the button, so the button will send a WM_COMMAND/BN_CLICKED notification to the tabs window when the button is clicked. You will have to subclass the tabs window via either SetWindowLongPtr(GWL_WNDPROC) or SetWindowSubClass() to receive that message. For example:
WNDPROC prevWndProc;
LRESULT CALLBACK myWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if ((uMsg == WM_COMMAND) && (HIWORD(wParam) == BN_CLICKED))
{
// LOWORD(wParam) is the ID, and lParam is the HWND,
// of the button that was clicked. do something ...
}
return CallWindowProc(prevWndProc, hWnd, uMsg, wParam, lParam);
}
tabs = CreateWindowEx(0, WC_TABCONTROL, 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, rc.right + 2, rc.bottom - 22, hwnd, NULL, instance, NULL);
prevWndProc = (WNDPROC) SetWindowLongPtr(tabs, GWLP_WNDPROC, (LONG_PTR) &myWndProc);
Or:
LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
if ((uMsg == WM_COMMAND) && (HIWORD(wParam) == BN_CLICKED))
{
// LOWORD(wParam) is the ID, and lParam is the HWND,
// of the button that was clicked. do something ...
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
tabs = CreateWindowEx(0, WC_TABCONTROL, 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, rc.right + 2, rc.bottom - 22, hwnd, NULL, instance, NULL);
SetWindowSubclass(tabs, &mySubClassProc, 0, 0);
If a control is a child of a tab (or any other control) then it's the tab that will get notification messages like WM_COMMAND.
It's easiest to make your controls all children of your main window and just fix the z-order to make them appear in front of the tab (you already have WS_CLIPSIBLINGS set on the tab control, which you would also need). If you leave the controls as children of the tab then the only way to get notification messages is to sub-class the tab.
Or, you can do this the way property sheets do it, and use child dialogs (a dialog with the DS_CONTROL style set) to host the tab content. Then you can have a separate dialog procedure that handles messages from the child controls, and it makes it easy to show/hide a whole page of controls rather than handling them all individually. The TCM_ADJUSTRECT message can be used to calculate the size/position that you need to display your child dialog at.