最近有小夥伴需要在不規則窗體上放置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
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/technology-regions-overview?view=netframeworkdesktop-4.8&redirectedfrom=MSDN
這裡提供三個解決辦法
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上的一個老哥,連結如下:
https://stackoverflow.com/questions/23529824/how-do-i-work-around-the-activex-webbrowser-flaw-in-a-wpf-window-that-allowstran
- 首先我們新建一個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即可。
示例程式碼