.net - navegacion - ¿Puedo superponer una ventana WPF sobre otra?
navegacion entre ventanas wpf (5)
Después de un montón de pruebas de las diferentes soluciones:
- Microsoft.DwayneNeed ( https://microsoftdwayneneed.codeplex.com/ )
Pros:- Funciona bien hasta donde puedo decir (no lo he probado tanto, pero parecía que sí)
- El golpe de rendimiento parece ser bastante bajo.
Contras:- Biblioteca relativamente grande.
- AirspaceFixer ( https://github.com/chris84948/AirspaceFixer )
Pros:- Funciona bien (no lo he probado tanto pero parece que sí)
Contras:- Hay un pequeño golpe de rendimiento. (Más grande que con Microsoft.DwayneNeed por lo que puedo decir)
- AirspacePopup de Fredrik Hedblad ( https://stackoverflow.com/a/6452940/4870255 )
Pros:- Código simple. Sobre todo: D
Contras:- En pantalla completa falta una parte de la parte inferior.
- Problemas de varios monitores: cuando se mueve de una ventana a otra, la superposición no está completamente sobre la ventana de presentación de winform.
Para mi la mejor solución fue usar Microsoft.DwayneNeed. (Lo usé para obtener CefSharp-Winforms en una aplicación WPF)
Porque conseguir que funcione no es sencillo, es un pequeño tutorial:
- Vaya a https://microsoftdwayneneed.codeplex.com/SourceControl/latest
- Haga clic en descargar.
- Abra Microsoft.DwayneNeed.sln
- Compilarlo
- Haga referencia a Microsoft.DwayneNeed.dll en la carpeta Debug o Releas creada.
- Añadir
xmlns:interop="clr-namespace:Microsoft.DwayneNeed.Interop;assembly=Microsoft.DwayneNeed a tu ventana.
- Añadir
<interop:AirspaceDecorator AirspaceMode="Redirect" Background="White" IsInputRedirectionEnabled="True" IsOutputRedirectionEnabled="True"> <WindowsFormsHost x:Name="windowsFormsHost1" Visibility="Visible" /> </interop:AirspaceDecorator> a su cuadrícula.
Ejemplo:
<Window x:Class="Toll.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:interop="clr-namespace:Microsoft.DwayneNeed.Interop;assembly=Microsoft.DwayneNeed"
xmlns:local="clr-namespace:Toll"
mc:Ignorable="d"
Title="MainWindow" Width="1500" Height="800" Closing="Window_Closing">
<Grid Name="root">
<interop:AirspaceDecorator AirspaceMode="Redirect"
Background="White"
IsInputRedirectionEnabled="True"
IsOutputRedirectionEnabled="True">
<WindowsFormsHost x:Name="windowsFormsHost1" Visibility="Visible" />
</interop:AirspaceDecorator>
<Ellipse Width="100" Height="100" Fill="Green" Margin="100"/>
</Grid>
</Window>
Tengo una ventana WPF, que contiene un elemento WindowsFormsHost
. Necesito dibujar cosas sobre este elemento, pero la naturaleza de WindowsFormsHost
significa que siempre está en la parte superior de la pila de dibujo. Como no puedo dibujar en la misma ventana WPF en la parte superior del componente WindowsFormsHost
, ¿puedo superponer otra ventana en la parte superior?
He intentado esto de forma rudimentaria, pero tengo algunos problemas:
1) No puedo detener las ventanas de otras aplicaciones que se interponen entre la ventana principal y la ventana de superposición.
2) Cuando hago 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 la ventana que para todos los propósitos aparece como parte de otra ventana. UserControls no funcionará para mí, ya que WindowsFormsHost
siempre dibujará sobre él.
¿Algunas ideas?
Actualización [23 de mayo de 11 a 10:13]
Gracias a los dos por las respuestas.
He intentado el enfoque ChildWindow
, y el elemento WindowsFormsHost
aún se dibuja en la parte superior. Como lo entiendo, solo una verdadera ventana puede dibujarse sobre un WindowsFormsHost
, cualquier cosa en la misma ventana irá debajo del WindowsFormsHost
.
Un elemento con WindowsFormsHost
aún se dibujará bajo un componente WinForms, siempre se dibujará en la parte superior, y eso parece no negociable ...
Supongo que lo que estoy buscando es una forma de acoplar una ventana externa para que actúe como parte de la ventana principal. En la Mac, existe el concepto de una verdadera "ventana secundaria", estoy buscando algo así.
En una aplicación MVP típica que usa CAL o agregador de eventos, es posible que tenga que crear otra ventana y denominarla emergente o secundaria ( ChildWindow.XAML
) y tener un método de obtención en ( ChildWindow.XAML.CS
) como
public static ChildWindow Get()
{
ChildWindow dialogBox = new ChildWindow();
return dialogBox;
}
Tener una propiedad en la ventana del manual que pueda devolver el tipo de Ventana del Niño cuando sea necesario. me gusta
public bool ShowChildWindow
{
get
{
return PopUpDialog.Get().ShowDialog().GetValueOrDefault();
}
}
Espero que esto ayude,
He resuelto este problema utilizando una Window
Popup
lugar de una Window
transparente
Actualizar
Terminé con una ventana Popup
subclasificada a la que llamo AirspacePopup
.
¿Qué hace AirspacePopup
- Siga su
PlacementTarget
. - No siempre está en la parte superior, sino que se coloca en relación con la
Window
en la que se coloca. Esta solución proviene del blog de Chris Cavanagh . - Se permite ser movido "fuera de la pantalla". Esto se logra recortando la
Popup
y estableciendo unMargin
negativo en su hijo una vez que se mueve fuera de la pantalla. Esta solución viene de este post de Rick Sladkey.
Aquí hay un ejemplo donde se utiliza AirspacePopup
para dibujar una Ellipse
sobre un Control WebBrowser
(que en realidad es un Control WinForms) pero funcionará igual de bien con cualquier WindowsFormsHost
.
<Grid>
<local:AirspacePopup PlacementTarget="{Binding ElementName=webBrowser}"
FollowPlacementTarget="True"
AllowOutsideScreenPlacement="True"
ParentWindow="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
IsOpen="True"
AllowsTransparency="True"
Placement="Center"
Width="{Binding ElementName=googleBrowser, Path=ActualWidth}"
Height="{Binding ElementName=googleBrowser, Path=ActualHeight}">
<Grid>
<Ellipse Width="100" Height="100" Fill="Green" Margin="100"/>
</Grid>
</local:AirspacePopup>
<WebBrowser Name="webBrowser" Loaded="WebBrowser_Loaded"/>
</Grid>
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..com");
}
}
Espacio aéreo
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 restore InconsistentNaming
#pragma warning restore 1591
#pragma warning restore 169
#endregion
}
Podría hacer la parte de "superposición" en WindowsFormsHost
. Entonces, allí podría tener un elemento secundario que debería estar encima de otro contenido en el elemento alojado.
Yo sugeriría usar la biblioteca MahApps .
He estado luchando con este problema durante algún tiempo y encontré 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: