wpf overlay element on top of webview2

Element code source github CrissCross project

Two tool classes and a webview2 encapsulation class are required

// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
namespace WpfApp1
{
    /// <summary>
    /// Window Host.
    /// </summary>
    /// <typeparam name="TWindow">The type of the window to host.</typeparam>
    /// <seealso cref="HwndHost" />
    public class WindowHost<TWindow> : HwndHost
    where TWindow : Window, new()
    {
        private const int GWLSTYLE = -0x10;
        private const uint WSCHILD = 0x40000000u;

        /// <summary>
        /// Initializes a new instance of the <see cref="WindowHost{TWindow}" /> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="window">The window.</param>
        public WindowHost(string name, TWindow? window = null)
        {
            Window = window = new();
            Window.Name = name;
            Window.ResizeMode = ResizeMode.NoResize;
            Window.WindowStyle = WindowStyle.None;
            Window.ShowInTaskbar = false;
            Window.AllowsTransparency = true;
            Window.Background = Brushes.Transparent;
            Window.Show();
            WindowHandle = new WindowInteropHelper(Window).Handle;
        }

        /// <summary>
        /// Gets the window handle.
        /// </summary>
        /// <value>
        /// The window handle.
        /// </value>
        public IntPtr WindowHandle { get; }

        /// <summary>
        /// Gets the window.
        /// </summary>
        /// <value>
        /// The window.
        /// </value>
        public TWindow Window { get; }

        /// <summary>
        /// Closes this instance.
        /// </summary>
        public void Close()
        {
            Window.Close();
            DestroyWindowCore(new HandleRef(Window, IntPtr.Zero));
        }

        /// <summary>
        /// When overridden in a derived class, creates the window to be hosted.
        /// </summary>
        /// <param name="hwndParent">The window handle of the parent window.</param>
        /// <returns>
        /// The handle to the child Win32 window to create.
        /// </returns>
        protected override HandleRef BuildWindowCore(HandleRef hwndParent)
        {
            HandleRef href = default;

            if (WindowHandle != IntPtr.Zero)
            {
                _ = NativeMethods.SetWindowLong(WindowHandle, GWLSTYLE, WSCHILD);
                NativeMethods.SetParent(WindowHandle, hwndParent.Handle);
                href = new HandleRef(this, WindowHandle);
            }

            return href;
        }

        /// <summary>
        /// When overridden in a derived class, destroys the hosted window.
        /// </summary>
        /// <param name="hwnd">A structure that contains the window handle.</param>
        protected override void DestroyWindowCore(HandleRef hwnd)
        {
            if (WindowHandle != hwnd.Handle)
            {
                NativeMethods.SetParent(WindowHandle, hwnd.Handle);
            }
        }
    }
}
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Runtime.InteropServices;

namespace WpfApp1
{
    internal static class NativeMethods
    {
        [DllImport("user32.dll")]
        internal static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        [DllImport("user32.dll")]
        internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
    }
}
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Effects;

namespace WpfApp1
{

    /// <summary>
    /// Navigation Web View.
    /// </summary>
    /// <seealso cref="ContentControl" />
    /// <seealso cref="IDisposable" />
    [ToolboxItem(true)]
    public class WebView2Wpf : ContentControl, IDisposable
    {
        private static readonly DependencyPropertyKey CanGoBackPropertyKey = DependencyProperty.RegisterReadOnly(
            nameof(CanGoBack),
            typeof(bool),
            typeof(WebView2Wpf),
            new PropertyMetadata(false));

        private static readonly DependencyPropertyKey CanGoForwardPropertyKey = DependencyProperty.RegisterReadOnly(
            nameof(CanGoForward),
            typeof(bool),
            typeof(WebView2Wpf),
            new PropertyMetadata(false));

#pragma warning disable SA1202 // Elements should be ordered by access

        /// <summary>
        /// The WPF DependencyProperty which backs the Microsoft.Web.WebView2.Wpf.WebView2.CreationProperties property.
        /// </summary>
        public static readonly DependencyProperty CreationPropertiesProperty = DependencyProperty.Register(
            nameof(CreationProperties),
            typeof(CoreWebView2CreationProperties),
            typeof(WebView2Wpf),
            new PropertyMetadata(CreationPropertiesChanged));

        /// <summary>
        /// The WPF DependencyProperty which backs the Microsoft.Web.WebView2.Wpf.WebView2.Source property.
        /// </summary>
        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
            nameof(Source),
            typeof(Uri),
            typeof(WebView2Wpf),
            new PropertyMetadata(SourceChanged));

        /// <summary>
        /// The WPF DependencyProperty which backs the Microsoft.Web.WebView2.Wpf.WebView2.CanGoBack property.
        /// </summary>
        public static readonly DependencyProperty CanGoBackProperty = CanGoBackPropertyKey!.DependencyProperty;

