¿Puedo superponer una ventana de WPF sobre otra?

Tengo una ventana de WPF, que contiene un elemento de WindowsFormsHost . Necesito dibujar cosas sobre este elemento, pero la naturaleza de WindowsFormsHost significa que siempre está en la parte superior de la stack de dibujos. Como no puedo dibujar en la misma ventana WPF en la parte superior del componente WindowsFormsHost , ¿puedo superponer otra ventana encima?

Lo he intentado de manera rudimentaria, pero tengo algunos problemas:

1) No puedo detener las ventanas de otras aplicaciones que se encuentran entre la ventana principal y la ventana de superposición.

2) Cuando presiono Alt-Tab, la ventana de superposición aparece en la lista de ventanas, lo cual es bastante feo.

Básicamente, necesito el concepto de una “ventana secundaria” y una ventana que, a todos los efectos, aparece como parte de otra ventana. UserControls no funcionará para mí, ya que WindowsFormsHost siempre dibujará encima de él.

¿Algunas ideas?


Actualización [23 de mayo a las 10:13]

Gracias a ambos por las respuestas.

ChildWindow enfoque ChildWindow , y el elemento WindowsFormsHost aún se dibuja en la parte superior. Tal como lo entiendo, solo una ventana verdadera puede dibujar encima de un WindowsFormsHost , cualquier cosa en la misma ventana irá debajo de WindowsFormsHost .

Un elemento con WindowsFormsHost seguirá dibujando bajo un componente de WinForms, siempre se dibujarán en la parte superior, y eso parece no negociable …

Creo que lo que estoy buscando es una forma de acoplar una ventana externa para actuar como parte de la ventana principal. En la Mac, existe el concepto de una verdadera “ventana infantil”, estoy buscando algo así.

He solucionado este problema utilizando una Window Popup lugar de una Window transparente

Actualizar

Terminé con una ventana Popup subclase que llamo AirspacePopup .

Qué hace AirspacePopup

  • Sigue su PlacementTarget .
  • No siempre está en la parte superior, pero se coloca en relación con la Window en la que se está colocando. Esta solución proviene del blog de Chris Cavanagh .
  • Se permite mover “fuera de la pantalla”. Esto se logra al recortar el Popup y establecer Margin negativo en su hijo una vez que se mueve fuera de la pantalla. Esta solución proviene de esta publicación de StackOverflow de Rick Sladkey

Aquí hay un ejemplo donde AirspacePopup se usa para dibujar una Ellipse encima de un Control WebBrowser (que de hecho es un Control WinForms) pero funcionará igual de bien con cualquier WindowsFormsHost .

         

Código simple detrás para navegar ..

 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void WebBrowser_Loaded(object sender, RoutedEventArgs e) { WebBrowser webbrowser = sender as WebBrowser; webbrowser.Navigate("http://www.stackoverflow.com"); } } 

