I am using the new Common Item Dialog API to show file open/save dialogs. How do i get these dialogs to be centered on the owner window ? In the old API, this could be done by handling WM_INITDIALOG in the Callback hook for GetOpenFileName, but the IFileDialog does not seem to provide any way to center the dialogs.
Related
I have an application with a tray menu and I'm trying to automate some tests that involve the tray menu. Basically I need obtain the tray menu's items and do stuff with them.
However, I've only been able to find ways to programmatically obtain menu items for within the application. But my automation tests are going to be an external application, so that doesn't help me.
How can I obtain an external application's tray menu items programmatically?
There are ways to enumerate/access the tray icons themselves (usually involving hooking into the notification tray itself, or using UI Automation), but there is no way to access a popup menu that appears when a tray icon is clicked on. The reason is because the icon's owning application receives a message when the click occurs and then acts accordingly, which usually involves displaying its own popup menu. There is no menu associated with the icon itself.
For what you are attempting, you would have to enumerate the icons and figure out which icon belongs to the app you are interested in (not a trivial task on its own), then simulate a click on the icon so the app displays its popup menu. See the following question for some details:
Finding and simulating a click on a system tray icon?
Interacting with the popup menu once it is displayed will be more difficult. You won't have access to the menu itself. You will likely have to resort to just issuing mouse events via mouse_event() or SendInput() to move the mouse cursor over the menu and click its items (assuming they appear in predictable locations relative to the icon).
If you can obtain the icon's HWND+ID or GUID (by hooking the notification tray itself), you can use Shell_NotifyIconGetRect() to get the icon's coordinates, at least.
How can I obtain an external application's tray menu items programmatically?
You cannot. There is no public API that provides access to notification icons.
Depending on what sort of assumptions you find acceptable, you can programmatically interact with the taskbar button's menu once it's visible. The image below shows the Inspect SDK tool reporting properties on the OneNote clipping tool button's menu. (And the menu items say they support the UIA Invoke Patten, so they should be programmatically invokable by UIA client code.)
If you want to invoke your tray button's menu items, you might consider the following steps using UIA. You may feel the assumptions that I make here are unacceptable for your situation.
Find the element with a class name "NotifyIconOverflowWindow", that's a direct child of the root menu. I'm assuming the button is in the overflow area.
Enumerate the children of the overflow element, looking for a button with the name of your button. This assumes the UI language is known and accounted for.
Get the bounding rect of the button and simulate a mouse right-click on the button. The click simulation is necessary because I'll bet the UI doesn't support IUIAutomationElement3::ShowContextMenu(), (but you could always try it).
Once the context menu's up, find the element with a ControlType a Menu, a Name of "Context", that's a direct child of the root element.
Once you have the menu, enumerate the child elements in the menu to find the items, and do what you want with them. Eg get the menu item's Invoke pattern and invoke it.
We have a set of modeless dialogs that are children of a modal dialog. Only one of the modeless dialogs appears at a time; the others are hidden. This is controlled by elements in the modal dialog. If you think of a Windows property sheet you'll have the idea.
It all works fine except for the Tab key and the Alt+char hotkeys. They don't do what they're supposed to do, i.e., navigate the controls in the active modeless dialog. They just beep.
In a normal scenario, i.e., a modeless dialog owned by the application window, this is handled by calling IsDialogMessage() in the application's message loop. We can't do that because it's not our message loop--Windows is running the message loop to service the modal dialog, and all we get are messages sent to the dialog proc.
We're trying to think of ways to handle this without having to resort to doing all of the navigation ourselves via WM_GETDLGCODE.
Any ideas? Straight C++ Win32 API, none of that newfangled stuff the kids are all using these days.
TIA
ADDITIONAL INFO: Further investigation reveals that Windows is applying the navigation keys to the host modal dialog, not to the child modeless dialogs. We need them to go to the modeless dialogs.
The answer, found by a colleague, turns out to be adding the DS_CONTROL style to the modeless dialogs.
Raymond Chen discusses DS_CONTROL here.
It's amazing that you can work with Windows every day for 20 years and still run across stuff that you've absolutely never heard of.
My MFC application has multiple top level (parented to the desktop) windows, any one of which can host an external application which can launch a modal dialog. Is there a way for one the other top level windows to get a notification when any of the others becomes modal?
My specific problem is that one of the my windows is hosting an embedded PDF viewer and when the user clicks print, only the window hosting the viewer is locked, not the others.
When a modal dialog is shown EnableWindow(FALSE) is called for the parent. It is deactivated now and will not accept any mouse input. Also it will not receive the keyboard focus.
When EnableWindow(FALSE) is called WM_ENABLE with wParam==FALSE is sent to the window.
When your parent receives this message you can call EnableWindow(FALSE) for all your other windows too. Recursion might be a problem here, but you can use a private window message or flags to prevent this.
Before the modal dialog closes EnableWndow(TRUE) is called again and WM_ENABLE with wParam==TRUE is sent again.
In my Win32 app, I had a modal dialog that displays settings that I had to add more settings to. In order to fit the new settings, I dropped a TabCtrl in the dialog and implemented two modeless dialogs. The UI is working switching between them but the modeless dialogs don't respond to the keyboard. In a regular app, IsDialogMessage (hWndCurModelessDialog) would be called. How would I do this for my Modal dialog containing a modeless dialog?
You dont. The modal dialog box function calls IsDialogMessage from its own message loop automatically.
The modeless dialogs are the 'pages' ? Make sure they are parented to the main dialog (rather than the tab control) and have the DS_CONTROL style. This style allows IsDialogMessage to recurse into a child dialogs controls when tabbing.
Even when BorderStyle is set to 0, it is possible to force a window to show up on the taskbar either by turning on the ShowInTaskbar property or by using the windows api directly: SetWindowLong Me.hwnd, GWL_EXSTYLE, GetWindowLong(Me.hwnd, Win.GWL_EXSTYLE) Or Win.WS_EX_APPWINDOW. However, such taskbar entries lack a right-click menu in their taskbar entry. Right-clicking them does nothing instead of bringing up a context menu. Is there a way, to attach a standard or custom handler to it?
Without a hack, I think you're going to be stuck here, I'm sorry to say. When you set the VB6 borderless properties, you inherently disable the control menu. The control menu (typically activated by right-clicking the title bar of a window or left-clicking the icon in the upper left) is what's displayed when you right-click a window in the task bar.
Now, if you're in the mood to hack, you might be able to "simulate" the behavior in such a way that the user doesn't know the difference. I got the idea from this message thread on usenet.
Basically, it sounds like you may be able to hack it by using two forms. One form is minimized right away, and becomes your "stub" in the task bar. The other form is the one you're currently designing (which we'll call the "main" form). The stub form is what actually loads and displays your main form.
The stub form isn't borderless, and must not deactivate the control menu. It is positioned off screen and at the smallest possible size. You'll respond to its form-level events, and then use those to communicate the appropriate behaviors to the borderless form.
That's the general gist of the hack. If I wasn't at work right now, I'd whip up a simple VB6 project and see if I could get it to work for you.