        /// <summary>
        /// The WPF DependencyProperty which backs the Microsoft.Web.WebView2.Wpf.WebView2.CanGoForward property.
        /// </summary>
        public static readonly DependencyProperty CanGoForwardProperty = CanGoForwardPropertyKey!.DependencyProperty;

        /// <summary>
        /// The WPF DependencyProperty which backs the Microsoft.Web.WebView2.Wpf.WebView2.ZoomFactor property.
        /// </summary>
        public static readonly DependencyProperty ZoomFactorProperty = DependencyProperty.Register(
            nameof(ZoomFactor),
            typeof(double),
            typeof(WebView2Wpf));

        /// <summary>
        /// The navigate back is enabled property.
        /// </summary>
        public static new readonly DependencyProperty ContentProperty = DependencyProperty.Register(
            nameof(Content),
            typeof(object),
            typeof(WebView2Wpf),
            new PropertyMetadata(true, ContentChanged));
#pragma warning restore SA1202 // Elements should be ordered by access

        public readonly WebView2 _WebBrowser;
        private WindowHost<Window>? _windowHost;
        private bool _disposedValue;

        /// <summary>
        /// Initializes a new instance of the <see cref="WebView2Wpf"/> class.
        /// </summary>
        public WebView2Wpf()
        {
            _WebBrowser = new()
            {
                HorizontalAlignment = HorizontalAlignment.Stretch,
                VerticalAlignment = VerticalAlignment.Stretch,
            };
            Unloaded + = (s, e) => Dispose();
        }

        /// <summary>
        /// Gets accesses the complete functionality of the underlying Microsoft.Web.WebView2.Core.CoreWebView2
        /// COM API. Returns null until initialization has completed. See the Microsoft.Web.WebView2.Wpf.WebView2
        /// class documentation for an initialization overview.
        /// </summary>
        /// <value>
        /// The core web view2.
        /// </value>
        [Browsable(false)]
        public CoreWebView2 CoreWebView2 => _WebBrowser.CoreWebView2;

        /// <summary>
        /// Gets or sets the creation properties.
        /// </summary>
        /// <value>
        /// The creation properties.
        /// </value>
        [Category("Common")]
        public CoreWebView2CreationProperties CreationProperties
        {
            get => (CoreWebView2CreationProperties)GetValue(CreationPropertiesProperty);
            set => SetValue(CreationPropertiesProperty, value);
        }

        /// <summary>
        /// Gets or sets the source.
        /// </summary>
        /// <value>
        /// The source.
        /// </value>
        [Category("Common")]
        publicUriSource
        {
            get => (Uri)GetValue(SourceProperty);
            set => SetValue(SourceProperty, value);
        }

        /// <summary>
        /// Gets a value indicating whether this instance can go back.
        /// if the WebView can navigate to a previous page in the navigation
        /// history. Wrapper around the Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack
        /// property of Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2. If Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2
        /// isn't initialized yet then returns false.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance can go back; otherwise, <c>false</c>.
        /// </value>
        [Browsable(false)]
        public bool CanGoBack => _WebBrowser.CanGoBack;

        /// <summary>
        /// Gets a value indicating whether this instance can go forward.
        /// if the WebView can navigate to a next page in the navigation history.
        /// Wrapper around the Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward property
        /// of Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2. If Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2
        /// isn't initialized yet then returns false.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance can go forward; otherwise, <c>false</c>.
        /// </value>
        [Browsable(false)]
        public bool CanGoForward => _WebBrowser.CanGoForward;

        /// <summary>
        /// Gets or sets a value indicating whether [zoom factor].
        /// </summary>
        /// <value>
        /// <c>true</c> if [zoom factor]; otherwise, <c>false</c>.
        /// </value>
        [Category("Common")]
        public double ZoomFactor
        {
            get => (double)GetValue(ZoomFactorProperty);
            set => SetValue(ZoomFactorProperty, value);
        }

        /// <summary>
        /// Gets or sets the content of the XAML overlay />.
        /// </summary>
        [Bindable(true)]
        [Category("Content")]
        public new object Content
        {
            get => GetValue(ContentProperty);
            set => SetValue(ContentProperty, value);
        }

        /// <summary>
        /// Gets the opacity mask.
        /// </summary>
        /// <value>
        /// The opacity mask.
        /// </value>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new System.Windows.Media.Brush OpacityMask => _WebBrowser.OpacityMask;

        /// <summary>
        /// Gets the opacity.
        /// </summary>
        /// <value>
        /// The opacity.
        /// </value>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new double Opacity => _WebBrowser.Opacity;

        /// <summary>
        /// Gets the effect.
        /// </summary>
        /// <value>
        /// The effect.
        /// </value>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new Effect Effect => _WebBrowser.Effect;

        /// <summary>
        /// Gets the context menu.
        /// </summary>
        /// <value>
        /// The context menu.
        /// </value>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new ContextMenu ContextMenu => _WebBrowser.ContextMenu;

