Parent-Less Button - c++

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?

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.

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").

Related

WinEvent Telling the different objects apart?

I’m listening to global win events for cases such as object destroyed, hidden, shown, created etc…
However I’d like to do certain things based on what that handle (HWND) is. For example did a button just get hidden or destroyed or did a window? Was it a child window that got hidden or parent? The last question regards to seemingly getting an event raised for every object in the parent window when parent window closes which is too much noise. Trying to make sense of what raised the event.
https://learn.microsoft.com/en-us/windows/win32/winauto/what-are-winevents
Windows offers a rich set of functions for interrogating an HWND. For example (error checking omitted for brevity):
did a button just get hidden or destroyed or did a window?
TCHAR classname [32];
GetClassName (hWnd, classname, 32);
if (_tcscmp (classname, __T ("BUTTON")) == 0)
...
Was it a child window that got hidden or parent?
LONG dwStyle = GetWindowLong (hWnd, GWL_STYLE);
if (dwStyle & WS_CHILD)
...
an event [is] raised for every object in the parent window when parent window closes which is too much noise.
I think you are stuck with this. When a parent window is destroyed, it automatically destroys all its children, and each of those will send the corresponding event(s) as a result.

how to know if my control is visible or not

I have a dialog containing controls (edits, combobox, etc...) some are visible and some or not.
What in a function, I want is looping on all controls and get the last shown control.
I want to add that when creating the dialog, I set the visibility to some controls as SW_HIDE and others to SW_SHOW.
What I did is using the ::IsWindowVisible, but returns false even if I set the visibility to SW_SHOW.
NB: the function is called just after the creation of the dialog, the dialog is not yet visible.
The control is not visible at the time you call the function, because the dialog that contains it is not visible. As per the documentation for the IsWindowVisible function:
If the specified window, its parent window, its parent's parent window, and so forth, have the WS_VISIBLE style, the return value is nonzero. Otherwise, the return value is zero.
This not only explains the behavior that you're seeing, but also suggests a possible solution. If you want to know whether a particular window has the WS_VISIBLE style bit set (and don't care about any of its ancestors), then just retrieve that window's styles and test for the presence of the WS_VISIBLE bit directly. For example:
LONG_PTR wndStyles = GetWindowLongPtr(hwndCtrl, GWL_STYLE);
bool isVisible = (wndStyles & WS_VISIBLE) == WS_VISIBLE;
That said, I don't really understand why you would need to do this. If your dialog-initialization code sets the visibility of controls, then you know what their visibility is. You don't need to retrieve it dynamically at run-time. In other words, this sounds like an X-Y problem. I suspect there is a better way to write your code so that this whole issue is a non-issue.

How to make a modeless dialog always on top within the app

I have a modeless popup dialog in my app. I want to make it topmost only within the app it belongs to, not always topmost on the desktop.
I have tried to set the first parameter to wndTopMost, but this way the dialog will remain on top on the desktop, which is very bad user experience.
I have also tried wndNoTopMost with SWP_NOZORDER parameter, but this only put the dialog in front when its displayed. If I move another dialog/window to the dialog location, the dialog will be buried under the new dialog/window.
I am currently using the SetWindowPos in the OnInitDialog();
SetWindowPos(&wndNoTopMost
, myRect.left
, myRect.top
, myRect.Width()
, myRect.Height()
, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOZORDER
);
Yes HWND_TOPMOST is a very bad user experience, so I will commend you for not taking the easy way out and trying to flip this switch.
The key to getting a dialog to appear on top of other windows is setting its owner. (Note that in Win32, an owner is distinct from a parent window, although the terms are often confusing.) All dialogs have an owner, and dialogs always stay on top of their owner.
So when you create the modeless dialog (e.g. using the CreateDialog function), make sure to specify the handle to your application's main window as its owner. Confusingly, the parameter is named hwndParent, but it actually specifies the owner window.

C++/Win32 API - SetFocus to button does not work

