我一直在浏览WPF WebBrowserControl之上渲染WPF UserControl(作为父控件)的问题。
同样的情况,当我尝试在Window(作为父控件)上实现相同的功能时,在WPF WebBrowser之上,我很成功。但是在WPF UserControl的情况下,我无法做到这一点。 (即无法将用户控件置于Web浏览器之上,但能够将窗口置于WPF WebBrowser控件之上)。
问题被称为" AirSpace问题",它通过使用HWND的概念被删除。
任何人都能帮忙吗?
窗口代码作为父级(工作正常):
XAML:
<Window x:Class="WebBrowserDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WebBrowserDemo"
>
<DockPanel>
<Grid>
<Button HorizontalAlignment="Right"
MinWidth="80" Margin="3" Content="Send" Click="Button_Click_1" />
</Grid>
<Grid>
<local:AirspacePopup PlacementTarget="{Binding ElementName=webBrowser}" x:Name="PopupNew"
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>
<local:UserControl1></local:UserControl1>
</Grid>
</local:AirspacePopup>
<WebBrowser Name="webBrowser" Loaded="WebBrowser_Loaded"/>
</Grid>
</DockPanel>
自定义控件类(AirSpace类):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls.Primitives;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
namespace WebBrowserDemo
{
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 (placementTarget!=null && 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;
}
// 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
}
}
UserControl.xaml:
<UserControl x:Class="WebBrowserDemo.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="I AM USER CONTROl"></TextBlock>
<Ellipse Width="100" Height="100" Fill="Green" Margin="100"/>
</Grid>
</UserControl>
需要使用UserControl实现相同类型的方案。请帮助别人。