Get ListBox handle from Window handle - winapi

I am incredibly confused by the whole "window" verses "listbox" thing in the WIN32 API. I am simply trying to create a window as a "listbox" and add elements to it. My end goal will be a listbox similar to the on here: http://msdn.microsoft.com/en-us/library/windows/desktop/hh298365%28v=vs.85%29.aspx
I start by create a window with a listbox like so:
hDlg = CreateWindowExA(
WS_EX_CLIENTEDGE,
"ListBox",
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
But from what I understand, hDlg now points to the entire window and not the listbox.
Ideally I want to be able to add items to the listbox similar to this:
int pos = (int)SendMessage(hwndList, LB_ADDSTRING, 0,
(LPARAM) "Test Item1");
However I cannot get the handle of the list in the same way as the tutorial because they use this line:
HWND hwndList = GetDlgItem(hDlg, IDC_LISTBOX_EXAMPLE);
but IDC_LISTBOX_EXAMPLE throws a compilation error because it isn't included anywhere. And for the life of me, I cannot google a correct result for the second parameter int nIDDlgItem.
Can someone please explain to me how I can find the value GetDlgItem() or otherwise find a handle to my listbox from CreateWindowExA()?

The problem is that you're using the predefined ListBox window class to create a top-level window.
hDlg = CreateWindowExA(WS_EX_CLIENTEDGE, "ListBox", ...
This is creating a listbox control. The second parameter to CreateWindowEx specifies the window class - this tells the system what type of window you want to create. Since you're passing "ListBox" for that value, it will be creating an instance of the ListBox class.
The trouble is that you're creating that window to be a top-level window. WS_OVERLAPPEDWINDOW is a window style used for top-level windows (i.e. the one titled "List Box Example" in the above screenshot). Child windows, such as the listbox control, need to have the WS_CHILD style set.
What you really need to do is use RegisterClass to register your own window class for the top-level window. You would use this class name when calling CreateWindowEx to create the main window, and then create the various controls as children of that window.

Related

WinAPI FindWindow

I am building a win32 application in visual studio 2012. I have 4 disabled buttons created with the following code:
HWND hWndButton=CreateWindowEx(NULL,
L"BUTTON",
L"APP1",
WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON|WS_DISABLED|BS_ICON,
40,
40,
180,
140,
hWnd,
(HMENU)IDC_BUTTON1,
GetModuleHandle(NULL),
NULL);
What I want to do is to enable the buttons as the application runs. I tried to use findwindow to find and enable the first button but it doesn't find it. My code is:
HWND hwB1 = FindWindow(L"BUTTON",L"APP1");
if (hwB1 !=0) MessageBox(NULL,L"FOUND",L"Button Found",MB_OK);
EnableWindow(hwB1,true);
Am I doing something wrong?
Thank you in advance.
FindWindow() finds top-level windows, not child windows.
If you really do want to look up a child window by name, you can use the FindWindowEx() function, but using the ID is usually more efficient.
The function that does this is GetDlgItem(). This looks up child windows by their ID, which you provide when you create them.
HWND hwB1 = GetDlgItem(hWnd, IDC_BUTTON1);
hWnd is the parent window, and IDC_BUTTON1 is the ID.
Another alternative is to simply store the window handle that's returned when you create the child window - hWndButton in your code example - and then you don't need to look it up at all.
Use FindWindowEx(),
HWND hwB1=FindWindowEx(hWnd/*Parent window*/,
hWndButton/*Child window*/,
"BUTTON"/*Class of the child window*/,
"APP1"/*Title of the child window*/);
This is a better method when the control ID is unknown.

Child Window Z-Order

I see in MSDN, it says:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
If the created window is a child window, its default position is at the bottom of the Z-order. If the created window is a top-level window, its default position is at the top of the Z-order (but beneath all topmost windows unless the created window is itself topmost).
However, another documentation says:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms632599(v=vs.85).aspx
When an application creates a window, the system puts it at the top of the z-order for windows of the same type
I tested it like this:
btn1 = ::CreateWindow(L"button", L"OK", WS_TABSTOP|BS_DEFPUSHBUTTON|WS_VISIBLE|WS_CHILD
, 10, 10, 50, 30, hWnd, (HMENU)51, hInst, NULL);
btn2 = ::CreateWindow(L"button", L"Cancel", WS_TABSTOP|WS_CHILD|BS_PUSHBUTTON|WS_VISIBLE
, 20, 20, 70, 30, hWnd, (HMENU)52, hInst, NULL);
I created two buttons in a window and they overlapped, I can see that the button created later is covering the first button created.
Is the first statement in MSDN contradictory to my testing?
The documentation is accurate. You are being tripped up by another problem, you allow the child windows to draw themselves across other child windows. So now the painting order matters.
You fix that by adding the WS_CLIPSIBLINGS style flag to your CreateWindowEx call. You'll now see that the OK button is on top. Fix:
btn1 = ::CreateWindow(L"button", L"OK",
WS_TABSTOP|BS_DEFPUSHBUTTON|WS_VISIBLE|WS_CHILD|WS_CLIPSIBLINGS,
10, 10, 50, 30, hWnd, (HMENU)51, hInst, NULL);
// etc, use it as well on other child windows
You should not rely to much on the how the child windows are displayed and which one is drawn last. If I run your sample code I get an OK button which is overlapped by the Cancel button. If I move the mouse over the buttons then the OK button comes into foreground and draws over the Cancel button.
I once had similar trouble with overlapping child controls. Then I found out that Microsoft says Overlapping Controls Are Not Supported by Windows.
BTW, if you really want to see the Z-order, then use GetTopWindow and GetNextWindow. Or the simpler way: run Microsoft Spy++.

property sheet in dialog using WINAPI example (without using MFC)

Can anyone point me at an WINAPI example of embedding a property sheet in a dialog box using WINAPI (not MFC)?
For the record, here are some of the things I learned while
investigating this question. I obtained most/all of this from
asking questions here or by searching the web. Thanks to all for
your help!
I have written a class to encapsulate everything I've discovered;
if interested in getting a copy send me an email mdorl#wisc.edu.
Pass the HWND of the parent window for your property sheet in
.hwndParent of the PROPSHEETHEADER when creating the property sheet
with the PropertySheet page function.
I used a picture control on the dialog as the parent of the property
sheet so that's the HWND I used as the hwndParent.
Change the style of the property sheet using the call back
pfnCallback field in the header, don't forget to include
PSH_USECALLBACK in dwFlags. See PropSheetProc Function in
the MSDN. In the PSCB_PRECREATE handler of the callback,
adjust the window style of the property sheet by removing
WS_POPUP and adding WS_CHILD. I did not want a border or title
bar on the property sheet so I also removed WS_CAPTION, WS_SYSEMNU,
& DS_MODALFRAME
LONG L = ((LPDLGTEMPLATE)lParam)->style;
L &= ~WS_POPUP;
L &= ~WS_CAPTION; // gets rid of title bar
L &= ~WS_SYSMENU;
L &= ~DS_MODALFRAME; // gets rid of border
L |= WS_CHILD; // 40000000
((LPDLGTEMPLATE)lParam)->style = L;
Using a child window instead of a popup window neatly solves
some problems such as moving the property sheet when the parent
moves, keeping the blue/gray state of the parent window properly
colored, and clipping issues. I.e. you don't have to worry about these things.
If you stopped here, you'd face another problem. The dialog support
code in WIN32 would get in a cpu loop trying to find the property sheet and
controls in its Windows. See
What is WS_EX_CONTROLPARENT and DS_CONTROL For?
for a pretty good explanation of
this problem. The solution is to add the WS_EX_CONTROLPARENT extended
style to the property sheet. You can find information on this
on the web by searching for WS_EX_CONTROLPARENT. What you won't find
caused me much grief. If you use a control on the dialog as the
parent, you must also set its WS_EX_CONTROLPARENT.
I do not know what the difference between WS_EX_CONTROLPARENT and DS_CONTROL is nor do I know if you could substitute DS_CONTROL for WS_EX_CONTROLPARENT. From the web I gather that DS_CONTROL has something to do with tabs. My test app works the same with respect to tabs with or without DS_CONTROL.
I took care of the WS_EX_CONTROLPARENT business after calling the
PropertySheet function to create the property sheet. I do not know
if you could do this in the call back routine or not. I suppose
you could use GetParent in the call back to get the HWND of the
control.
LONG S;
S = GetWindowLong (hwndPS, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS, GWL_EXSTYLE, S);
S = GetWindowLong (hwndPS_Area, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS_Area, GWL_EXSTYLE, S);
After creating the property sheet with the PropertySheet function,
you have to position it properly:
SetWindowPos(hwndPS, HWND_TOP, 2, 2, -1, -1,
SWP_NOSIZE | SWP_NOACTIVATE);
I allowed a couple of pixels for the border.
If you want to get rid of all the property sheet buttons and the space they take:
RECT rectWnd;
RECT rectButton;
GetWindowRect(hwndPS, &rectWnd);
HWND hWnd = ::GetDlgItem(hwndPS, IDOK);
if(!hWnd)
{
DebugBreak();
}
GetWindowRect(hWnd, &rectButton);
SetWindowPos (hwndPS, NULL, 0, 0,
rectWnd.right - rectWnd.left, rectButton.top - rectWnd.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
Individual buttons can be eliminated:
hwndOk = GetDlgItem (hwndPS,IDOK); // Hide the OK button
ShowWindow (hwndOk, SW_HIDE);
EnableWindow (hwndOk, FALSE);
You can get rid of the APPLY button using the PSH_NOAPPLYNOW
dwFlags in the PROPERTYSHEETHEADER when you create the property sheet.
The property sheet pages will NOT be created until the user first
activates them. I wanted them all to be created when the property
sheet was created.
unsigned int iP;
for (iP=0; iP<Header.nPages; iP++)
{
if (iP != Header.nStartPage)
SendMessage (hwndPS, PSM_SETCURSEL,iP,NULL);
}
SendMessage (hwndPS, PSM_SETCURSEL,Header.nStartPage,NULL);
There's a PROPSHEETPAGE flag that does the same thing (from the MSDN)
PSP_PREMATURE Version 4.71. Causes the page to be created when the
property sheet is created. If this flag is not specified, the page
will not be created until it is selected the first time.
But I was worried about the Version 4.71 business so I did it myself.
You can change the text on any of the buttons:
SetDlgItemText (hwndPS,ButtonID,Text);
where ButtonID is one of IDOK IDCANCEL IDHELP IDAPPLYNOW
IDAPPLYNOW is undefined on my system so
#define IDAPPLYNOW 0x3021
Here's how I intend to use my property sheet. I'm going to put all the initialization and
termination code in the dialog containing the property sheet and eliminate all the property sheet buttons. I can set all the initial values there and retrieve all the final values there when the user pushes some DO IT button. I notice that the act of setting, for example, a text box causes the containing page dialog to get a WM_COMMAND/EN_CHANGE. If you use this message to enable the APPLY button, you may need to disable the change flag for the page after setting any list boxes. I solved this problem by clearing all the changed flags after setting the initial value. (I suspect the property sheet clears the changed flag after all the INITDIALOG messages have been sent. In any case APPLY is not enabled if the text boxes are set in the page INITDIALOG handlers.) If I find an error I will select that property sheet page and put some red text in the parent dialog describing the error. The only thing the property sheet dialog procedures need to do is manipulate their controls.
One of the things that puzzles me is how to determine if all pages have returned PSNRET_NOERROR from their WS_NOTIFY/PSN_APPLY messages. I've had some success counting the
number of successive PSNRET_NOERROR replies setting the count to zero when a PSNRET_INVALID is reported or a PSN_KILLACTIVE is received. If the count reaches the number of pages in the property sheet header, I assume all pages have returned PSNRET_NOERROR. But I worry about things like disabled and invisible pages.

Using CreateWindow and adding buttons and such to it? How?

I have realised after so long of coding DirectX/OpenGL applications I don't have the faintest of ideas how to create windows with basic form objects like text boxes, labels, command buttons etc.
I can create a window using the CreateWindow function just fine, how can I add buttons, command prompts and other form objects to it?
Look at Create Window Help. Once you create your main window you can create child windows by providing the parent HWND to the function. For standard controls you use one of the class names defined at the button, like EDIT for an edit box and BUTTON for a button.
As an example: CreateWindow(L"BUTTON", L"Button", BS_TEXT | WS_CHILD | WS_VISIBLE, 40, 40, 100, 40, hMainWnd, (HMENU)ID_MYBUTTON, hInstance, NULL);
Or you could just create a dialog box instead and edit it with Visual Studio's resource editor (if you have full VS that is).

Why does clicking a child window not always bring the application to the foreground?

When an application is behind another applications and
I click on my application's taskbar icon, I expect the entire application to
come to the top of the z-order, even if an app-modal, WS_POPUP dialog box is
open.
However, some of the time, for some of my (and others') dialog boxes, only the dialog box comes to the front; the rest of the application stays behind.
I've looked at Spy++ and for the ones that work correctly, I can see
WM_WINDOWPOSCHANGING being sent to the dialog's parent. For the ones that
leave the rest of the application behind, WM_WINDOWPOSCHANGING is not being
sent to the dialog's parent.
I have an example where one dialog usually brings the whole app with it and the other does not. Both the working dialog box and the non-working dialog box have the same window style, substyle, parent, owner, ontogeny.
In short, both are WS_POPUPWINDOW windows created with DialogBoxParam(),
having passed in identical HWNDs as the third argument.
Has anyone else noticed this behavioral oddity in Windows programs? What messages does the TaskBar send to the application when I click its button? Who's responsibility is it to ensure that all of the application's windows come to the foreground?
In my case the base parentage is an MDI frame...does that factor in somehow?
I know this is very old now, but I just stumbled across it, and I know the answer.
In the applications you've seen (and written) where bringing the dialog box to the foreground did not bring the main window up along with it, the developer has simply neglected to specify the owner of the dialog box.
This applies to both modal windows, like dialog boxes and message boxes, as well as to modeless windows. Setting the owner of a modeless popup also keeps the popup above its owner at all times.
In the Win32 API, the functions to bring up a dialog box or a message box take the owner window as a parameter:
INT_PTR DialogBox(
HINSTANCE hInstance,
LPCTSTR lpTemplate,
HWND hWndParent, /* this is the owner */
DLGPROC lpDialogFunc
);
int MessageBox(
HWND hWnd, /* this is the owner */
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType
);
Similary, in .NET WinForms, the owner can be specified:
public DialogResult ShowDialog(
IWin32Window owner
)
public static DialogResult Show(
IWin32Window owner,
string text
) /* ...and other overloads that include this first parameter */
Additionally, in WinForms, it's easy to set the owner of a modeless window:
public void Show(
IWin32Window owner,
)
or, equivalently:
form.Owner = this;
form.Show();
In straight WinAPI code, the owner of a modeless window can be set when the window is created:
HWND CreateWindow(
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent, /* this is the owner if dwStyle does not contain WS_CHILD */
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
or afterwards:
SetWindowLong(hWndPopup, GWL_HWNDPARENT, (LONG)hWndOwner);
or (64-bit compatible)
SetWindowLongPtr(hWndPopup, GWLP_HWNDPARENT, (LONG_PTR)hWndOwner);
Note that MSDN has the following to say about SetWindowLong[Ptr]:
Do not call SetWindowLongPtr with the GWLP_HWNDPARENT index to change the parent of a child window. Instead, use the SetParent function.
This is somewhat misleading, as it seems to imply that the last two snippets above are wrong. This isn't so. Calling SetParent will turn the intended popup into a child of the parent window (setting its WS_CHILD bit), rather than making it an owned window. The code above is the correct way to make an existing popup an owned window.
When you click on the taskbar icon Windows will send a WM_ACTIVATE message to your application.
Are you sure your code is passing the WM_ACTIVATE message to the DefWindowProc window procedure for processing?
Is the dialog's parent window set correctly?
After I posted this, I started my own Windows Forms application and reproduced the problem you describe. I have two dialogs, one works correctly the other does not and I can't see any immediate reason is to why they behave differently. I'll update this post if I find out.
Raymond Chen where are you!

Resources