WPF创建不规则窗体时WebBrowser控件不显示的问题
最近有小伙伴需要在不规则窗体上放置WebBrowser控件,因为设置了WindowStyle="None" 和 AllowsTransparency="True"。
导致WebBrowser控件不显示。
界面代码如下所示:
1 <Window x:Class="WebBrowserDemo.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WebBrowserDemo" 7 mc:Ignorable="d" 8 Height="979" Width="1259" 9 WindowStyle="None" ResizeMode="NoResize" Background="Transparent" AllowsTransparency="True" 10 MouseLeftButtonDown="Window_MouseLeftButtonDown" Loaded="Window_Loaded"> 11 <Canvas> 12 <Image Source="background.png" Stretch="Fill" Width="1259" Height="979" /> 13 <WebBrowser x:Name="webBrowser" Width="521" Height="635" Canvas.Left="58" Canvas.Top="198" Address="https://www.baidu.com" HorizontalAlignment="Left" VerticalAlignment="Center"></cef:ChromiumWebBrowser> 14 </Canvas> 15 </Window>
预期效果如下:
但实际浏览器并不会显示出来。
导致这个问题的原因是因为空域(airspace)问题,因为WebBrowser并不是一个原生的WPF控件,而是一个Win32控件。
详细描述可以参考以下两个链接:
https://learn.microsoft.com/en-us/archive/blogs/changov/webbrowser-control-on-transparent-wpf-window
这里提供三个解决办法
1、将WebBrowser控件替换为CefSharp/WebView2等控件
这种方法最简单,几乎不用修改什么代码,缺点是老版本系统可能不兼容。
2、使用WindowsChrome
1 <WindowChrome.WindowChrome> 2 <WindowChrome GlassFrameThickness="-1"/> 3 </WindowChrome.WindowChrome>
需要配合 ResizeMode="CanMinimize" Background="Transparent" WindowStyle="None" 使用
完整界面代码如下:
1 <Window x:Class="WebBrowserDemo.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WebBrowserDemo" 7 mc:Ignorable="d" 8 ResizeMode="CanMinimize" Background="Transparent" WindowStyle="None" Height="979" Width="1259"> 9 <WindowChrome.WindowChrome> 10 <WindowChrome GlassFrameThickness="-1"/> 11 </WindowChrome.WindowChrome> 12 <Canvas> 13 <Canvas.Background> 14 <ImageBrush ImageSource="background.png" Stretch="Uniform"></ImageBrush> 15 </Canvas.Background> 16 <WebBrowser x:Name="webBrowser" Width="527" Height="501" Canvas.Left="54" Canvas.Top="249" Source="https://bing.com" HorizontalAlignment="Center" VerticalAlignment="Top"></WebBrowser> 17 </Canvas> 18 </Window>
3、将WebBrowser封装到一个独立的窗口,变成独立控件
这个方法来自stackoverflow上的一个老哥,链接如下:
- 首先我们新建一个Window
WebBrowserEx.xaml
1 <Window x:Class="WebBrowserDemo.Controls.WebBrowserEx" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WebBrowserDemo.Controls" 7 xmlns:winForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" 8 WindowStyle="None" 9 ShowInTaskbar="False" 10 ResizeMode="NoResize" Width="761" Height="444"> 11 <WindowsFormsHost x:Name="wfh"> 12 <winForms:WebBrowser x:Name="wfBrowser" /> 13 </WindowsFormsHost> 14 </Window>
- 后台代码如下
WebBrowserEx.xaml.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Data; 9 using System.Windows.Documents; 10 using System.Windows.Input; 11 using System.Windows.Media; 12 using System.Windows.Media.Imaging; 13 using System.Windows.Shapes; 14 15 namespace WebBrowserDemo.Controls 16 { 17 public partial class WebBrowserEx : Window 18 { 19 public WebBrowserEx() 20 { 21 InitializeComponent(); 22 } 23 24 public static readonly DependencyProperty TargetElementProperty = DependencyProperty.Register("TargetElement", typeof(FrameworkElement), typeof(WebBrowserEx), new PropertyMetadata(TargetElementPropertyChanged)); 25 public FrameworkElement TargetElement 26 { 27 get 28 { 29 return GetValue(TargetElementProperty) as FrameworkElement; 30 } 31 set 32 { 33 SetValue(TargetElementProperty, value); 34 } 35 } 36 37 38 public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(WebBrowserEx), new PropertyMetadata(SourcePropertyChanged)); 39 public string Source 40 { 41 get 42 { 43 return GetValue(SourceProperty) as string; 44 } 45 set 46 { 47 SetValue(SourceProperty, value); 48 } 49 } 50 private static void SourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 51 { 52 var webBrowserOverlayWindow = sender as WebBrowserEx; 53 54 if (webBrowserOverlayWindow != null) 55 { 56 webBrowserOverlayWindow.wfBrowser.Navigate(args.NewValue as string); 57 } 58 } 59 60 private static void TargetElementPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 61 { 62 var oldTargetElement = args.OldValue as FrameworkElement; 63 var webBrowserOverlayWindow = sender as WebBrowserEx; 64 var mainWindow = Window.GetWindow(webBrowserOverlayWindow.TargetElement); 65 66 if (webBrowserOverlayWindow != null && mainWindow != null) 67 { 68 webBrowserOverlayWindow.Owner = mainWindow; 69 webBrowserOverlayWindow.Owner.LocationChanged += webBrowserOverlayWindow.PositionAndResize; 70 webBrowserOverlayWindow.TargetElement.LayoutUpdated += webBrowserOverlayWindow.PositionAndResize; 71 72 if (oldTargetElement != null) 73 oldTargetElement.LayoutUpdated -= webBrowserOverlayWindow.PositionAndResize; 74 75 webBrowserOverlayWindow.PositionAndResize(sender, new EventArgs()); 76 77 if (webBrowserOverlayWindow.TargetElement.IsVisible && webBrowserOverlayWindow.Owner.IsVisible) 78 { 79 webBrowserOverlayWindow.Show(); 80 } 81 82 webBrowserOverlayWindow.TargetElement.IsVisibleChanged += (x, y) => 83 { 84 if (webBrowserOverlayWindow.TargetElement.IsVisible && webBrowserOverlayWindow.Owner.IsVisible) 85 { 86 webBrowserOverlayWindow.Show(); 87 } 88 else 89 { 90 webBrowserOverlayWindow.Hide(); 91 } 92 }; 93 } 94 } 95 96 protected override void OnClosed(EventArgs e) 97 { 98 base.OnClosed(e); 99 100 Owner.LocationChanged -= PositionAndResize; 101 if (TargetElement != null) 102 { 103 TargetElement.LayoutUpdated -= PositionAndResize; 104 } 105 } 106 107 private void PositionAndResize(object sender, EventArgs e) 108 { 109 if (TargetElement != null && TargetElement.IsVisible) 110 { 111 var point = TargetElement.PointToScreen(new Point()); 112 Left = point.X + 396; //这里可以控制位置 113 Top = point.Y + 326; //point是左上角0,0的位置 114 115 //Height = TargetElement.ActualHeight; //这里可以控制宽高 116 //Width = TargetElement.ActualWidth; 117 } 118 } 119 120 } 121 }
- 然后我们新建一个自定义控件
CustomWebBrowser.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Data; 9 using System.Windows.Documents; 10 using System.Windows.Input; 11 using System.Windows.Media; 12 using System.Windows.Media.Imaging; 13 using System.Windows.Navigation; 14 using System.Windows.Shapes; 15 16 namespace WebBrowserDemo.Controls 17 { 18 public class CustomWebBrowser : Control 19 { 20 21 private WebBrowserEx _WebBrowserOverlayWindow; 22 public static readonly DependencyProperty TargetElementProperty = DependencyProperty.Register("TargetElement", typeof(FrameworkElement), typeof(CustomWebBrowser), new PropertyMetadata(TargetElementPropertyChanged)); 23 public FrameworkElement TargetElement 24 { 25 get 26 { 27 return GetValue(TargetElementProperty) as FrameworkElement; 28 } 29 set 30 { 31 SetValue(TargetElementProperty, value); 32 } 33 } 34 35 public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(CustomWebBrowser), new PropertyMetadata(SourcePropertyChanged)); 36 public string Source 37 { 38 get 39 { 40 return GetValue(SourceProperty) as string; 41 } 42 set 43 { 44 SetValue(SourceProperty, value); 45 } 46 } 47 48 private static void SourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 49 { 50 var transparentWebBrowser = sender as CustomWebBrowser; 51 if (transparentWebBrowser != null) 52 { 53 transparentWebBrowser._WebBrowserOverlayWindow.Source = args.NewValue as string; 54 } 55 } 56 57 private static void TargetElementPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 58 { 59 var transparentWebBrowser = sender as CustomWebBrowser; 60 if (transparentWebBrowser != null) 61 { 62 transparentWebBrowser._WebBrowserOverlayWindow.TargetElement = args.NewValue as FrameworkElement; 63 } 64 } 65 66 public CustomWebBrowser() 67 { 68 _WebBrowserOverlayWindow = new WebBrowserEx(); 69 } 70 71 static CustomWebBrowser() 72 { 73 DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomWebBrowser), new FrameworkPropertyMetadata(typeof(CustomWebBrowser))); 74 } 75 } 76 }
- 自定义控件样式如下
1 <Style TargetType="{x:Type local:CustomWebBrowser}"> 2 <Setter Property="Template"> 3 <Setter.Value> 4 <ControlTemplate TargetType="{x:Type local:CustomWebBrowser}"> 5 <Border Background="{TemplateBinding Background}" 6 BorderBrush="{TemplateBinding BorderBrush}" 7 BorderThickness="{TemplateBinding BorderThickness}"> 8 </Border> 9 </ControlTemplate> 10 </Setter.Value> 11 </Setter> 12 </Style>
- 使用时将WebBrowser控件换成CustomWebBrowser即可。