HWND button = CreateWindowEx(0, "BUTTON", ...);
SetFocus(button); // Button no get focus! :(
Also, I have other controls on my form that I am able to SetFocus() to.
Thanks, Martin
It has been FOREVER since I've had to do this, but...
Were this a dialog, I would tell you to send a WM_NEXTDLGCTL via PostMessage(). The default dialog item message handler would take care of the rest for you setting keyboard focus and selection activation. However, this is a different case if I read this correctly. You're creating both parent and child windows raw on the fly. If this is the case, SetFocus() to the parent window, and handle WM_SETFOCUS on the parent window by bringing it to top, then setting focus on the child window. WM_SETFOCUS, and WM_KILLFOCUS were designed to allow you to switch the 'activated' state of your controls, and most handle it for you (unless your window is an owner draw control or some such). But in a raw window, when your base parent window is sent the focus, you need to appropriately ensure the proper child has it if you're hosting any (think of it as managing your own 'dialog'). Again, normally this is done by the default dialog procedure for you if this were a dialog, but being raw windows you're kind of stuck managing it all yourself.
Though I can't imagine how, I hope that helped somewhat.
SetFocus is a function, not a procedure. Call it as a function and check its returned value. Either the retuned value is null because you made an error in the CreateWindowEx() call and "button" isn't a valid handle or it's a window not associated with your thread's message queue, or the return value is not null (it's now the prior focused window's handle) and you do have the focus (but are somehow failing to detect it).
Try setting the WS_TABSTOP style on the button.
If you create that button in respond of the WM_INITDIALOG message you should return FALSE to prevent dialog box procedure to change the focus.

Custom tooltip with a WS_POPUP dialog

I want to create custom tooltips where I can put any kind of controls. I have derived from CDialog and used the WS_POPUP | WS_BORDER styles. I also add the CS_DROPSHADOW style in the OnInitDialog to get the tooltip shadow.
Then I manage myself the WM_MOUSEHOVER and WM_MOUSELEAVE events to show/hide the tooltips.
I display the tooltip using SetWindowPos and SWP_NOACTIVATE to prevent the parent from becoming inactive and the new dialog from becoming active. But anyway, when I create the dialog using CDialog::Create method...the main window becomes inactive...what makes a very bad effect.
So my custion is how can I create a CDialog with the WS_POPUP style without my main window (or the parent window of the dialog) becomening inactive when the new dialog shows up???
Thanks for helping!
Edited: I do not use the WS_VISIBLE style to create the dialog...this this the resource:
IDD_LABEL_TOOLTIP_DLG DIALOGEX 0, 0, 100, 9
STYLE DS_SETFONT | WS_POPUP | WS_BORDER
FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "##################",IDC_TOOLTIP_LBL_TEXT,0,0,99,9
END
The code that display the tooltip is something like that:
if(!pTooltipDlg)
{
pTooltipDlg = new MyCustomTooltipDlg();
pTooltipDlg->Create( MyCustomTooltipDlg::IDD, this);
}
pTooltipDlg->ShowWindow(SW_SHOWNOACTIVATE);
The first time (ie when the create is being call) the main windows lose the focus...the rest of them this ugly effect is not happening...so I am sure is because of the Create.
When you create your window, don't set the WS_VISIBLE flag on it. Then you can use ShowWindow with SW_SHOWNA or SW_SHOWNOACTIVATE to make the dialog visible.
Are you calling CDialog::Create() with WS_VISIBLE set? It might be that even just calling Create() is enough to take focus from the parent. It might also be worth overriding WM_SETFOCUS on your tooltip class and not calling the base class to make it impossible for the focus to change windows.
First off, consider using a CWnd rather than a CDialog. This gives you much finer control. And you are not really using any features of the CDialog anyway other than the dialog template; it is not too difficult to dynamically create your controls.
You may also want to consider, in the message handlers, handling OnShowWindow and ensure any show commands are changed to SW_SHOWNA as in Mark Ransom's comment.
Additionally, as a tooltip, it should probably have a NULL parent window.
Ok. I finally got it! I just had to return FALSE in the OnInitDialog method to avoid the dialog from being activated.
Thanks to all of you!