Should Form.ShowDialog(IWin32Window) work with any window handle? - c#

When using System.Windows.Forms.ShowDialog(IWin32Window), should I be able to pass in an IWin32Window representing any window handle and have it be modal with respect to that window?
As part of an Internet Explorer 7 extension I'm trying to open a window modal with respect to an Internet Explorer tab. It's not the currently selected tab, but I can get the hwnd of the tab OK. However, when I pass this to ShowDialog my Form is shown, but it's not modal with respect to anything: I can still do things in Internet Explorer, including in the tab that's supposed to be the owner. My form is shown floating above the Internet Explorer windows and it stays on top, so it's not like it's just opened as a normal form, but it's not correctly modal.
Using Spy++, I can find my form and it's owner handle is correctly set.
Does this mean that something has gone wrong, or I'm doing something wrong? How do I make my form correctly modal?
FYI, I'm using this wrapper class to create an IWin32Window from a hwnd (thanks Ryan!):
/// <summary>
/// Wrapper class so that we can return an IWin32Window given a hwnd
/// </summary>
public class WindowWrapper : System.Windows.Forms.IWin32Window
{
public WindowWrapper(IntPtr handle)
{
_hwnd = handle;
}
public IntPtr Handle
{
get { return _hwnd; }
}
private IntPtr _hwnd;
}
UPDATE: Using Internet Explorer 7 & .NET 2.0
UPDATE: Playing around some more with Spy++ and the handles it exposes, I find that if I use a different hwnd then I can make my window modal to the tab:
I was using the tab's hwnd as suggested by the IWebBrowser2.HWND doc, which in Spy++ appears as class TabWindowClass. It has a child of class Shell DocObject View, which has a child of Internet_Explorer_Server. If I use the hwnd of the Internet Explorer_Server then it works correctly, for example, when I click with the mouse on other tabs, Internet Explorer reacts normally. When I click with the mouse on the tab of interest, it plays the windows d'oh sound and doesn't do anything.
I don't yet know how to programatically get the Internet_Explorer_Server hwnd, but it should be possible.
Also, for what it's worth, while playing with other window handles I was generally able to make my form modal to other applications and dialogs. So I guess the answer to my question is 'many but not all handles'... possibly it depends on the application?
UPDATE: Another side-note: The original reason I wanted to make my form modal to the tab instead of the whole window is that when opening a MessageBox from my form, passing the form as owner, the MessageBox would not always open on top of my form. If a new Internet Explorer tab had just been opened but wasn't active then the MessageBox would be hidden and that tab would start flashing. However, since Internet Explorer was disabled with my form opened modal it wasn't possible to switch to that tab, so Internet Explorer would be frozen. I thought that opening my form modal to the tab would solve this, but I've found another solution is to avoid using MessageBox: if I use a second form and ShowDialog(this) from my first form then the second form correctly opens to the front. So it seems that Form.ShowDialog() works better than MessageBox.Show() in some cases. More discussion in Problems with modal dialogs and messageboxes.

ShowDialog() does two important things. It starts pumping a message loop so it acts modally to the calling code. And it disables any other windows in the application with a EnableWindow(false) API call. The latter is what is not happening in your case. Not entirely surprising, considering that the window that needs to be disabled is not a WF window.
You may need to call EnableWindow() yourself. Be sure to re-enable it in before the dialog closes or Windows will go hunting for another app's window to give the focus to.

Your code is correct. The problem you are likely running into though is that IE has a threading model related to its tabs. I don't know the exact details but the short version is that each tab can and likely is running on a different thread than other tabs.
The Modal'ness of a dialog is specific to the thread where the dialog is running. UI on other threads will be unaffected by a model dialog on another thread. It's entirely possible you are able to access tabs which are running on a different thread for this reason.

Here's a more concise version of Ryan/Rory's WindowWrapper code:
internal class WindowWrapper : IWin32Window
{
public IntPtr Handle { get; private set; }
public WindowWrapper(IntPtr hwnd) { Handle = hwnd; }
}

I have never tried this from an IE extension, but I have a hunch that IE may not "respect" a Win32-style modal window the same way it does a modal window raised from Javascript using window.open().
Have you tested this code against something other than IE, just to confirm it works the way it should for other applications?

here is a build in solution in .NET:
public static NativeWindow FromHandle(IntPtr handle)

Related

Why can't show FolderBrowserDialog twice in Window constructor?

