WPF中的WebBrowser控件之上的WPF用户控件

时间:2016-03-01 10:59:48

标签: wpf user-controls webbrowser-control

我一直在浏览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实现相同类型的方案。请帮助别人。

0 个答案:

没有答案