0

I have an WPF User control, let's say UCInner, which contains a WPF Popup. UCInner is used in another WPF user control, let's say UCOuter.

UCOuter is embedded in an ElementHost (ElementHost.Child = UCOuter).

Finally UCOuter is embedded within an Outlook VSTO custom task pane ahd this latter used in a winforms application (Outlook VSTO Add-in).

So from the most inner WPF Control, UCInner, I would like to obtain the parent Window. I have tried some alternatives with no success, I am always getting null or exceptions:

Window w = Window.GetWindow(myPopup);
Window w = Window.GetWindow(UCInner);

I also have tried what explained here and also this one.

  • UPDATED - ANOTHER ATTEMPT:

I have tried below piece of code and i can get successfully the window handle, but now from the handle I need to get the Window Object.

dynamic activeWindow = Globals.ThisAddIn.Application.ActiveWindow();
Microsoft.VisualStudio.OLE.Interop.IOleWindow win = activeWindow as Microsoft.VisualStudio.OLE.Interop.IOleWindow;

IntPtr handle;
win.GetWindow(out handle);

So in order to get the Window object I have tried this based on the Window handle:

System.Windows.Interop.HwndSource hwndSource = System.Windows.Interop.HwndSource.FromHwnd(handle);

Window w = hwndSource.RootVisual as Window;

but this does not work, hwndSource is null.