Out of curiosity, I wonder why I can't show two different instances of FolderBrowserDialog one after the other in the constructor of a Window, but can do it in the Window's Loaded event.
The Example 1 just shows the first dialog (fbd1), and doesn't show the next one.
The Example 2 shows the two dialogs.
Example 1 :
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
using (var fbd1 = new FolderBrowserDialog()) {
fbd1.ShowDialog();
}
using (var fbd2 = new FolderBrowserDialog()) {
fbd2.ShowDialog();
}
}
}
Example 2 :
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
using (var fbd1 = new FolderBrowserDialog()) {
fbd1.ShowDialog();
}
using (var fbd2 = new FolderBrowserDialog()) {
fbd2.ShowDialog();
}
}
}
By the way, I've also tested with WinForms, and this is almost the same.
It doesn't work in the Form's constructor and in the Form's Load event, but works in the Shown event.
The answer you like is not in fact the correct answer, it actually does activate the second dialog. Activation state and Z-order are distinct windows properties. You just can't see the dialog because you lost the foreground. One you can only ever keep when you have a window that can stay in the foreground.
A program gets ~6 seconds to steal the foreground with its own window after it starts up. That timeout is easy to see, Windows displays the Cursors.AppStarting cursor (small arrow with hourglass). That worked to get the 1st dialog into the foreground. What happens next is however doomed to go wrong. When the user closes the dialog then your app has no window left that can be moved into the foreground. Windows now goes hunting for another window to put in the foreground, inevitably one that's owned by another process. Pretty likely to be the VS main window when you debug for example. And the 6 seconds has expired. The 2nd dialog will show up and get activated but of course it is overlapped by that window.
Cold hard fact is that a dialog must always have an owner. FolderBrowserDialog is a bit too forgiving about that, providing you with a ShowDialog() overload without an owner argument. Very convenient, not always correct. It uses GetActiveWindow() under the hood to find an owner. If there isn't one then the desktop window becomes the owner, trouble ahead,
otherwise without throwing an exception.
As Reza Aghaei said in his 2nd comment :
When you close the first dialog, the second one appears, but since
your Form is not visible at the moment and is not visible in task-bar,
it doesn't activate the second dialog, while it's open behind other
windows. Just press Alt+Tab to see open windows and you will see the
second dialog too. But when your Form is visible (for example when run
code in Shown) you will not have this issue.
This is the answer to my curiosity.

How to keep window visible at all times, but not force it to be on top

I'm creating a "desktop gadget" of sorts, I've disabled manual minimizing of the window, but now there is another problem: the system can still hide the window if the user presses Windows+D, for example.
When hidden that way, no usual minimize/resize/visibility events are fired.
I want to do something almost like TopMost, but without forcing the window order.
Maybe it's possible to install a global shortcut event using win32 API, and briefly set TopMost to true, but that sounds very hackish.
I found one solution, but it does not seem to work on Windows 10: Keeping window visible through "Show Desktop"/Win+D
The other common option, which would be writing an actual desktop gadget, is not possible on Windows 10, given their deprecation.
Are there any other methods to keep a window visible (but not on top of the screen) at all moments?
This function is working for me:
BOOL FixShowDesktop(HWND hWnd)
{
HWND hWndTmp = FindWindowEx(NULL, NULL, L"Progman", NULL);
if (hWndTmp)
{
hWndTmp = FindWindowEx(hWndTmp, NULL, L"SHELLDLL_DefView", NULL);
if (hWndTmp)
{
SetWindowLongPtr(hWnd, -8, (LONG_PTR)hWndTmp);
return TRUE;
}
}
return FALSE;
}
Note, this code is a bit better then from Keeping window visible through "Show Desktop"/Win+D because the window can be overflowed by other windows (like any other window). Using SetParent places window under all other windows.

How to hide/show the tool window but not close it

I have a VS2010 Isolated Shell application and I have a tool window as below
public class MyWindow : ToolWindowPane
And this tool window supports multiple instance
[ProvideToolWindow(typeof (MyWindow ),
MultiInstances = true,
Style = VsDockStyle.MDI,
Transient = true)]
public sealed class MyVsxPackage : Package
Then in my application I can open more than one tool window (each tool window has its own window Id), but when I close one of them I just want to hide it, and next time when I reopen the window the settings on the window should keep the same before it was closed, how to handle this?
In another words, my question is: is there any way that prevent the multiple instance tool window from being closed by click the 'x' button (just hide it)?
Thanks in advance!
I supposed you have a list of toolbars with toolbars created in your App.
You must have a method in your toolbar that hides/show it. (If you don't really close toolbars, don't call close method).
When you "close" a toolbar call hide method, when reopen call show. (When close App, call close).
Hope this help you.

How to make Internet Explorer ActiveX dialog visible

