wpf - 如何解决 AllowsTransparency=true 的 WPF 窗口中的 ActiveX WebBrowser 缺陷

标签 wpf xaml skinning

我正在构建一个为所有窗口和对话框创建自定义外观的应用程序;每个窗口的右上角都有一个定制的“X”按钮,等等。我基本上设置了 WindowStyle=None 和 AllowsTransparency=True。我在此窗口中有一个选项卡控件,其中一个选项卡有一个 WPF WebBrowser 控件。当我刚开始时,我没有在窗口上自定义皮肤,并且 Web 浏览器控件按预期工作。一旦我为窗口设置了皮肤并设置了 AllowsTransparency=True,WebBrowser 控件现在只显示一个空白页面。

我知道 ActiveX 中存在某种缺陷导致了这种情况,因为 WebBrowser WPF 控件本质上是一个包装器。有人可以为这个问题提供一个体面的解决方案或一个体面的解决方法吗?

以下是我的xaml和截图。为了这篇文章,我将它简化为一个简单的窗口:

这有效:(AllowTransparency=False)

<Window x:Class="AllowTransparencyIssue.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" 
        Height="500" 
        Width="700"
        AllowsTransparency="False"
        WindowStartupLocation="CenterScreen"
        WindowStyle="SingleBorderWindow"

        >
    <Border BorderBrush="Blue" BorderThickness="3">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>

            <Button Grid.Row="0" HorizontalAlignment="Right" Click="Button_Click">Close Window</Button>

            <TabControl Grid.Row="1">

                <TabItem Header="StartPage.com">
                    <TabItem.Content>
                        <WebBrowser Source="http://www.startpage.com" />
                    </TabItem.Content>
                </TabItem>

                <TabItem Header="Google.com">
                    <TabItem.Content>
                        <WebBrowser Source="http://www.google.com" />
                    </TabItem.Content>
                </TabItem>

                <TabItem Header="Bing.com">
                    <TabItem.Content>
                        <WebBrowser Source="http://www.google.com" />
                    </TabItem.Content>
                </TabItem>

            </TabControl>
        </Grid>
    </Border>
</Window>

截图:

No Transparency

以下代码设置 AllowsTransparency=True 和 WindowStyle=None:

<Window x:Class="AllowTransparencyIssue.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" 
        Height="500" 
        Width="700"
        AllowsTransparency="True"
        WindowStartupLocation="CenterScreen"
        WindowStyle="None"

        >
    <Border BorderBrush="Blue" BorderThickness="3">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>

            <Button Grid.Row="0" HorizontalAlignment="Right" Click="Button_Click">Close Window</Button>

            <TabControl Grid.Row="1">

                <TabItem Header="StartPage.com">
                    <TabItem.Content>
                        <WebBrowser Source="http://www.startpage.com" />
                    </TabItem.Content>
                </TabItem>

                <TabItem Header="Google.com">
                    <TabItem.Content>
                        <WebBrowser Source="http://www.google.com" />
                    </TabItem.Content>
                </TabItem>

                <TabItem Header="Bing.com">
                    <TabItem.Content>
                        <WebBrowser Source="http://www.google.com" />
                    </TabItem.Content>
                </TabItem>

            </TabControl>
        </Grid>
    </Border>
</Window>

截图:

Allows Transparency

最佳答案

我在整个网络上发现了一篇似乎被冗余复制和粘贴的帖子。该帖子帮助我解决了我的问题,但我决定对其进行改进并将其包装得更干净一些。此外,当我在 TabControl 中使用它时,帖子中的原始代码不起作用。

这是我找到的原始帖子的链接:

http://social.msdn.microsoft.com/Forums/en-US/41088e8f-800d-4a29-a693-d9edf1682c0f/c-wpf-winforms-web-browser-styling?forum=wpf

上述解决方案要求在 WindowsFormsHost 对象中创建一个承载 Windows 窗体 WebBrowser 控件的窗口,该对象实际上会将自身覆盖在边框控件之上。

这是我对解决方案的改进:

WebBrowserOverlayWindow.xaml:

