0

Can I create a button without a parent in WINAPI?

I tried doing:

CreateWindowEx(0, "Button", "BTN", WS_POPUP | BS_PUSHBUTTON, 0, 0, 15, 15, nullptr, nullptr, nullptr, nullptr);

then setting the parent to a specified window later on and also showing the button using ShowWindow. This indeed created a fine looking button.

However, the button has no ID and cannot be Identified in WM_COMMAND because the ID is 0.. If two buttons were parentless, there'd be no way to tell them apart. Now if I give it an ID through the HMENU parameter:

CreateWindowEx(0, "Button", "BTN", WS_POPUP | BS_PUSHBUTTON, 0, 0, 15, 15, nullptr, 15, nullptr, nullptr);

GetLastError() prints "Invalid Menu Handle" and the button will not be created.

If I give it no parent and WS_CHILD, it will say cannot create a top level child window which is understandable.

So what I did was I set the Parent to GetDesktopWindow() and give the button an ID. That works but the button isn't parentless..

So is there a way to give a button an ID (So as to identify it in WM_COMMAND) and at the same time, have its parent NULL so that I can set the parent later? How does Windows Forms do it? The buttons can be parentless until you do Form.add(ButtonName);

Can the same effect be achieved in WINAPI?

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • You could distinguish between buttons via `WM_COMMAND`'s `lParam`, which is the control's `HWND`. (It still might not be a good idea, though. http://blogs.msdn.com/b/oldnewthing/archive/2012/06/19/10321772.aspx) – jamesdlin Mar 09 '13 at 07:02
  • 1
    Calling Windows APIs, I'm not sure passing `nullptr` instead if `NULL`. – masoud Mar 09 '13 at 07:14
  • I solved it. I had to pass HWND_MESSAGE as the Parent Parameter. When you call SetParent, that parameter gets changed to the Parent's handle and all is well. – Brandon Mar 09 '13 at 07:43
  • By the way, @MM. it is perfectly valid to call Windows API functions using `nullptr`. Remember that `NULL` is a preprocessor macro that, in C++, expands to the integer literal `0`. The `nullptr` keyword will do the exact same thing. More information on the behavior of `nullptr` is [here](http://stackoverflow.com/questions/1282295/what-exactly-is-nullptr). I highly recommend using `nullptr` instead of `NULL` in all new code. – Cody Gray - on strike Mar 10 '13 at 13:11

2 Answers2

4

This is appears on its face to be a very silly sort of question.

Button controls are by definition child controls, so the call to the CreateWindowEx function you use to create the button should also be specifying the WS_CHILD style.

Of course, as you mention, you cannot create a child control with no parent; you'll get an error. There is no such thing as a top-level child window.

So then, the answer to the initial question

Can I create a button without a parent in WINAPI?

is clearly no. Buttons are child controls, and all child controls must have a parent.

Just because Windows let you get away with specifying the WS_POPUP flag when you create a button control doesn't mean that it's a valid combination.

I strongly recommend re-reading the documentation for the CreateWindowEx function. In particular, note that the hMenu parameter is overloaded with respect to its meaning. If you are creating an overlapped or pop-up window (WS_OVERLAPPED or WS_POPUP), it specifies a handle to a menu. If you're creating a child window (WS_CHILD), it specifies the identifier of the child window. The fact that the same parameter is used for both things, depending on the style of the window, should tell you something.

How does Windows Forms do it? The buttons can be parentless until you do Form.add(ButtonName);

They most certainly cannot. The button controls are not created until you add them to a form or other parent control. The System.Windows.Forms.Button class constructor does not create a Win32 window. It just holds a collection of necessary styles used to create the underlying Win32 window when appropriate.

You could, of course, do the same thing by writing a C++ Button class. A simple implementation would just have member variables corresponding to the parameters of CreateWindowEx and a Create member function that would actually call CreateWindowEx to create the Win32 window once all of the members had been set. The Create method could throw an exception if one of the necessary members had not yet been set to a valid value.

I solved it. I had to pass HWND_MESSAGE as the Parent Parameter. When you call SetParent, that parameter gets changed to the Parent's handle and all is well.

No, this really is not a "solution" to the problem. As kero points out, you've simply set the button control's parent to the message-only window. Again, this might appear to work, but it's a rather strange thing to do and I hardly recommend it as a solution.

If you really want to hack it, I recommend creating your own hidden top-level window to use as a parent for your "unparented" child controls. Then you could use the same trick of calling SetParent to reparent them.

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 1
    I accepted this as the answer because it explains a lot and I ended up following your advice. I made a button class with a variable to track whether or not the button has been added to a form (another class I created). Works fine. It does not create a button until a valid parent is supplied. – Brandon Mar 10 '13 at 16:13
1

I solved it. I had to pass HWND_MESSAGE as the Parent Parameter. When you call SetParent, that parameter gets changed to the Parent's handle and all is well.

No, you didn't get "Parent-Less Button": the parent window of your message-only button is the "main" message-only window (class "Message").

masoud
  • 55,379
  • 16
  • 141
  • 208
kero
  • 678
  • 3
  • 10
  • This is entirely correct, but it's also not an answer to the question. Not sure whether to upvote or downvote... – Cody Gray - on strike Mar 10 '13 at 12:25
  • @Cody Gray, to your comment above about "rather strange thing": I think it isn't dangerous :) Any window had once been messages-only: at the time of its creation. // HCBT_CREATEWND + GetAncestor(wParam,GA_PARENT) – kero Mar 10 '13 at 22:32
  • It isn't the *characteristics* of the window that you're parenting to that I find strange. You're right, of course, that all windows are ultimately processing messages. The issue is with parenting a child to a window that *doesn't belong to you*. The root message-only window is a part of Windows, not part of your application. It might work okay to create a child of someone else's parent window (just like you could create a child window of the Notepad window), but it's not behavior I would recommend. – Cody Gray - on strike Mar 11 '13 at 11:40