Willy
  • 9,848
  • 22
  • 141
  • 284
  • it is never late to find out what is MCVE ([mcve]) – ASh Dec 20 '22 at 17:20
  • Do you need to get an instance of the Explorer or Inspector class from the Outlook object model? Is that what you are looking to get from the inner user control? – Eugene Astafiev Dec 20 '22 at 17:42
  • @EugeneAstafiev Well, the Outlook application window itself. – Willy Dec 20 '22 at 18:19
  • 1
    You an use the [ActiveInspector](https://learn.microsoft.com/en-us/office/vba/api/outlook.application.activeinspector) or [ActiveExplorer](https://learn.microsoft.com/en-us/office/vba/api/outlook.application.activeexplorer) methods of the Outlook `Application` class anywhere in the code. – Eugene Astafiev Dec 20 '22 at 18:21
  • Do you actually have a Window object? If your control is embedded inside Outlook, you will not have a standalone WPF window - it simply does not exist. What do you need Window for? Are you trying to correctly parent a message box or a standalone popup? – Dmitry Streblechenko Dec 20 '22 at 19:04
  • @DmitryStreblechenko i am trying to correctly parent a popup which is placed in my UCInner – Willy Dec 20 '22 at 19:07
  • @EugeneAstafiev ActiveWindow method of the Outlook Applicationn does not return a Window object. I need to subscribe to the Activated and Deactivated events of the Window. – Willy Dec 20 '22 at 19:31
  • It seems you just need to specify the parent window handle for your popup window and don't invent a wheel further. The `IOleWindow` interface implemented by Outlook windows allows getting the window handle for setting a parent for your popup windows. – Eugene Astafiev Dec 20 '22 at 22:38
  • @EugeneAstafiev how to set the parent of the Popup? Popup.Parent is readonly. – Willy Dec 21 '22 at 00:12
  • You need to set the `Owner` property using the [WindowInteropHelper](https://learn.microsoft.com/en-us/dotnet/api/system.windows.interop.windowinterophelper?view=windowsdesktop-7.0) class. – Eugene Astafiev Dec 21 '22 at 09:27
  • So, you first retrieve the parent window handle from Outlook windows by casting them to the IOleWindow and calling the `GetWindow` method which returns a handle that should be assigned to the `Owner` property. – Eugene Astafiev Dec 21 '22 at 09:29
  • @EugeneAstafiev yep, as Dmitry said in his answer, but what do i need to pass as parameter to the WindowInteropHelper? my popup? – Willy Dec 21 '22 at 12:54
  • @EugeneAstafiev my popup is within a wpf usercontrol and i want to set its parent or owner to the outlook explorer window. – Willy Dec 21 '22 at 13:09
  • I've posted the code for the explorer window. – Eugene Astafiev Dec 21 '22 at 14:21
  • @EugeneAstafiev Yep, I see, but I have a particular scenario in which it does not work. I am wondering if it is possible to get the Window Object from once I get the handle using oleWindow.GetWindow(out handle);. Is it possible? I mean, Is it possible to get the Window Object from the ActiveExplorer? – Willy Dec 22 '22 at 11:40
  • What Window Object do you want to get? Isn't an explorer window in Outlook what you are looking for? – Eugene Astafiev Dec 22 '22 at 20:12

2 Answers2

1

If you need to figure out the right parent window to display your own WPF window, cast Application.ActiveWindow to IOleWindow (Application.ActiveWindow can return either Explorer or Inspector, they both support IOleWindow) and call IOleWindow.GetWindow. Once you have the HWND, create an instance of the WindowInteropHelper class and specify the Outlook window handle as the parent:

if (outlookHwnd != IntPtr.Zero)
{
    WindowInteropHelper helper = new WindowInteropHelper(YourDialogWindow);
    helper.Owner = outlookHwnd;
    YourDialogWindow.ShowInTaskbar = false;
}
Dmitry Streblechenko
  • 62,942
  • 4
  • 53
  • 78
  • Ok, i see, but in my case, what do i need to pass as parameter to the WindowInteropHelper? my popup? – Willy Dec 21 '22 at 12:53
  • my popup is within an wpf usercontrol and i would like to set the parent or owner of the popup to the outlook explorer. I do not know if it is possible. – Willy Dec 21 '22 at 13:10
  • Yes, it is possible. Is your popup another WPF Window object? – Dmitry Streblechenko Dec 21 '22 at 15:42
  • my WPF popup is placed within an WPF UserControl, not WPF Window object. – Willy Dec 21 '22 at 16:28
  • But then the control will be the parent, why do you need a window? Or are you saying that the popup will be a separate window in the Windows sense, but then it does not really matter that it gets displayed by some other user control. In that case the popup is a Window control descendant, and the suggestion above applies. – Dmitry Streblechenko Dec 21 '22 at 20:03
  • Looking at your updated question, you need to use WindowInteropHelper class and pass your popup Window descendant class to its constructor. WindowInteropHelper will then take care of making sure your window is the child of the HWND assigned to the WindowInteropHelper.Owner property. – Dmitry Streblechenko Dec 21 '22 at 20:07
  • My popup is placed within a WPF usercontrol and the popup as it is designed remains a topmost even if you place another application window on top so what I am trying to do what is explained in this link: https://stackoverflow.com/a/55541429/1624552 I want to get the window by passing the popup as explained there but I am getting a null. Then once I have the window I want to subscribe to Activated and Deactivated events to see if another window application different from Outlook is overlapping on top of my popup and then make the popup visible or not accordingly. – Willy Dec 21 '22 at 20:54
  • That won't work - you are assuming an arbitrary straight WinAPI window can be cast as / represented as an WPF Window object - that is only true if the window in question is a WPF window. If you want a window that always stays up on top of other windows, your window must have the WS_EX_TOPMOST bit set. See https://stackoverflow.com/questions/3729369/topmost-is-not-topmost-always-wpf – Dmitry Streblechenko Dec 22 '22 at 05:03
1

First, you need to retrieve the parent window handle, in case of Explorer window in Outlook you can use:

Outlook.Explorer explorer = OutlookApplication.ActiveExplorer();
IOleWindow oleWindow = explorer as IOleWindow;
IntPtr handle = IntPtr.Zero;

oleWindow.GetWindow(out handle);

if (handle != IntPtr.Zero)
{
    WindowInteropHelper helper = new WindowInteropHelper(DialogWindow);
    helper.Owner = handle;
    DialogWindow.ShowInTaskbar = false;
    DialogWindow.ShowDialog();
}

where IOleWindow can be defined in the following way:

/// <summary>
/// Implemented and used by containers and objects to obtain window handles
/// and manage context-sensitive help.
/// </summary>
/// <remarks>
/// The IOleWindow interface provides methods that allow an application to obtain
/// the handle to the various windows that participate in in-place activation,
/// and also to enter and exit context-sensitive help mode.
/// </remarks>
[ComImport]
[Guid("00000114-0000-0000-C000-000000000046")]
[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleWindow
{
    /// <summary>
    /// Returns the window handle to one of the windows participating in in-place activation
    /// (frame, document, parent, or in-place object window).
    /// </summary>
    /// <param name="phwnd">Pointer to where to return the window handle.</param>
    void GetWindow (out IntPtr phwnd) ;

    /// <summary>
    /// Determines whether context-sensitive help mode should be entered during an
    /// in-place activation session.
    /// </summary>
    /// <param name="fEnterMode"><c>true</c> if help mode should be entered;
    /// <c>false</c> if it should be exited.</param>
    void ContextSensitiveHelp ([In, MarshalAs(UnmanagedType.Bool)] bool fEnterMode) ;
}

Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
  • Is it possible to get the Window Object based on the handle obtained using oleWindow.GetWindow(out handle)? so I can then subscribe to Activated and Deactivated events from the Window Object. – Willy Dec 22 '22 at 11:44
  • What is your end goal? Why do you need to handle these events if you display a popup window? – Eugene Astafiev Dec 22 '22 at 20:10
  • My goal is the following: I have a WPF User control inside an Outlook custom task pane. Inside that WPF Control I have buttons, WPF TextBlocks, etc. and I have a user control which contains a WPF Popup and I attach to labels, TextBlocks, etc. By design Popup is the topmost, so if you put another desktop application over the Outlook, that Popup remains always visible at the topmost. To avoid this, I am trying to get the Window object of the Outlook (Explorer) and subscribe to Activated and Deactivated events to do what is explained here: https://stackoverflow.com/a/55541429 – Willy Dec 22 '22 at 20:28
  • Applying https://stackoverflow.com/a/55541429 i can make WPF Popup visible or not depending on whether the Outlook window is activated or deactivated so then Popup is not remaining always at the topmost when I open another application different than Outlook. – Willy Dec 22 '22 at 20:32
  • There is no need to subscribe to the `Activated` and `Deactivated` events if you specify the parent window handle correctly. – Eugene Astafiev Dec 23 '22 at 20:56
  • you mean setting the parent window of the WPF Popup? If so, afaik there's no way to do it. Could you indicate me how to do it? – Willy Dec 23 '22 at 21:21
  • Have you tried using the code mentioned in my post? – Eugene Astafiev Dec 24 '22 at 15:07
  • yep, i tried but in WindowInteropHelper helper = new WindowInteropHelper(DialogWindow); in my case what do I need to pass to WindowInteropHelper instance? my WPF popup is in an WPF user control and this user control is within another user control which in turn is embedded into an elementhost that finally is embedded in an Outlook custom task pane. So what do i need to pass to the WindowsInteropHelper instance? – Willy Dec 24 '22 at 17:35
  • You dialog instance should be wrapped with `WindowInteropHelper`. – Eugene Astafiev Dec 25 '22 at 16:53
  • yep, i know, but in my case i don't have a dialog, only a WPF popup (It is not a window) within a WPF User control. https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/popup-overview?view=netframeworkdesktop-4.8 – Willy Dec 25 '22 at 17:34
  • That is not a popup then, right? It is just a task pane with a WPF user control. – Eugene Astafiev Dec 25 '22 at 18:51
  • It is an Outook custom task pane with an WPF User control embedded into it (using an ElementHost container, otherwise it is imposible to mix winform with WPF). But as I have said from the beginning,, this WPF User control has some other WPF user controls within it and one of them contains basically an WPF Popup. So as you see, the only window I have is the Outlook window itself (Active Explorer or Active Inspector). – Willy Dec 25 '22 at 22:43
  • It doesn't matter what your WPF user interface contains on the task pane. If you display any popup window (even with a WPF content) you need to set the parent window handle to your dialog box to avoid such cases as you faced with already. – Eugene Astafiev Dec 26 '22 at 17:51
  • Yep, i know, but my WPF Popup is a .NET Framework WPF object which does not inherit from a Window object. See here: https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.popup?view=netframework-4.5 – Willy Dec 26 '22 at 22:33