I have an ActiveX control written in C# which operates a scanner from the browser using WIA. Everything works fine except the WIA CommonDialog pops under the browser window. How can I get it to show up on top of the browser?
wiaDialog = new WIA.CommonDialog();
wiaImage = wiaDialog.ShowAcquireImage(WiaDeviceType.ScannerDeviceType, WiaImageIntent.UnspecifiedIntent, WiaImageBias.MaximizeQuality, wiaFormatJPEG, false, false, false);
[Edit]
Thanks very much to Noseratio for putting me onto the right track. The suggestion to use BringWindowToTop invoked via a timer before popping up the dialog does not quite work. Instead the function to use is SetForegroundWindow. The code is as follows (invoked from a System.Timer.Timer prior to opening the scan dialog):
public static void scanDialogToTop(Object caller, EventArgs theArgs) {
scanner.theTimer.Stop();
foreach (Process p in Process.GetProcesses()) {
if (p.MainWindowTitle.StartsWith("Scan using")) {
SetForegroundWindow(p.MainWindowHandle);
break;
}
}
}
See this article for a more complete discussion.
It does not look like you can specify a parent window for ShowAcquireImage. If the caption of the popup window is static, you could use FindWindow to find the popup's handle. If ShowAcquireImage is a blocking call (doesn't return until the popup window is closed), before calling it you'd need to setup a timer and call FindWindow upon a timer event. I also suspect the WIA popup is created on a different thread (you could check that with Spy++). If that's the case, you could use the following hack to give the WIA popup window focus. Otherwise you just do BringWindowToTop.

WPF owner window on top of child window

Is it possible for Owner window in WPF be on top of Child window when you click on it while Owner window is below Child window?
here is example how I call child window:
Window2 window = new Window2();
window.Owner = this;
window.Show();
Parent/Owner window will always be under child window.
To get the behavior you want, you don't want to set the Owner on either window.
You, of course will have to handle the logic yourself when closing either of the windows to close your imaginary "child" window.
There may be some other logic you'll have to implement related to minimizing, maximizing, etc.
Many of the answers on this page involve nulling-out the Window.Owner property for some or all of the (non-MainWindow) windows in your System.Windows.Application. While this is a simple and easy fix that does indeed, in isolation, fix the issue of Window overlap, unfortunately it also inhibits a lot of useful application-wide functionality that WPF seems otherwise eager to provide in the areas of:
Application acti­vation/deactivation (via mouse-click, desktop Alt-Tab switching, etc...),
correct observance of the Application.​ShutdownMode property, and generally,
orderly cleanup, resource disposal, and exit of your Application upon shutdown.
It is possible fix the Window overlap issue while still preserving these system-wide WPF features by instead designating a special invisible window instance as your Application.MainWindow.
Modify your application so that the first Window it creates--which is the Window that gets assigned to Application.MainWindow--is a special dummy Window that is then made invisible by setting its Visibility to Visibility.Hidden or calling Window.Hide(). This will not hide its owned windows. Then, ensure that your desired "main" window containing your true content, plus all the other windows, owned by this invisible window.
Once hidden, the dummy Window will not show in the Windows 10 taskbar. You can set the Window.ShowInTaskbar property on whichever of the visible windows you deem appropriate to simulate apparent special designation, as required.
This approach allows any of the visible windows in your Application to be on top of the others, while still preserving WPF features and behaviors for system-wide app activation. For example, when the Application is activated (by clicking on any one of the windows, or via Alt-tab), all of the application's windows are together brought above any other desktop app windows, while still preserving the most recent "in-app" Z-order. WPF shutdown functionality is also preserved, including correct observation of the Application.ShutdownMode logic in accordance with the invisible MainWindow (or all the others) being closed.
I ran into a similar situation. I solved this by simply removing the owner after showing the window.
Window2 window = new Window2();
window.Owner = this;
window.Show();
window.Owner = null;
Edit:
Someone replied to this, and while looking at it, I decided I wanted to make an extension method.
public static void ShowAsIfChildOf(this Window childWindow, Window parentWindow)
{
childWindow.Owner = parentWindow;
childWindow.Show();
childWindow.Owner = null;
}
Please note that the solution mentioned by Licht
Window2 window = new Window2();
window.Owner = this;
window.Show();
window.Owner = null;
seems to me to be the same as
Window2 window = new Window2();
window.Show();
i.e., as if no ownership relationship has been set, i.e., when you close the owner window the owned windows do not close, etc., and may not be a solution when one would wish to have all of the ownership relationship features except "An owner window can never cover an owned window."
Once this relationship is established, the following behaviours are exhibited:
If an owner window is minimized, all its owned windows are minimized as well.
If an owned window is minimized, its owner is not minimized.
If an owner window is maximized, both the owner window and its owned windows are restored.
An owner window can never cover an owned window.
Owned windows that were not opened using ShowDialog are not modal. The user can still interact with the owner window.
If you close an owner window, its owned windows are also closed.
The best solution here looks to me to be the one with hidden main dummy window described by Glenn Slayden, at the link below, though it would be nice if there was a simpler one.
https://stackoverflow.com/a/66110288/19683309
Don't set Owner for child window, then in MainWindow handle OnClosing event like this:
private void MainWindow_OnClosing(object? sender, CancelEventArgs e)
{
foreach (var window in Application.Current.Windows)
{
if (window is Window appWindow)
{
if(appWindow.Equals(Application.Current.MainWindow))
continue;
appWindow.Close();
}
}
}

Categories