        /// <summary>
        /// Gets the focus visual style.
        /// </summary>
        /// <value>
        /// The focus visual style.
        /// </value>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new Style FocusVisualStyle => _WebBrowser.FocusVisualStyle;

        /// <summary>
        /// Gets the input scope.
        /// </summary>
        /// <value>
        /// The input scope.
        /// </value>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public new InputScope InputScope => _WebBrowser.InputScope;

        /// <summary>
        /// Navigates the WebView to the previous page in the navigation history. Equivalent
        /// to calling Microsoft.Web.WebView2.Core.CoreWebView2.GoBack on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2
        /// If CoreWebView2 hasn't been initialized yet then does nothing.
        /// </summary>
        public void GoBack() => _WebBrowser?.GoBack();

        /// <summary>
        /// Navigates the WebView to the next page in the navigation history. Equivalent
        /// to calling Microsoft.Web.WebView2.Core.CoreWebView2.GoForward on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2
        /// If CoreWebView2 hasn't been initialized yet then does nothing.
        /// </summary>
        public void GoForward() => _WebBrowser?.GoForward();

        /// <summary>
        /// Reloads the current page. Equivalent to calling Microsoft.Web.WebView2.Core.CoreWebView2.Reload
        /// on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2.
        /// </summary>
        public void Reload() => _WebBrowser?.Reload();

        /// <summary>
        /// Stops all navigations and pending resource fetches. Equivalent to calling Microsoft.Web.WebView2.Core.CoreWebView2.Stop
        /// on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2.
        /// </summary>
        public void Stop() => _WebBrowser?.Stop();

        /// <summary>
        /// Initiates a navigation to htmlContent as source HTML of a new document. Equivalent
        /// to calling Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)
        /// on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2.
        /// </summary>
        /// <param name="htmlContent">Content of the HTML.</param>
        public void NavigateToString(string htmlContent) => _WebBrowser?.NavigateToString(htmlContent);

        /// <summary>
        /// Executes JavaScript code from the javaScript parameter in the current top level
        /// document rendered in the WebView. Equivalent to calling Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)
        /// on Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2.
        /// </summary>
        /// <param name="javaScript">The java script.</param>
        /// <returns>A string.</returns>
        public async Task<string> ExecuteScriptAsync(string javaScript) => await _WebBrowser.ExecuteScriptAsync(javaScript);

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.FrameworkElement.Initialized" /> event. This method is invoked whenever <see cref="P:System.Windows.FrameworkElement.IsInitialized" /> is set to <see langword="true" /> internally.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Windows.RoutedEventArgs" /> that contains the event data.</param>
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            _windowHost = new(Name);
            _windowHost.Window.HorizontalAlignment = HorizontalAlignment;
            _windowHost.Window.VerticalAlignment = VerticalAlignment;
            _windowHost.Window.Content = Content;
            var layoutRoot = new Grid();
            layoutRoot.Children.Add(_WebBrowser);
            layoutRoot.Children.Add(_windowHost);
            base.Content = layoutRoot;
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposedValue)
            {
                if (disposing)
                {
                    _WebBrowser.Dispose();
                    _windowHost?.Close();
                    _windowHost?.Dispose();
                }

                _disposedValue = true;
            }
        }

        private static void CreationPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is WebView2Wpf browser & amp; & amp; e.NewValue is CoreWebView2CreationProperties creationProperties)
            {
                browser._WebBrowser.CreationProperties = creationProperties;
            }
        }

        private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is WebView2Wpf browser & amp; & amp; e.NewValue is Uri source)
            {
                browser._WebBrowser.Source = source;
            }
        }

        private static void ContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is WebView2Wpf browser & amp; & amp; browser._windowHost?.Window is not null)
            {
                browser._windowHost.Window.Content = e.NewValue;
            }
        }
    }
}

display effect

test code

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:wpf="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" >
    <Grid>
        <!--<wpf:WebView2 Source="http://www.baidu.com" />-->
        <local:WebView2Wpf x:Name="WebView2Wpf" Source="https://www.google.com" Loaded="WebView2Wpf_Loaded" Panel.ZIndex="-1">
            <Grid>
                <Button Content="I am on top of WebView2 control with Criscross" Height="50" Width="575" FontSize="25" HorizontalAlignment="Center"
                    Foreground="LimeGreen" />
            </Grid>
        </local:WebView2Wpf>
    </Grid>
</Window>

wpf background code

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 WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void WebView2Wpf_Loaded(object sender, RoutedEventArgs e)
        {
            WebView2Wpf._WebBrowser.Loaded + = _WebBrowser_Loaded; ;
        }

        private void _WebBrowser_Loaded(object sender, RoutedEventArgs e)
        {
            WebView2Wpf._WebBrowser.CoreWebView2InitializationCompleted + = _WebBrowser_CoreWebView2InitializationCompleted;
        }

        private void _WebBrowser_CoreWebView2InitializationCompleted(object? sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
        {

        }
    }
}

wpf overlay element on top of webview2