Espacio aéreoPopup

 using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Interop; public class AirspacePopup : Popup { public static readonly DependencyProperty IsTopmostProperty = DependencyProperty.Register("IsTopmost", typeof(bool), typeof(AirspacePopup), new FrameworkPropertyMetadata(false, OnIsTopmostChanged)); public static readonly DependencyProperty FollowPlacementTargetProperty = DependencyProperty.RegisterAttached("FollowPlacementTarget", typeof(bool), typeof(AirspacePopup), new UIPropertyMetadata(false)); public static readonly DependencyProperty AllowOutsideScreenPlacementProperty = DependencyProperty.RegisterAttached("AllowOutsideScreenPlacement", typeof(bool), typeof(AirspacePopup), new UIPropertyMetadata(false)); public static readonly DependencyProperty ParentWindowProperty = DependencyProperty.RegisterAttached("ParentWindow", typeof(Window), typeof(AirspacePopup), new UIPropertyMetadata(null, ParentWindowPropertyChanged)); private static void OnIsTopmostChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) { AirspacePopup airspacePopup = source as AirspacePopup; airspacePopup.SetTopmostState(airspacePopup.IsTopmost); } private static void ParentWindowPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) { AirspacePopup airspacePopup = source as AirspacePopup; airspacePopup.ParentWindowChanged(); } private bool? m_appliedTopMost; private bool m_alreadyLoaded; private Window m_parentWindow; public AirspacePopup() { Loaded += OnPopupLoaded; Unloaded += OnPopupUnloaded; DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(PlacementTargetProperty, typeof(AirspacePopup)); descriptor.AddValueChanged(this, PlacementTargetChanged); } public bool IsTopmost { get { return (bool)GetValue(IsTopmostProperty); } set { SetValue(IsTopmostProperty, value); } } public bool FollowPlacementTarget { get { return (bool)GetValue(FollowPlacementTargetProperty); } set { SetValue(FollowPlacementTargetProperty, value); } } public bool AllowOutsideScreenPlacement { get { return (bool)GetValue(AllowOutsideScreenPlacementProperty); } set { SetValue(AllowOutsideScreenPlacementProperty, value); } } public Window ParentWindow { get { return (Window)GetValue(ParentWindowProperty); } set { SetValue(ParentWindowProperty, value); } } private void ParentWindowChanged() { if (ParentWindow != null) { ParentWindow.LocationChanged += (sender, e2) => { UpdatePopupPosition(); }; ParentWindow.SizeChanged += (sender, e2) => { UpdatePopupPosition(); }; } } private void PlacementTargetChanged(object sender, EventArgs e) { FrameworkElement placementTarget = this.PlacementTarget as FrameworkElement; if (placementTarget != null) { placementTarget.SizeChanged += (sender2, e2) => { UpdatePopupPosition(); }; } } private void UpdatePopupPosition() { FrameworkElement placementTarget = this.PlacementTarget as FrameworkElement; FrameworkElement child = this.Child as FrameworkElement; if (PresentationSource.FromVisual(placementTarget) != null && AllowOutsideScreenPlacement == true) { double leftOffset = CutLeft(placementTarget); double topOffset = CutTop(placementTarget); double rightOffset = CutRight(placementTarget); double bottomOffset = CutBottom(placementTarget); Debug.WriteLine(bottomOffset); this.Width = Math.Max(0, Math.Min(leftOffset, rightOffset) + placementTarget.ActualWidth); this.Height = Math.Max(0, Math.Min(topOffset, bottomOffset) + placementTarget.ActualHeight); if (child != null) { child.Margin = new Thickness(leftOffset, topOffset, rightOffset, bottomOffset); } } if (FollowPlacementTarget == true) { this.HorizontalOffset += 0.01; this.HorizontalOffset -= 0.01; } } private double CutLeft(FrameworkElement placementTarget) { Point point = placementTarget.PointToScreen(new Point(0, placementTarget.ActualWidth)); return Math.Min(0, point.X); } private double CutTop(FrameworkElement placementTarget) { Point point = placementTarget.PointToScreen(new Point(placementTarget.ActualHeight, 0)); return Math.Min(0, point.Y); } private double CutRight(FrameworkElement placementTarget) { Point point = placementTarget.PointToScreen(new Point(0, placementTarget.ActualWidth)); point.X += placementTarget.ActualWidth; return Math.Min(0, SystemParameters.VirtualScreenWidth - (Math.Max(SystemParameters.VirtualScreenWidth, point.X))); } private double CutBottom(FrameworkElement placementTarget) { Point point = placementTarget.PointToScreen(new Point(placementTarget.ActualHeight, 0)); point.Y += placementTarget.ActualHeight; return Math.Min(0, SystemParameters.VirtualScreenHeight - (Math.Max(SystemParameters.VirtualScreenHeight, point.Y))); } private void OnPopupLoaded(object sender, RoutedEventArgs e) { if (m_alreadyLoaded) return; m_alreadyLoaded = true; if (Child != null) { Child.AddHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(OnChildPreviewMouseLeftButtonDown), true); } m_parentWindow = Window.GetWindow(this); if (m_parentWindow == null) return; m_parentWindow.Activated += OnParentWindowActivated; m_parentWindow.Deactivated += OnParentWindowDeactivated; } private void OnPopupUnloaded(object sender, RoutedEventArgs e) { if (m_parentWindow == null) return; m_parentWindow.Activated -= OnParentWindowActivated; m_parentWindow.Deactivated -= OnParentWindowDeactivated; } private void OnParentWindowActivated(object sender, EventArgs e) { SetTopmostState(true); } private void OnParentWindowDeactivated(object sender, EventArgs e) { if (IsTopmost == false) { SetTopmostState(IsTopmost); } } private void OnChildPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { SetTopmostState(true); if (!m_parentWindow.IsActive && IsTopmost == false) { m_parentWindow.Activate(); } } protected override void OnOpened(EventArgs e) { SetTopmostState(IsTopmost); base.OnOpened(e); } private void SetTopmostState(bool isTop) { // Don't apply state if it's the same as incoming state if (m_appliedTopMost.HasValue && m_appliedTopMost == isTop) { return; } if (Child == null) return; var hwndSource = (PresentationSource.FromVisual(Child)) as HwndSource; if (hwndSource == null) return; var hwnd = hwndSource.Handle; RECT rect; if (!GetWindowRect(hwnd, out rect)) return; Debug.WriteLine("setting z-order " + isTop); if (isTop) { SetWindowPos(hwnd, HWND_TOPMOST, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS); } else { // Z-Order would only get refreshed/reflected if clicking the // the titlebar (as opposed to other parts of the external // window) unless I first set the popup to HWND_BOTTOM // then HWND_TOP before HWND_NOTOPMOST SetWindowPos(hwnd, HWND_BOTTOM, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS); SetWindowPos(hwnd, HWND_TOP, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS); SetWindowPos(hwnd, HWND_NOTOPMOST, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS); } m_appliedTopMost = isTop; } #region P/Invoke imports & definitions #pragma warning disable 1591 //Xml-doc #pragma warning disable 169 //Never used-warning // ReSharper disable InconsistentNaming // Imports etc. with their naming rules [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); static readonly IntPtr HWND_TOP = new IntPtr(0); static readonly IntPtr HWND_BOTTOM = new IntPtr(1); private const UInt32 SWP_NOSIZE = 0x0001; const UInt32 SWP_NOMOVE = 0x0002; const UInt32 SWP_NOZORDER = 0x0004; const UInt32 SWP_NOREDRAW = 0x0008; const UInt32 SWP_NOACTIVATE = 0x0010; const UInt32 SWP_FRAMECHANGED = 0x0020; /* The frame changed: send WM_NCCALCSIZE */ const UInt32 SWP_SHOWWINDOW = 0x0040; const UInt32 SWP_HIDEWINDOW = 0x0080; const UInt32 SWP_NOCOPYBITS = 0x0100; const UInt32 SWP_NOOWNERZORDER = 0x0200; /* Don't do owner Z ordering */ const UInt32 SWP_NOSENDCHANGING = 0x0400; /* Don't send WM_WINDOWPOSCHANGING */ const UInt32 TOPMOST_FLAGS = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING; // ReSharper restre InconsistentNaming #pragma warning restre 1591 #pragma warning restre 169 #endregion } 

Después de probar muchas de las diferentes soluciones:

  1. Microsoft.DwayneNeed ( https://microsoftdwayneneed.codeplex.com/ )

    Pros:

    • Funciona bien por lo que puedo decir (No lo he probado tanto pero parecía)
    • El golpe de rendimiento parece ser muy bajo.

    Contras:

    • Relativly gran biblioteca.
  2. AirspaceFixer ( https://github.com/chris84948/AirspaceFixer )

    Pros:

    • Funciona bien (No lo he probado tanto pero parecía)

    Contras:

    • Hay un pequeño golpe de rendimiento. (Más grande que con Microsoft.DwayneNeed por lo que puedo decir)
  3. AirspacePopup de Fredrik Hedblad ( https://stackoverflow.com/a/6452940/4870255 )

    Pros:

    • Código simple. Mayormente: D

    Contras:

    • En pantalla completa falta una parte de la parte inferior.
    • Problemas de múltiples monitores: cuando se pasa de una ventana a otra, la superposición no se completa completamente sobre la ventana winformshost.

Para mí, la mejor solución fue usar Microsoft.DwayneNeed. (Lo usé para obtener CefSharp-Winforms en una aplicación WPF)

Porque hacer que funcione no es sencillo, ella es un pequeño tutorial:

  1. Vaya a https://microsoftdwayneneed.codeplex.com/SourceControl/latest
  2. Haga clic en descargar.
  3. Abra Microsoft.DwayneNeed.sln
  4. Comstackrlo
  5. Referencia Microsoft.DwayneNeed.dll en la carpeta Debug o Release creada.
  6. Añadir
      xmlns: interop = "clr-namespace: Microsoft.DwayneNeed.Interop; assembly = Microsoft.DwayneNeed 

    a tu ventana.

  7. Añadir
     
     
      

    a su cuadrícula

Ejemplo:

         

En una aplicación MVP típica que use CAL o el agregador de eventos, es posible que tenga que crear otra ventana y ChildWindow.XAML un nombre emergente o secundario ( ChildWindow.XAML ) y tener un método get en ( ChildWindow.XAML.CS ) como

 public static ChildWindow Get() { ChildWindow dialogBox = new ChildWindow(); return dialogBox; } 

Tener una propiedad en la ventana que puede devolver el tipo Childwindow cuando lo necesite. me gusta

 public bool ShowChildWindow { get { return PopUpDialog.Get().ShowDialog().GetValueOrDefault(); } } 

Espero que esto ayude,

Podría hacer la parte “superposición” en WindowsFormsHost . Entonces, podría tener un elemento secundario que debería estar encima de otro contenido en el elemento alojado.

Sugeriría usar la biblioteca MahApps .

He estado luchando con este problema durante un tiempo y descubrí que la biblioteca MahApps solucionó el problema del espacio aéreo sin ninguna configuración de mi parte. Para obtener más información, consulte sus preguntas frecuentes sobre esto:

https://github.com/MahApps/MahApps.Metro/wiki/FAQ#1-why-is-so-and-so-winforms-control-invisible-or-not-rendering-why-is-the-webbrowser- or-other-control-covering-my-flyout-or-another-control-airspace