I'm trying to invoke a WPF dialog from a child window of a Win32 application and was able to achieve this using the Window Handle (HWND) of the invoking window and passing it to the WPF code where I use the WindowInteropHelper class to set the Owner property to the handle of the invoking window. The code snippet below shows the WPF code where I'm setting the Owner property.
public void ShowModal(IntPtr ownerWindow)
{
WindowInteropHelper helper = new WindowInteropHelper(this);
helper.Owner = ownerWindow;
this.ShowDialog();
}
I'm getting the HWND of the invoking dialog using the Windows function as shown below:
HWND handle = GetTopWindow(GetActiveWindow());
While this works as expected functionally (the WPF dialog invokes as a modal dialog), it gets invoked at the top left corner of the screen. I've even set the WindowStartupLocation="CenterOwner" property in the XAML code. It works perfectly fine when invoked from a WPF window but not when Win32 window is involved. I guess I'm missing something in the WPF-Win32 interop here although it seems to me that the problem lies with the HWND retrieved from GetTopWindow(GetActiveWindow()).
UPDATE: I replaced the above code to get the HWND of the invoking window to the code below and now the WPF dialog always invokes at the center of the screen irrespective of the position of the window that invokes it.
HWND hWnd = GetActiveWindow();
if (hWnd != NULL)
{
hWnd = FindWindowEx(hWnd, NULL, "Window1", NULL);
if (hWnd != NULL)
{
hWnd = FindWindowEx(hWnd, NULL, "Window2", NULL);
}
}
Here Window2 is the Window that invokes the WPF dialog.
In your ShowModal() can you try :
this.Owner = App.MainWindow;
Related
I have a program running on my PC that controls another machine via TeamViewer. It all works fine except that sending a mouse click requires TeamViewer to be in the foreground. I have code that sends mouse clicks to programs like Notepad where the edit panel is called "Edit." But the TeamViewer panel is called TV_REMOTEDESKTOP_CLASS and FindWindowEx fails to find its handle.
Here is my code:
IntPtr handle = WinGetHandle("axie_machine");
if (handle != IntPtr.Zero)
{
var panel = FindWindowEx(handle, IntPtr.Zero, "TV_REMOTEDESKTOP_CLASS", null);
PerformRightClick(panel, new Point(200, 200));
}
Here is the image of Spy++ showing the details of the panel
FindWindowEx returns 0x000000.
Can anyone see what I am doing wrong with FindWindowEx and point in the right direction?
Assuming by WinGetHandle("axie_machine"), you're getting the handle of the TeamViewer window using (part of) its title, then, you're actually getting the handle of the top-level window which your target window whose class is "TV_REMOTEDESKTOP_CLASS" is not a child of. It is one of its descendants, but not a direct child. There's one parent window in between as you can see here:
So, change your code to get the parent window of your target "panel" and then use that to get to the target. The code should looks something like the following:
IntPtr tvWindowHandle = WinGetHandle("axie_machine");
if (tvWindowHandle != IntPtr.Zero)
{
var panelParent = FindWindowEx(tvWindowHandle, IntPtr.Zero, "ATL:03B8D350", null);
if (panelParent != IntPtr.Zero)
{
var panel = FindWindowEx(panelParent, IntPtr.Zero, "TV_REMOTEDESKTOP_CLASS", null);
PerformRightClick(panel, new Point(200, 200));
}
}
Note: You might want to double-check the class of the parent window. It was "ATL:03B8D350" in my version of TV but it might be different for you if you're using another version.
I work on add-in for CAD programm. And I've found a bug in owner logic.
When I create a simple WPF window I get the handle of the main CAD window and set it as the owner of my WPF window. The handle is correct, I checked it by SPY++
Here how I do that in constructor of WPF window (it has type System.Windows.Window):
var helper = new WindowInteropHelper(this);
helper.Owner = owner;
MSDN says the next tips about Owner-Child relationships:
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.
If an owned window was opened by its owner window using Show, and the owner window is closed, the owned window's Closing event is not raised.
And all work except "An owner window can never cover an owned window".
If my WPF window is non-modal it can be hidden behind the main CAD window when I click on it.
If my WPF window is modal in some cases it also can be hidden behind the main CAD window when I minimize and maximize it.
So I've used TOPMOST property to fix these problems. And I set it as TRUE when try to click on the main CAD window and set as FALSE when try to switch to other programms. It looks next:
private IntPtr HookForCAD(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_ACTIVATE && (int)wParam == WA_INACTIVE)
{
this.Topmost = lParam == new WindowInteropHelper(this).Owner;
}
if (msg == WM_ACTIVATEAPP)
{
this.Topmost = (int)wParam != WA_INACTIVE;
}
return IntPtr.Zero;
}
It helps fix almost all cases. But there is still one issue. When we see the part of the window of other program (3) behind the CAD window (2). We open our WPF window (1) and try to switch to other program by mouse click on area of window (3). And it doesn't work. Window (3) will be activated but we still see the WPF window and CAD window in the foreground.
How can I fix it ??
I have the following C# code in a WPF project:
private static void RunConfig(string owner)
{
long ownerHandle;
var settingsWindow = new SettingsWindow();
if (long.TryParse(owner, out ownerHandle))
{
WindowInteropHelper helper = new WindowInteropHelper(settingsWindow);
helper.Owner = new IntPtr(ownerHandle);
}
settingsWindow.ShowDialog();
}
The SettingsWindow isn't properly modal to the owner window (i.e. I can focus on, interact with, and even close the owner window while the SettingsWindow is still open). What am I doing wrong?
For context, this code is part of a screen saver program, and the owner window is the Control Panel screen saver selection window (which passes in the handle to use as owner via command line parameter). I know the IF statement is evaluating true and correctly parsing the handle.
I have also tried using the SetWindowLongPtr method from user32.dll (compiling for x64, hence not using SetWindowLong) which is briefly described here and shown in use here. This method works in WinForms, but doesn't seem to work here in WPF. Help me Obi-Wan Kenobi, you're my only hope.
It turns out that using WindowInteropHelper to set the native window as owner of the WPF Window does work, it just doesn't do the whole job. When set this way, the WPF Window will remain visible on top of the native window, even if the native window has focus. However, that is the only effect obtained. The WPF Window does not prevent interaction with the native Window, and the native window can even be closed, without the WPF Window closing or being affected.
In order to get the rest of the desired behaviour, we need to use the EnableWindow function in user32.dll to disable the native Window before calling ShowDialog on the WPF Window, and again to re-enable it once the WPF Window closes.
The modified code looks like this:
private static void RunConfig(string owner)
{
long ownerHandle;
var settingsForm = new SettingsWindow();
if (long.TryParse(owner, out ownerHandle))
{
WindowInteropHelper helper = new WindowInteropHelper(settingsForm);
helper.Owner = new IntPtr(ownerHandle);
NativeMethods.EnableWindow(helper.Owner, false);
settingsForm.ShowDialog();
NativeMethods.EnableWindow(helper.Owner, true);
}
else
{
settingsForm.ShowDialog();
}
}
(Note: The above code is correct in general, but incomplete in the case of screen savers, which is what this code is actually being used for. In the case that this code is being used for the config window of a screen saver, the string passed in for the owner handle is not the handle of the Control Panel window to be used as owner, but rather a handle for a control that is a child of the Control Panel window. The extra step in this case is to get the handle of the parent of that control. We can do this by calling GetParent, also in user32.dll, on the passed-in handle. This will return the real handle we want to use for the owner and EnableWindow calls.)
If anyone from Microsoft ever finds this, maybe consider modifying WindowInteropHelper to properly set all of this up when Owner is assigned and ShowDialog used, since this is the proper complete behavior for modal windows.
I've created a Microsoft Word 2010 vsto add-in which displays a number of custom Wpf Window dialogs when users click on ribbon buttons.
The issue I'm having is that the custom dialog dissapears behind the Word instance if you click the Word icon in the task bar.
After some Googling it appears that this can be fixed by setting the Owner property of my window, but I'm struggling to get the Window instance of the Word application.
I've attached the relevant code below, any suggestions?
using WordNS = Microsoft.Office.Interop.Word;
Window wrapperWindow = new Window();
wrapperWindow.ResizeMode = ResizeMode.NoResize;
wrapperWindow.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
wrapperWindow.ShowInTaskbar = false;
wrapperWindow.Content = dialogViewModel.View;
wrapperWindow.Title = dialogViewModel.Title;
wrapperWindow.SizeToContent = SizeToContent.WidthAndHeight;
WordNS.Application app = (WordNS.Application)Marshal.GetActiveObject("Word.Application");
wrapperWindow.Owner = (Window)app.ActiveWindow;
The exception clearly states that the answer to your question is No.
If Microsoft.Office.Interop.Word provides any means to get the window handle (HWND) of Word's main window (or if you get that handle by some Win32 call), you might try to set your window's owner by the WindowInteropHelper.Owner property.
Used Clemens' suggestion to go with the WindowInteropHelper route, below is the complete code to get this working:
1) Define this pointer anywhere inside your class:
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
2) Add the following code to the window declaration code:
Window wrapperWindow = new Window();
//Set all the relevant window properties
//Set the owner of the window to the Word application
IntPtr wordWindow = GetForegroundWindow();
WindowInteropHelper wih = new WindowInteropHelper(wrapperWindow);
wih.Owner = wordWindow;
I am working on a C# XNA screensaver kit and so far, everything is in place except for the configuration dialog which must be modal to the Screen Saver Settings dialog provided by windows ("/c:<hwnd>" argument).
My benchmark is Vistas builtin 3D Text screensaver - my code shall provide the same features and regarding the configuration dialog, 3D Text displays fully modal to the Screen Saver Settings dialog and when clicking Screen Saver Settings dialog, the dialogs blink without accepting the click.
I have tried the method of wrapping the HWND with a IWin32Window as suggested by Ryan Farley, but even though my dialog displays on top of the Screen Saver Settings dialog, the controls in Screen Saver Settings dialog still can be clicked.
So do I need some exotic Win32API calls to inform the parent dialog that it has been modalized or does a more clean solution exist?
Try calling the SetParent API function.
Actually, it turned out that the HWND provided by windows to the screensaver is a child of the settings dialog, so by calling GetParent on the HWND, I get an HWND that represents the dialog.
Today is the day that I wrote my first question on stackoverflow.com and answered the first question.
I had the same problem. Additionally I was not able to directly hook a dialog to a foreign window with .NET. Therefore I supply a work around to hook a dialog to the parent of a given window handle:
public class CHelpWindow : Form
{ // this class hooks to a foreign window handle and then it starts a
// modal dialog to this form; .NET seems not to be able to hook a dialog
// to a foreign window handle directly: therefore this solution
// retrieves the parent window of the passed child
[DllImport("user32.dll")]
private static extern IntPtr GetParent (IntPtr hWndChild);
// changes the parent window of the passed child
[DllImport("user32.dll")]
private static extern IntPtr SetParent
(IntPtr hWndChild, IntPtr hWndNewParent);
// --------------------------------------------------------------------
public CHelpWindow (long liHandle)
// this constructor initializes this form and hooks this form to the parent
// of the passed window handle; then it prepares the call to create the
// dialog after this form window is first shown in the screen.
{
// removing the system title of the window
FormBorderStyle = FormBorderStyle.None;
// the dialog will be created when this form is first shown
Shown += new EventHandler (HelpWindow_Shown);
// hooking to the parent of the passed handle: that is the control, not
// the tab of the screen saver dialog
IntPtr oParent = GetParent (new IntPtr (liHandle));
SetParent (Handle, oParent);
}
// --------------------------------------------------------------------
private void HelpWindow_Shown (object oSender, EventArgs oEventArgs)
// this function is called when the form is first shown; now is the right
// time to start our configuration dialog
{
// positioning this window outside the parent area
Location = new Point (-100, -100);
// reducing the size
Size = new Size (1, 1);
ClientSize = new Size (1, 1);
// creating the dialog
CKonfiguration oConfiguration = new CKonfiguration ();
// starting this dialog with the owner of this object; because this
// form is hooked to the screen saver dialog, the startet dialog is
// then indirectly hooked to that dialog
oConfiguration.ShowDialog (this);
// we don not need this object any longer
Close ();
}
}
After extracting your handle from the command line
/c:####
you create your Dialog by
CHelpWindow oHelpWindow = new CHelpWindow (liHandle);
oHelpWindow.ShowDialog ();
Reimer