<Window x:Class="AllowTransparencyIssue.WebBrowserOverlayWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WebBrowserOverlayWindow"
        xmlns:winForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"     
        WindowStyle="None"
        ShowInTaskbar="False"
        ResizeMode="NoResize">
    <WindowsFormsHost x:Name="wfh">
        <winForms:WebBrowser x:Name="wfBrowser" />
    </WindowsFormsHost>
</Window>

WebBrowserOverlayWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace AllowTransparencyIssue
{
    /// <summary>
    /// Interaction logic for WebBrowserOverlayWindow.xaml
    /// </summary>
    public partial class WebBrowserOverlayWindow : Window
    {
        public WebBrowserOverlayWindow()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty TargetElementProperty = DependencyProperty.Register("TargetElement", typeof(FrameworkElement), typeof(WebBrowserOverlayWindow), new PropertyMetadata(TargetElementPropertyChanged));
        public FrameworkElement TargetElement
        {
            get
            {
                return GetValue(TargetElementProperty) as FrameworkElement;
            }
            set
            {
                SetValue(TargetElementProperty, value);
            }
        }


        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(WebBrowserOverlayWindow), new PropertyMetadata(SourcePropertyChanged));
        public string Source
        {
            get
            {
                return GetValue(SourceProperty) as string;
            }
            set
            {
                SetValue(SourceProperty, value);
            }
        }
        private static void SourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var webBrowserOverlayWindow = sender as WebBrowserOverlayWindow;

            if (webBrowserOverlayWindow != null)
            {
                webBrowserOverlayWindow.wfBrowser.Navigate(args.NewValue as string);
            }
        }

        private static void TargetElementPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var oldTargetElement = args.OldValue as FrameworkElement;
            var webBrowserOverlayWindow = sender as WebBrowserOverlayWindow;
            var mainWindow = Window.GetWindow(webBrowserOverlayWindow.TargetElement);

            if (webBrowserOverlayWindow != null && mainWindow != null)
            {
                webBrowserOverlayWindow.Owner = mainWindow;
                webBrowserOverlayWindow.Owner.LocationChanged += webBrowserOverlayWindow.PositionAndResize;
                webBrowserOverlayWindow.TargetElement.LayoutUpdated += webBrowserOverlayWindow.PositionAndResize;

                if (oldTargetElement != null)
                    oldTargetElement.LayoutUpdated -= webBrowserOverlayWindow.PositionAndResize;

                webBrowserOverlayWindow.PositionAndResize(sender, new EventArgs());

                if (webBrowserOverlayWindow.TargetElement.IsVisible && webBrowserOverlayWindow.Owner.IsVisible)
                {
                    webBrowserOverlayWindow.Show();
                }

                webBrowserOverlayWindow.TargetElement.IsVisibleChanged += (x, y) =>
                {
                    if (webBrowserOverlayWindow.TargetElement.IsVisible && webBrowserOverlayWindow.Owner.IsVisible)
                    {
                        webBrowserOverlayWindow.Show();
                    }
                    else
                    {
                        webBrowserOverlayWindow.Hide();
                    }
                };
            }
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            Owner.LocationChanged -= PositionAndResize;
            if (TargetElement != null)
            {
                TargetElement.LayoutUpdated -= PositionAndResize;
            }
        }

        private void PositionAndResize(object sender, EventArgs e)
        {
            if (TargetElement != null && TargetElement.IsVisible)
            {
                var point = TargetElement.PointToScreen(new Point());
                Left = point.X;
                Top = point.Y;

                Height = TargetElement.ActualHeight;
                Width = TargetElement.ActualWidth;
            }
        }

    }
}

Generic.xaml ResourceDictionary(在“主题”文件夹中):

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:AllowTransparencyIssue">


    <Style TargetType="{x:Type local:TransparentWebBrowser}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TransparentWebBrowser}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

