views:

2028

answers:

4

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 IE7 extension I'm trying to open a window modal with respect to an IE 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 IE, including in the tab that's supposed to be the owner. My form is shown floating above the IE 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? Any thoughts on how to 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 IE7 & .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, eg when I click with the mouse on other tabs IE 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 FWIW 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 IE tab had just been opened but wasn't active then the MessageBox would be hidden and that tab would start flashing. However, since IE was disabled with my form opened modal it wasn't possible to switch to that tab, so IE 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 here.

A: 

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?

Dave Swersky
A: 

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.

JaredPar
Rory
@Rory, that may be the case but what I've said about threads is correct. A modal dialog on one thread will not affect UI in another trhead.
JaredPar
+3  A: 

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.

Hans Passant
+1  A: 

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; }
}
gt