TransparentWebBrowser.cs(自定义控件):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace AllowTransparencyIssue
{

    public class TransparentWebBrowser : Control
    {

        private WebBrowserOverlayWindow _WebBrowserOverlayWindow;
        public static readonly DependencyProperty TargetElementProperty = DependencyProperty.Register("TargetElement", typeof(FrameworkElement), typeof(TransparentWebBrowser), new PropertyMetadata(TargetElementPropertyChanged));
        public FrameworkElement TargetElement
        {
            get
            {
                return GetValue(TargetElementProperty) as FrameworkElement;
            }
            set
            {
                SetValue(TargetElementProperty, value);
            }
        }

        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(TransparentWebBrowser), new PropertyMetadata(SourcePropertyChanged));
        public string Source
        {
            get
            {
                return GetValue(SourceProperty) as string;
            }
            set
            {
                SetValue(SourceProperty, value);
            }
        }

        private static void SourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var transparentWebBrowser = sender as TransparentWebBrowser;
            if (transparentWebBrowser != null)
            {
                transparentWebBrowser._WebBrowserOverlayWindow.Source = args.NewValue as string;
            }
        }

        private static void TargetElementPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var transparentWebBrowser = sender as TransparentWebBrowser;
            if (transparentWebBrowser != null)
            {
                transparentWebBrowser._WebBrowserOverlayWindow.TargetElement = args.NewValue as FrameworkElement;
            }
        }

        public TransparentWebBrowser()
        {
            _WebBrowserOverlayWindow = new WebBrowserOverlayWindow();

            //TODO: Figure out how to automatically set the TargetElement binding...

            //var targetElementBinding = new Binding();
            //var rs = new RelativeSource();
            //rs.AncestorType = typeof(Border);
            //targetElementBinding.RelativeSource = rs;

            //_WebBrowserOverlayWindow.SetBinding(TransparentWebBrowser.TargetElementProperty, targetElementBinding);


        }

        static TransparentWebBrowser()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TransparentWebBrowser), new FrameworkPropertyMetadata(typeof(TransparentWebBrowser)));
        }
    }
}

修改后的 MainWindow.xaml:

<Window x:Class="AllowTransparencyIssue.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:AllowTransparencyIssue"
        Title="MainWindow" 
        Height="500" 
        Width="700"
        AllowsTransparency="True"
        WindowStartupLocation="CenterScreen"
        WindowStyle="None"


        >
    <Border BorderBrush="Blue" BorderThickness="3">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>

            <Button Grid.Row="0" HorizontalAlignment="Right" Click="Button_Click">Close Window</Button>

            <TabControl Grid.Row="1">

                <TabControl.Items>
                    <TabItem Header="StartPage.com">
                        <TabItem.Content>
                            <Border>
                                <local:TransparentWebBrowser TargetElement="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}" Source="http://www.startpage.com" />
                            </Border>
                        </TabItem.Content>

                    </TabItem>

                    <TabItem Header="Google.com">
                        <TabItem.Content>
                            <Border>
                                <local:TransparentWebBrowser TargetElement="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}" Source="http://www.google.com" />
                            </Border>
                        </TabItem.Content>
                    </TabItem>

                    <TabItem Header="Bing.com">
                        <TabItem.Content>
                            <Border>
                                <local:TransparentWebBrowser TargetElement="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}" Source="http://www.bing.com" />
                            </Border>
                        </TabItem.Content>
                    </TabItem>

                </TabControl.Items>
            </TabControl>
        </Grid>
    </Border>
</Window>

证明此功能有效的屏幕截图: Final Screenshot

我最后的想法和我的回答问题是,任何人都可以弄清楚如何在“TransparentWebBrowser”类的实例构造函数中连接绑定(bind)代码,这样我就不必在 MainWindow 中显式设置 TargetElement 属性.xaml???

关于wpf - 如何解决 AllowsTransparency=true 的 WPF 窗口中的 ActiveX WebBrowser 缺陷,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23529824/

相关文章:

c# - 在弹出窗口中获取 IOC 容器

.net - WPF RichTextBox - 键入文本的格式

c# - 快速将 Bitmap 转换为 BitmapSource wpf

css - DotNnetNuke 6 对 css 类 "dnnFormRequired"的更改 所以它显示为红色星号?

asp.net - DNN 皮肤可以使用包含空白元素而不是 div 的内容 Pane 吗?

c# - 使用 INotifyPropertyChanged

wpf - 将标记扩展编写为嵌套元素

.net - 如何在 WPF DataGrid 中定义自己的列?

c# - 将 WPF (XAML) 控件转换为 XPS 文档

css - 全局 CSS 配色方案/皮肤