diff --git a/CefSharp.Wpf.HwndHost.Example/App.config b/CefSharp.Wpf.HwndHost.Example/App.config
new file mode 100644
index 0000000000..1e8988739d
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CefSharp.Wpf.HwndHost.Example/App.xaml b/CefSharp.Wpf.HwndHost.Example/App.xaml
new file mode 100644
index 0000000000..4f5c756edb
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/App.xaml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/CefSharp.Wpf.HwndHost.Example/App.xaml.cs b/CefSharp.Wpf.HwndHost.Example/App.xaml.cs
new file mode 100644
index 0000000000..d2d4a5c6a8
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/App.xaml.cs
@@ -0,0 +1,62 @@
+// Copyright © 2019 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
+using System.IO;
+using System.Windows;
+using CefSharp.Wpf.HwndHost.Handler;
+
+namespace CefSharp.Wpf.HwndHost.Example
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ public App()
+ {
+#if !NETCOREAPP3_1_OR_GREATER
+ CefRuntime.SubscribeAnyCpuAssemblyResolver();
+#endif
+ }
+
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ var settings = new CefSettings()
+ {
+ //By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
+ CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache")
+ };
+
+ //Example of setting a command line argument
+ //Enables WebRTC
+ // - CEF Doesn't currently support permissions on a per browser basis see https://bitbucket.org/chromiumembedded/cef/issues/2582/allow-run-time-handling-of-media-access
+ // - CEF Doesn't currently support displaying a UI for media access permissions
+ //
+ //NOTE: WebRTC Device Id's aren't persisted as they are in Chrome see https://bitbucket.org/chromiumembedded/cef/issues/2064/persist-webrtc-deviceids-across-restart
+ settings.CefCommandLineArgs.Add("enable-media-stream");
+ //https://peter.sh/experiments/chromium-command-line-switches/#use-fake-ui-for-media-stream
+ settings.CefCommandLineArgs.Add("use-fake-ui-for-media-stream");
+ //For screen sharing add (see https://bitbucket.org/chromiumembedded/cef/issues/2582/allow-run-time-handling-of-media-access#comment-58677180)
+ settings.CefCommandLineArgs.Add("enable-usermedia-screen-capturing");
+
+ //See https://github.com/cefsharp/CefSharp/wiki/General-Usage#multithreadedmessageloop
+ //The default is true
+ const bool multiThreadedMessageLoop = true;
+
+ IBrowserProcessHandler browserProcessHandler = null;
+
+ if(!multiThreadedMessageLoop)
+ {
+ settings.MultiThreadedMessageLoop = false;
+ browserProcessHandler = new IntegratedMessageLoopBrowserProcessHandler(Dispatcher);
+ }
+
+ // Make sure you set performDependencyCheck false
+ Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: browserProcessHandler);
+
+ base.OnStartup(e);
+ }
+ }
+}
diff --git a/CefSharp.Wpf.HwndHost.Example/AssemblyInfo.cs b/CefSharp.Wpf.HwndHost.Example/AssemblyInfo.cs
new file mode 100644
index 0000000000..ed33db9bfa
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/AssemblyInfo.cs
@@ -0,0 +1,14 @@
+// Copyright © 2019 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CefSharp.Wpf.HwndHost.Example/Behaviours/HoverLinkBehaviour.cs b/CefSharp.Wpf.HwndHost.Example/Behaviours/HoverLinkBehaviour.cs
new file mode 100644
index 0000000000..1fc77b3656
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/Behaviours/HoverLinkBehaviour.cs
@@ -0,0 +1,34 @@
+using System.Windows;
+using System;
+using Microsoft.Xaml.Behaviors;
+
+namespace CefSharp.Wpf.HwndHost.Example.Behaviours
+{
+ public class HoverLinkBehaviour : Behavior
+ {
+ // Using a DependencyProperty as the backing store for HoverLink. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty HoverLinkProperty = DependencyProperty.Register("HoverLink", typeof(string), typeof(HoverLinkBehaviour), new PropertyMetadata(string.Empty));
+
+ public string HoverLink
+ {
+ get { return (string)GetValue(HoverLinkProperty); }
+ set { SetValue(HoverLinkProperty, value); }
+ }
+
+ protected override void OnAttached()
+ {
+ AssociatedObject.StatusMessage += OnStatusMessageChanged;
+ }
+
+ protected override void OnDetaching()
+ {
+ AssociatedObject.StatusMessage -= OnStatusMessageChanged;
+ }
+
+ private void OnStatusMessageChanged(object sender, StatusMessageEventArgs e)
+ {
+ var chromiumWebBrowser = sender as ChromiumWebBrowser;
+ chromiumWebBrowser.Dispatcher.BeginInvoke((Action)(() => HoverLink = e.Value));
+ }
+ }
+}
diff --git a/CefSharp.Wpf.HwndHost.Example/Behaviours/TextBoxBindingUpdateOnEnterBehaviour.cs b/CefSharp.Wpf.HwndHost.Example/Behaviours/TextBoxBindingUpdateOnEnterBehaviour.cs
new file mode 100644
index 0000000000..24ad3b4e7a
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/Behaviours/TextBoxBindingUpdateOnEnterBehaviour.cs
@@ -0,0 +1,28 @@
+using System.Windows.Controls;
+using System.Windows.Input;
+using Microsoft.Xaml.Behaviors;
+
+namespace CefSharp.Wpf.HwndHost.Example.Behaviours
+{
+ public class TextBoxBindingUpdateOnEnterBehaviour : Behavior
+ {
+ protected override void OnAttached()
+ {
+ AssociatedObject.KeyDown += OnTextBoxKeyDown;
+ }
+
+ protected override void OnDetaching()
+ {
+ AssociatedObject.KeyDown -= OnTextBoxKeyDown;
+ }
+
+ private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter)
+ {
+ var txtBox = sender as TextBox;
+ txtBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
+ }
+ }
+ }
+}
diff --git a/CefSharp.Wpf.HwndHost.Example/CefSharp.Wpf.HwndHost.Example.csproj b/CefSharp.Wpf.HwndHost.Example/CefSharp.Wpf.HwndHost.Example.csproj
new file mode 100644
index 0000000000..fea51297f1
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/CefSharp.Wpf.HwndHost.Example.csproj
@@ -0,0 +1,50 @@
+
+
+ net472
+ WinExe
+ x86;x64
+ false
+ true
+ AllRules.ruleset
+ app.manifest
+ CefSharp.Wpf.HwndHost.Example.App
+ false
+ win7-x86;win7-x64
+ 9.0
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CefSharp.Wpf.HwndHost.Example/CefSharp.Wpf.HwndHost.Example.netcore.csproj b/CefSharp.Wpf.HwndHost.Example/CefSharp.Wpf.HwndHost.Example.netcore.csproj
new file mode 100644
index 0000000000..5ce3208620
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/CefSharp.Wpf.HwndHost.Example.netcore.csproj
@@ -0,0 +1,67 @@
+
+
+
+
+ obj.netcore\
+ bin.netcore\
+
+
+
+
+
+ WinExe
+ netcoreapp3.1;net5.0-windows
+ $(TargetFrameworks);net6.0-windows
+ CefSharp.Wpf.HwndHost.Example
+ CefSharp.Wpf.HwndHost.Example
+ true
+ false
+ app.manifest
+ MinimumRecommendedRules.ruleset
+ x86;x64;arm64
+
+ arm64
+ CefSharp.Wpf.HwndHost.Example.App
+
+ Major
+ win-x86;win-x64
+ win-x86
+ win-x64
+ win-arm64
+ false
+ 9.0
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CefSharp.Wpf.HwndHost.Example/Converter/EnvironmentConverter.cs b/CefSharp.Wpf.HwndHost.Example/Converter/EnvironmentConverter.cs
new file mode 100644
index 0000000000..a5564df204
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/Converter/EnvironmentConverter.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace CefSharp.Wpf.HwndHost.Example.Converter
+{
+ public class EnvironmentConverter : IValueConverter
+ {
+ object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Environment.Is64BitProcess ? "x64" : "x86";
+ }
+
+ object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Binding.DoNothing;
+ }
+ }
+}
diff --git a/CefSharp.Wpf.HwndHost.Example/Converter/TitleConverter.cs b/CefSharp.Wpf.HwndHost.Example/Converter/TitleConverter.cs
new file mode 100644
index 0000000000..60333176e4
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/Converter/TitleConverter.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace CefSharp.Wpf.HwndHost.Example.Converter
+{
+ public class TitleConverter : IValueConverter
+ {
+ object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return "CefSharp.MinimalExample.Wpf.HwndHost - " + (value ?? "No Title Specified");
+ }
+
+ object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Binding.DoNothing;
+ }
+ }
+}
diff --git a/CefSharp.Wpf.HwndHost.Example/Handlers/DisplayHandler.cs b/CefSharp.Wpf.HwndHost.Example/Handlers/DisplayHandler.cs
new file mode 100644
index 0000000000..48b8f6c812
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/Handlers/DisplayHandler.cs
@@ -0,0 +1,53 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace CefSharp.Wpf.HwndHost.Example.Handlers
+{
+ public class DisplayHandler : CefSharp.Handler.DisplayHandler
+ {
+ private Border parent;
+ private Window fullScreenWindow;
+
+ protected override void OnFullscreenModeChange(IWebBrowser chromiumWebBrowser, IBrowser browser, bool fullscreen)
+ {
+ var webBrowser = (ChromiumWebBrowser)chromiumWebBrowser;
+
+ _ = webBrowser.Dispatcher.InvokeAsync(() =>
+ {
+ if (fullscreen)
+ {
+ //In this example the parent is a Border, if your parent is a different type
+ //of control then update this code accordingly.
+ parent = (Border)VisualTreeHelper.GetParent(webBrowser);
+
+ //NOTE: If the ChromiumWebBrowser instance doesn't have a direct reference to
+ //the DataContext in this case the BrowserTabViewModel then your bindings won't
+ //be updated/might cause issues like the browser reloads the Url when exiting
+ //fullscreen.
+ parent.Child = null;
+
+ fullScreenWindow = new Window
+ {
+ WindowStyle = WindowStyle.None,
+ WindowState = WindowState.Maximized,
+ Content = webBrowser
+ };
+ fullScreenWindow.Loaded += (_,_) => webBrowser.Focus();
+
+ fullScreenWindow.ShowDialog();
+ }
+ else
+ {
+ fullScreenWindow.Content = null;
+
+ parent.Child = webBrowser;
+
+ fullScreenWindow.Close();
+ fullScreenWindow = null;
+ parent = null;
+ }
+ });
+ }
+ }
+}
diff --git a/CefSharp.Wpf.HwndHost.Example/MainWindow.xaml b/CefSharp.Wpf.HwndHost.Example/MainWindow.xaml
new file mode 100644
index 0000000000..d775e488aa
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/MainWindow.xaml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Chromium: , CEF: , CefSharp: , Environment:
+
+
+
+
+
diff --git a/CefSharp.Wpf.HwndHost.Example/MainWindow.xaml.cs b/CefSharp.Wpf.HwndHost.Example/MainWindow.xaml.cs
new file mode 100644
index 0000000000..b9be246f33
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/MainWindow.xaml.cs
@@ -0,0 +1,26 @@
+using System.Windows;
+using CefSharp.Wpf.HwndHost.Example.Handlers;
+
+namespace CefSharp.Wpf.HwndHost.Example
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ Browser.DisplayHandler = new DisplayHandler();
+ }
+
+ private void ShowDevToolsClick(object sender, RoutedEventArgs e)
+ {
+ if (Browser.IsBrowserInitialized)
+ {
+ Browser.ShowDevTools();
+ }
+ }
+ }
+}
diff --git a/CefSharp.Wpf.HwndHost.Example/TabbedMainWindow.xaml b/CefSharp.Wpf.HwndHost.Example/TabbedMainWindow.xaml
new file mode 100644
index 0000000000..2f39878402
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/TabbedMainWindow.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/CefSharp.Wpf.HwndHost.Example/TabbedMainWindow.xaml.cs b/CefSharp.Wpf.HwndHost.Example/TabbedMainWindow.xaml.cs
new file mode 100644
index 0000000000..0010c81581
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/TabbedMainWindow.xaml.cs
@@ -0,0 +1,41 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace CefSharp.Wpf.HwndHost.Example
+{
+ ///
+ /// Interaction logic for TabbedMainWindow.xaml
+ ///
+ public partial class TabbedMainWindow : Window
+ {
+ public TabbedMainWindow()
+ {
+ InitializeComponent();
+
+ NewTab();
+ NewTab();
+ NewTab();
+ }
+
+ private void NewTab()
+ {
+ var browser = new ChromiumWebBrowser("https://example.com/");
+ browser.Address = "https://example.com/";
+ Tabs.Items.Add(new TabItem { Header = "Tab", Content = browser });
+ }
+
+ private void OnNewTabButtonClick(object sender, RoutedEventArgs e)
+ {
+ NewTab();
+ }
+
+ private void OnCloseTabButtonClick(object sender, RoutedEventArgs e)
+ {
+ var Tab = (TabItem)Tabs.Items[Tabs.SelectedIndex];
+ var browser = (ChromiumWebBrowser)Tab.Content;
+ Tab.Content = null;
+ browser.Dispose();
+ Tabs.Items.Remove(Tab);
+ }
+ }
+}
diff --git a/CefSharp.Wpf.HwndHost.Example/app.manifest b/CefSharp.Wpf.HwndHost.Example/app.manifest
new file mode 100644
index 0000000000..8db82df647
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/app.manifest
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PerMonitorV2
+ true/PM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CefSharp.Wpf.HwndHost.Example/crash_reporter.cfg b/CefSharp.Wpf.HwndHost.Example/crash_reporter.cfg
new file mode 100644
index 0000000000..de315caef9
--- /dev/null
+++ b/CefSharp.Wpf.HwndHost.Example/crash_reporter.cfg
@@ -0,0 +1,12 @@
+# Crash reporting is configured using an INI-style config file named
+# "crash_reporter.cfg".
+# This file **must** be placed next to the main application executable.
+# Comments start with a hash character and must be on their own line.
+# See https://bitbucket.org/chromiumembedded/cef/wiki/CrashReporting.md
+# for further details
+
+[Config]
+ProductName=CefSharp.MinimalExample.Wpf.HwndHost
+ProductVersion=1.0.0
+AppName=CefSharp.MinimalExample
+ExternalHandler=CefSharp.BrowserSubprocess.exe
\ No newline at end of file
diff --git a/CefSharp.Wpf/ChromiumWebBrowser.cs b/CefSharp.Wpf/ChromiumWebBrowser.cs
index 493b55532e..7c1c9732e8 100644
--- a/CefSharp.Wpf/ChromiumWebBrowser.cs
+++ b/CefSharp.Wpf/ChromiumWebBrowser.cs
@@ -3,7 +3,6 @@
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
using System;
-using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
diff --git a/CefSharp.Wpf/HwndHost/CefSettings.cs b/CefSharp.Wpf/HwndHost/CefSettings.cs
new file mode 100644
index 0000000000..01b1d0d551
--- /dev/null
+++ b/CefSharp.Wpf/HwndHost/CefSettings.cs
@@ -0,0 +1,26 @@
+// Copyright © 2019 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+namespace CefSharp.Wpf.HwndHost
+{
+ ///
+ /// Initialization settings. Many of these and other settings can also configured using command-line switches.
+ ///
+ public class CefSettings : CefSettingsBase
+ {
+ ///
+ /// Intialize with default values
+ ///
+ public CefSettings() : base()
+ {
+ // CEF doesn't call GetAuthCredentials unless the Chrome login prompt is disabled
+ // https://github.com/chromiumembedded/cef/issues/3603
+ CefCommandLineArgs.Add("disable-chrome-login-prompt");
+
+ // Disable "Restore pages" popup after incorrect shutdown
+ // https://github.com/chromiumembedded/cef/issues/3767
+ CefCommandLineArgs.Add("hide-crash-restore-bubble");
+ }
+ }
+}
diff --git a/CefSharp.Wpf/HwndHost/ChromiumWebBrowser.cs b/CefSharp.Wpf/HwndHost/ChromiumWebBrowser.cs
new file mode 100644
index 0000000000..ef20394efd
--- /dev/null
+++ b/CefSharp.Wpf/HwndHost/ChromiumWebBrowser.cs
@@ -0,0 +1,1971 @@
+// Copyright © 2019 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Threading;
+using CefSharp.DevTools.Page;
+using CefSharp.Internals;
+using CefSharp.Structs;
+using CefSharp.Wpf.HwndHost.Internals;
+
+namespace CefSharp.Wpf.HwndHost
+{
+ ///
+ /// ChromiumWebBrowser is the WPF web browser control
+ ///
+ ///
+ ///
+ /// based on https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/walkthrough-hosting-a-win32-control-in-wpf
+ /// and https://stackoverflow.com/questions/6500336/custom-dwm-drawn-window-frame-flickers-on-resizing-if-the-window-contains-a-hwnd/17471534#17471534
+ public class ChromiumWebBrowser : System.Windows.Interop.HwndHost, IWebBrowserInternal, IWpfWebBrowser
+ {
+ public const string BrowserNotInitializedExceptionErrorMessage =
+ "The ChromiumWebBrowser instance creates the underlying Chromium Embedded Framework (CEF) browser instance in an async fashion. " +
+ "The undelying CefBrowser instance is not yet initialized. Use the IsBrowserInitializedChanged event and check " +
+ "the IsBrowserInitialized property to determine when the browser has been initialized.";
+ private const string CefInitializeFailedErrorMessage = "Cef.Initialize() failed.Check the log file see https://github.com/cefsharp/CefSharp/wiki/Trouble-Shooting#log-file for details.";
+ private const string CefIsInitializedFalseErrorMessage = "Cef.IsInitialized was false!.Check the log file for errors!. See https://github.com/cefsharp/CefSharp/wiki/Trouble-Shooting#log-file for details.";
+
+ [DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
+ private static extern IntPtr CreateWindowEx(int dwExStyle,
+ string lpszClassName,
+ string lpszWindowName,
+ int style,
+ int x, int y,
+ int width, int height,
+ IntPtr hwndParent,
+ IntPtr hMenu,
+ IntPtr hInst,
+ [MarshalAs(UnmanagedType.AsAny)] object pvParam);
+
+ [DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
+ private static extern bool DestroyWindow(IntPtr hwnd);
+
+ [DllImport("user32.dll", EntryPoint = "GetWindowLong")]
+ private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int index);
+
+ [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
+ private static extern int SetWindowLong32(HandleRef hWnd, int index, int dwNewLong);
+
+ [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
+ private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int index, IntPtr dwNewLong);
+
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptra
+ //SetWindowLongPtr for x64, SetWindowLong for x86
+ private void RemoveExNoActivateStyle(IntPtr hwnd)
+ {
+ var exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
+
+ if (IntPtr.Size == 8)
+ {
+ if ((exStyle.ToInt64() & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
+ {
+ exStyle = new IntPtr(exStyle.ToInt64() & ~WS_EX_NOACTIVATE);
+ //Remove WS_EX_NOACTIVATE
+ SetWindowLongPtr64(new HandleRef(this, hwnd), GWL_EXSTYLE, exStyle);
+ }
+ }
+ else
+ {
+ if ((exStyle.ToInt32() & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
+ {
+ //Remove WS_EX_NOACTIVATE
+ SetWindowLong32(new HandleRef(this, hwnd), GWL_EXSTYLE, (int)(exStyle.ToInt32() & ~WS_EX_NOACTIVATE));
+ }
+ }
+ }
+
+ private const int WS_CHILD = 0x40000000,
+ WS_VISIBLE = 0x10000000,
+ LBS_NOTIFY = 0x00000001,
+ HOST_ID = 0x00000002,
+ LISTBOX_ID = 0x00000001,
+ WS_VSCROLL = 0x00200000,
+ WS_BORDER = 0x00800000,
+ WS_CLIPCHILDREN = 0x02000000;
+
+ private const uint WS_EX_NOACTIVATE = 0x08000000;
+ private const int GWL_EXSTYLE = -20;
+
+ ///
+ /// Handle we'll use to host the browser
+ ///
+ private IntPtr hwndHost;
+ ///
+ /// The managed cef browser adapter
+ ///
+ private IBrowserAdapter managedCefBrowserAdapter;
+ ///
+ /// The ignore URI change
+ ///
+ private bool ignoreUriChange;
+ ///
+ /// Initial address
+ ///
+ private string initialAddress;
+ ///
+ /// Used to stop multiple threads trying to load the initial Url multiple times.
+ /// If the Address property is bound after the browser is initialized
+ ///
+ private bool initialLoadCalled;
+ ///
+ /// Has the underlying Cef Browser been created (slightly different to initliazed in that
+ /// the browser is initialized in an async fashion)
+ ///
+ private bool browserCreated;
+ ///
+ /// The browser initialized - boolean represented as 0 (false) and 1(true) as we use Interlocker to increment/reset
+ ///
+ private int browserInitialized;
+ ///
+ /// The browser
+ ///
+ private IBrowser browser;
+ ///
+ /// Browser initialization settings
+ ///
+ private IBrowserSettings browserSettings;
+ ///
+ /// The request context (we deliberately use a private variable so we can throw an exception if
+ /// user attempts to set after browser created)
+ ///
+ private IRequestContext requestContext;
+ ///
+ /// A flag that indicates whether or not the designer is active
+ /// NOTE: Needs to be static for OnApplicationExit
+ ///
+ private static bool DesignMode;
+
+ ///
+ /// The value for disposal, if it's 1 (one) then this instance is either disposed
+ /// or in the process of getting disposed
+ ///
+ private int disposeSignaled;
+
+ ///
+ /// If true the the WS_EX_NOACTIVATE style will be removed so that future mouse clicks
+ /// inside the browser correctly activate and focus the window.
+ ///
+ private bool removeExNoActivateStyle;
+
+ ///
+ /// Current DPI Scale
+ ///
+ private double dpiScale;
+
+ ///
+ /// The HwndSource RootVisual (Window) - We store a reference
+ /// to unsubscribe event handlers
+ ///
+ private Window sourceWindow;
+
+ ///
+ /// Store the previous window state, used to determine if the
+ /// Windows was previous
+ /// and resume rendering
+ ///
+ private WindowState previousWindowState;
+
+ ///
+ /// This flag is set when the browser gets focus before the underlying CEF browser
+ /// has been initialized.
+ ///
+ private bool initialFocus;
+
+ ///
+ /// Initial browser load task complection source
+ ///
+ private TaskCompletionSource initialLoadTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ ///
+ /// Initial browser load action
+ ///
+ private Action initialLoadAction;
+
+ ///
+ /// Activates browser upon creation, the default value is false. Prior to version 73
+ /// the default behaviour was to activate browser on creation (Equivilent of setting this property to true).
+ /// To restore this behaviour set this value to true immediately after you create the instance.
+ /// https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window
+ ///
+ public bool ActivateBrowserOnCreation { get; set; }
+
+ ///
+ /// Gets a value indicating whether this instance is disposed.
+ ///
+ /// if this instance is disposed; otherwise, .
+ public bool IsDisposed
+ {
+ get
+ {
+ return Interlocked.CompareExchange(ref disposeSignaled, 1, 1) == 1;
+ }
+ }
+
+ ///
+ /// Gets or sets the browser settings.
+ ///
+ /// The browser settings.
+ public IBrowserSettings BrowserSettings
+ {
+ get { return browserSettings; }
+ set
+ {
+ if (browserCreated)
+ {
+ throw new Exception("Browser has already been created. BrowserSettings must be " +
+ "set before the underlying CEF browser is created.");
+ }
+
+ //New instance is created in the constructor, if you use
+ //xaml to initialize browser settings then it will also create a new
+ //instance, so we dispose of the old one
+ if (browserSettings != null && browserSettings.AutoDispose)
+ {
+ browserSettings.Dispose();
+ }
+
+ browserSettings = value;
+ }
+ }
+ ///
+ /// Gets or sets the request context.
+ ///
+ /// The request context.
+ public IRequestContext RequestContext
+ {
+ get { return requestContext; }
+ set
+ {
+ if (browserCreated)
+ {
+ throw new Exception("Browser has already been created. RequestContext must be " +
+ "set before the underlying CEF browser is created.");
+ }
+ if (value != null && value.GetType() != typeof(RequestContext))
+ {
+ throw new Exception(string.Format("RequestContxt can only be of type {0} or null", typeof(RequestContext)));
+ }
+ requestContext = value;
+ }
+ }
+ ///
+ /// Implement and assign to handle dialog events.
+ ///
+ /// The dialog handler.
+ public IDialogHandler DialogHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to JavaScript Dialogs.
+ ///
+ /// The js dialog handler.
+ public IJsDialogHandler JsDialogHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to key press.
+ ///
+ /// The keyboard handler.
+ public IKeyboardHandler KeyboardHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to browser requests.
+ ///
+ /// The request handler.
+ public IRequestHandler RequestHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to downloading files.
+ ///
+ /// The download handler.
+ public IDownloadHandler DownloadHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to browser load status.
+ ///
+ /// The load handler.
+ public ILoadHandler LoadHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to popups.
+ ///
+ /// The life span handler.
+ public ILifeSpanHandler LifeSpanHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to browser display state.
+ ///
+ /// The display handler.
+ public IDisplayHandler DisplayHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to the browser context menu
+ ///
+ /// The menu handler.
+ public IContextMenuHandler MenuHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to the browser component's focus
+ ///
+ /// The focus handler.
+ public IFocusHandler FocusHandler { get; set; }
+ ///
+ /// Implement and assign to handle events related to dragging.
+ ///
+ /// The drag handler.
+ public IDragHandler DragHandler { get; set; }
+ ///
+ /// Implement and control the loading of resources
+ ///
+ /// The resource handler factory.
+ public IResourceRequestHandlerFactory ResourceRequestHandlerFactory { get; set; }
+ ///
+ /// Implement and assign to handle messages from the render process.
+ ///
+ /// The render process message handler.
+ public IRenderProcessMessageHandler RenderProcessMessageHandler { get; set; }
+ ///
+ /// Implement to handle events related to find results.
+ ///
+ /// The find handler.
+ public IFindHandler FindHandler { get; set; }
+
+ ///
+ /// Implement to handle audio events.
+ ///
+ public IAudioHandler AudioHandler { get; set; }
+
+ ///
+ /// Implement to handle frame events.
+ ///
+ public IFrameHandler FrameHandler { get; set; }
+
+ ///
+ /// Implement to handle events related to permission requests.
+ ///
+ public IPermissionHandler PermissionHandler { get; set; }
+
+ ///
+ /// Event handler for receiving Javascript console messages being sent from web pages.
+ /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
+ /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
+ /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
+ /// (The exception to this is when your running with settings.MultiThreadedMessageLoop = false, then they'll be the same thread).
+ ///
+ public event EventHandler ConsoleMessage;
+
+ ///
+ /// Event handler for changes to the status message.
+ /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
+ /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.
+ /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
+ /// (The exception to this is when your running with settings.MultiThreadedMessageLoop = false, then they'll be the same thread).
+ ///
+ public event EventHandler StatusMessage;
+
+ ///
+ /// Event handler that will get called when the browser begins loading a frame. Multiple frames may be loading at the same
+ /// time. Sub-frames may start or continue loading after the main frame load has ended. This method may not be called for a
+ /// particular frame if the load request for that frame fails. For notification of overall browser load status use
+ /// OnLoadingStateChange instead.
+ /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
+ /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
+ /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
+ ///
+ /// Whilst this may seem like a logical place to execute js, it's called before the DOM has been loaded, implement
+ /// as it's called when the underlying V8Context is created
+ ///
+ public event EventHandler FrameLoadStart;
+
+ ///
+ /// Event handler that will get called when the browser is done loading a frame. Multiple frames may be loading at the same
+ /// time. Sub-frames may start or continue loading after the main frame load has ended. This method will always be called
+ /// for all frames irrespective of whether the request completes successfully.
+ /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
+ /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
+ /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
+ ///
+ public event EventHandler FrameLoadEnd;
+
+ ///
+ /// Event handler that will get called when the resource load for a navigation fails or is canceled.
+ /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
+ /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
+ /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
+ ///
+ public event EventHandler LoadError;
+
+ ///
+ /// Event handler that will get called when the Loading state has changed.
+ /// This event will be fired twice. Once when loading is initiated either programmatically or
+ /// by user action, and once when loading is terminated due to completion, cancellation of failure.
+ /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI
+ /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
+ /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
+ ///
+ public event EventHandler LoadingStateChanged;
+
+ ///
+ /// Event handler that will get called when the message that originates from CefSharp.PostMessage
+ ///
+ public event EventHandler JavascriptMessageReceived;
+
+ ///
+ /// Navigates to the previous page in the browser history. Will automatically be enabled/disabled depending on the
+ /// browser state.
+ ///
+ /// The back command.
+ public ICommand BackCommand { get; private set; }
+ ///
+ /// Navigates to the next page in the browser history. Will automatically be enabled/disabled depending on the
+ /// browser state.
+ ///
+ /// The forward command.
+ public ICommand ForwardCommand { get; private set; }
+ ///
+ /// Reloads the content of the current page. Will automatically be enabled/disabled depending on the browser state.
+ ///
+ /// The reload command.
+ public ICommand ReloadCommand { get; private set; }
+ ///
+ /// Prints the current browser contents.
+ ///
+ /// The print command.
+ public ICommand PrintCommand { get; private set; }
+ ///
+ /// Increases the zoom level.
+ ///
+ /// The zoom in command.
+ public ICommand ZoomInCommand { get; private set; }
+ ///
+ /// Decreases the zoom level.
+ ///
+ /// The zoom out command.
+ public ICommand ZoomOutCommand { get; private set; }
+ ///
+ /// Resets the zoom level to the default. (100%)
+ ///
+ /// The zoom reset command.
+ public ICommand ZoomResetCommand { get; private set; }
+ ///
+ /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web
+ /// page is shown.
+ ///
+ /// The view source command.
+ public ICommand ViewSourceCommand { get; private set; }
+ ///
+ /// Command which cleans up the Resources used by the ChromiumWebBrowser
+ ///
+ /// The cleanup command.
+ public ICommand CleanupCommand { get; private set; }
+ ///
+ /// Stops loading the current page.
+ ///
+ /// The stop command.
+ public ICommand StopCommand { get; private set; }
+ ///
+ /// Cut selected text to the clipboard.
+ ///
+ /// The cut command.
+ public ICommand CutCommand { get; private set; }
+ ///
+ /// Copy selected text to the clipboard.
+ ///
+ /// The copy command.
+ public ICommand CopyCommand { get; private set; }
+ ///
+ /// Paste text from the clipboard.
+ ///
+ /// The paste command.
+ public ICommand PasteCommand { get; private set; }
+ ///
+ /// Select all text.
+ ///
+ /// The select all command.
+ public ICommand SelectAllCommand { get; private set; }
+ ///
+ /// Undo last action.
+ ///
+ /// The undo command.
+ public ICommand UndoCommand { get; private set; }
+ ///
+ /// Redo last action.
+ ///
+ /// The redo command.
+ public ICommand RedoCommand { get; private set; }
+
+ ///
+ public ICommand ToggleAudioMuteCommand { get; private set; }
+
+ ///
+ /// Used as workaround for issue https://github.com/cefsharp/CefSharp/issues/3021
+ ///
+ private int canExecuteJavascriptInMainFrameChildProcessId;
+
+ ///
+ /// A flag that indicates if you can execute javascript in the main frame.
+ /// Flag is set to true in IRenderProcessMessageHandler.OnContextCreated.
+ /// and false in IRenderProcessMessageHandler.OnContextReleased
+ ///
+ public bool CanExecuteJavascriptInMainFrame { get; private set; }
+
+ ///
+ /// Initializes static members of the class.
+ ///
+ //TODO: Currently only a single Dispatcher is supported, all controls will
+ //be Shutdown on that dispatcher which may not actually be the correct Dispatcher.
+ //We could potentially use a ThreadStatic variable to implement this behaviour
+ //and support multiple dispatchers.
+ static ChromiumWebBrowser()
+ {
+ if (CefSharpSettings.ShutdownOnExit)
+ {
+ //Use Dispatcher.FromThread as it returns null if no dispatcher
+ //is available for this thread.
+ var dispatcher = Dispatcher.FromThread(Thread.CurrentThread);
+ if (dispatcher == null)
+ {
+ //No dispatcher then we'll rely on Application.Exit
+ var app = Application.Current;
+
+ if (app != null)
+ {
+ app.Exit += OnApplicationExit;
+ }
+ }
+ else
+ {
+ dispatcher.ShutdownStarted += DispatcherShutdownStarted;
+ dispatcher.ShutdownFinished += DispatcherShutdownFinished;
+ }
+ }
+ }
+
+ ///
+ /// Handles Dispatcher Shutdown
+ ///
+ /// sender
+ /// eventargs
+ private static void DispatcherShutdownStarted(object sender, EventArgs e)
+ {
+ var dispatcher = (Dispatcher)sender;
+
+ dispatcher.ShutdownStarted -= DispatcherShutdownStarted;
+
+ if (!DesignMode)
+ {
+ CefPreShutdown();
+ }
+ }
+
+ private static void DispatcherShutdownFinished(object sender, EventArgs e)
+ {
+ var dispatcher = (Dispatcher)sender;
+
+ dispatcher.ShutdownFinished -= DispatcherShutdownFinished;
+
+ if (!DesignMode)
+ {
+ CefShutdown();
+ }
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private static void OnApplicationExit(object sender, ExitEventArgs e)
+ {
+ if (!DesignMode)
+ {
+ CefShutdown();
+ }
+ }
+
+ ///
+ /// Required for designer support - this method cannot be inlined as the designer
+ /// will attempt to load libcef.dll and will subsequently throw an exception.
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void CefPreShutdown()
+ {
+ Cef.PreShutdown();
+ }
+
+ ///
+ /// Required for designer support - this method cannot be inlined as the designer
+ /// will attempt to load libcef.dll and will subsiquently throw an exception.
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void CefShutdown()
+ {
+ Cef.Shutdown();
+ }
+
+ ///
+ /// Initializes a new instance of the instance.
+ ///
+ /// Cef::Initialize() failed
+ public ChromiumWebBrowser()
+ {
+ DesignMode = System.ComponentModel.DesignerProperties.GetIsInDesignMode(this);
+
+ if (!DesignMode)
+ {
+ NoInliningConstructor();
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the instance.
+ ///
+ /// address to load initially
+ public ChromiumWebBrowser(string initialAddress)
+ {
+ this.initialAddress = initialAddress;
+
+ NoInliningConstructor();
+ }
+
+ ///
+ /// Constructor logic has been moved into this method
+ /// Required for designer support - this method cannot be inlined as the designer
+ /// will attempt to load libcef.dll and will subsiquently throw an exception.
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private void NoInliningConstructor()
+ {
+ InitializeCefInternal();
+
+ //Add this ChromiumWebBrowser instance to a list of IDisposable objects
+ // that if still alive at the time Cef.Shutdown is called will be disposed of
+ // It's important all browser instances be freed before Cef.Shutdown is called.
+ Cef.AddDisposable(this);
+ Focusable = true;
+ FocusVisualStyle = null;
+
+ WebBrowser = this;
+
+ SizeChanged += OnSizeChanged;
+ IsVisibleChanged += OnIsVisibleChanged;
+
+ BackCommand = new DelegateCommand(this.Back, () => CanGoBack);
+ ForwardCommand = new DelegateCommand(this.Forward, () => CanGoForward);
+ ReloadCommand = new DelegateCommand(this.Reload, () => !IsLoading);
+ PrintCommand = new DelegateCommand(this.Print);
+ ZoomInCommand = new DelegateCommand(ZoomIn);
+ ZoomOutCommand = new DelegateCommand(ZoomOut);
+ ZoomResetCommand = new DelegateCommand(ZoomReset);
+ ViewSourceCommand = new DelegateCommand(this.ViewSource);
+ CleanupCommand = new DelegateCommand(Dispose);
+ StopCommand = new DelegateCommand(this.Stop);
+ CutCommand = new DelegateCommand(this.Cut);
+ CopyCommand = new DelegateCommand(this.Copy);
+ PasteCommand = new DelegateCommand(this.Paste);
+ SelectAllCommand = new DelegateCommand(this.SelectAll);
+ UndoCommand = new DelegateCommand(this.Undo);
+ RedoCommand = new DelegateCommand(this.Redo);
+ ToggleAudioMuteCommand = new DelegateCommand(this.ToggleAudioMute);
+
+ managedCefBrowserAdapter = ManagedCefBrowserAdapter.Create(this, false);
+
+ browserSettings = new BrowserSettings(autoDispose:true);
+
+ PresentationSource.AddSourceChangedHandler(this, PresentationSourceChangedHandler);
+
+ FocusHandler = new FocusHandler();
+ LifeSpanHandler = new NoCloseLifespanHandler();
+
+ UseLayoutRounding = true;
+ }
+
+ private void PresentationSourceChangedHandler(object sender, SourceChangedEventArgs args)
+ {
+ if (args.NewSource != null)
+ {
+ var source = (HwndSource)args.NewSource;
+
+ var matrix = source.CompositionTarget.TransformToDevice;
+
+ dpiScale = matrix.M11;
+
+ var window = source.RootVisual as Window;
+ if (window != null)
+ {
+ window.StateChanged += OnWindowStateChanged;
+ window.LocationChanged += OnWindowLocationChanged;
+ sourceWindow = window;
+
+ if (CleanupElement == null)
+ {
+ CleanupElement = window;
+ }
+ else if (CleanupElement is Window parent)
+ {
+ //If the CleanupElement is a window then move it to the new Window
+ if (parent != window)
+ {
+ CleanupElement = window;
+ }
+ }
+ }
+ }
+ else if (args.OldSource != null)
+ {
+ var window = args.OldSource.RootVisual as Window;
+ if (window != null)
+ {
+ window.StateChanged -= OnWindowStateChanged;
+ window.LocationChanged -= OnWindowLocationChanged;
+ sourceWindow = null;
+ }
+ }
+ }
+
+ ///
+ protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
+ {
+ dpiScale = newDpi.DpiScaleX;
+
+ //If the DPI changed then we need to resize.
+ ResizeBrowser((int)ActualWidth, (int)ActualHeight);
+
+ base.OnDpiChanged(oldDpi, newDpi);
+ }
+
+ private void OnSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ ResizeBrowser((int)e.NewSize.Width, (int)e.NewSize.Height);
+ }
+
+ ///
+ protected override HandleRef BuildWindowCore(HandleRef hwndParent)
+ {
+ if (hwndHost == IntPtr.Zero)
+ {
+ hwndHost = CreateWindowEx(0, "static", "",
+ WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,
+ 0, 0,
+ (int)ActualWidth, (int)ActualHeight,
+ hwndParent.Handle,
+ (IntPtr)HOST_ID,
+ IntPtr.Zero,
+ 0);
+ }
+
+ CreateBrowser();
+
+ if (browserSettings.AutoDispose)
+ {
+ browserSettings.Dispose();
+ }
+
+ browserSettings = null;
+
+ // When CEF is integrated into main message loop
+ // We unregister the WPF keyboard KeyboardInputSite to make sure
+ // TAB works correctly
+ if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
+ {
+ var keyboardInputSite = ((IKeyboardInputSink)this).KeyboardInputSite;
+ if (keyboardInputSite != null)
+ {
+ ((IKeyboardInputSink)this).KeyboardInputSite = null;
+
+ keyboardInputSite.Unregister();
+ }
+ }
+
+ return new HandleRef(null, hwndHost);
+ }
+
+ ///
+ protected override void DestroyWindowCore(HandleRef hwnd)
+ {
+ DestroyWindow(hwnd.Handle);
+ }
+
+ ///
+ protected override bool TabIntoCore(TraversalRequest request)
+ {
+ if(InternalIsBrowserInitialized())
+ {
+ var host = browser.GetHost();
+ host.SetFocus(true);
+
+ return true;
+ }
+
+ return base.TabIntoCore(request);
+ }
+
+ ///
+ protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
+ {
+ if(!e.Handled)
+ {
+ if (InternalIsBrowserInitialized())
+ {
+ var host = browser.GetHost();
+ host.SetFocus(true);
+ }
+ else
+ {
+ initialFocus = true;
+ }
+ }
+
+ base.OnGotKeyboardFocus(e);
+ }
+
+ ///
+ protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
+ {
+ if (!e.Handled)
+ {
+ if (InternalIsBrowserInitialized())
+ {
+ var host = browser.GetHost();
+ host.SetFocus(false);
+ }
+ else
+ {
+ initialFocus = false;
+ }
+ }
+
+ base.OnLostKeyboardFocus(e);
+ }
+
+
+ ///
+ protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
+ {
+ const int WM_SETFOCUS = 0x0007;
+ const int WM_MOUSEACTIVATE = 0x0021;
+ switch (msg)
+ {
+ case WM_SETFOCUS:
+ case WM_MOUSEACTIVATE:
+ {
+ if(InternalIsBrowserInitialized())
+ {
+ var host = browser.GetHost();
+ host.SetFocus(true);
+
+ handled = true;
+
+ return IntPtr.Zero;
+ }
+ break;
+ }
+ }
+ return base.WndProc(hwnd, msg, wParam, lParam, ref handled);
+ }
+
+ ///
+ /// If not in design mode; Releases unmanaged and - optionally - managed resources for the
+ ///
+ /// to release both managed and unmanaged resources; to release only unmanaged resources.
+ protected override void Dispose(bool disposing)
+ {
+ // Attempt to move the disposeSignaled state from 0 to 1. If successful, we can be assured that
+ // this thread is the first thread to do so, and can safely dispose of the object.
+ if (Interlocked.CompareExchange(ref disposeSignaled, 1, 0) != 0)
+ {
+ return;
+ }
+
+ if (!DesignMode)
+ {
+ InternalDispose(disposing);
+ }
+
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// Releases unmanaged and - optionally - managed resources for the
+ ///
+ /// to release both managed and unmanaged resources; to release only unmanaged resources.
+ ///
+ /// This method cannot be inlined as the designer will attempt to load libcef.dll and will subsiquently throw an exception.
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private void InternalDispose(bool disposing)
+ {
+ Interlocked.Exchange(ref browserInitialized, 0);
+
+ if (disposing)
+ {
+ SizeChanged -= OnSizeChanged;
+ IsVisibleChanged -= OnIsVisibleChanged;
+
+ PresentationSource.RemoveSourceChangedHandler(this, PresentationSourceChangedHandler);
+ // Release window event listeners if PresentationSourceChangedHandler event wasn't
+ // fired before Dispose
+ if (sourceWindow != null)
+ {
+ sourceWindow.StateChanged -= OnWindowStateChanged;
+ sourceWindow.LocationChanged -= OnWindowLocationChanged;
+ sourceWindow = null;
+ }
+
+
+ UiThreadRunAsync(() =>
+ {
+ OnIsBrowserInitializedChanged(true, false);
+
+ //To Minic the WPF behaviour this happens after OnIsBrowserInitializedChanged
+ IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty);
+
+ WebBrowser = null;
+ });
+
+ // Don't maintain a reference to event listeners anylonger:
+ ConsoleMessage = null;
+ FrameLoadEnd = null;
+ FrameLoadStart = null;
+ IsBrowserInitializedChanged = null;
+ LoadError = null;
+ LoadingStateChanged = null;
+ StatusMessage = null;
+ TitleChanged = null;
+ JavascriptMessageReceived = null;
+
+ // Release reference to handlers, except LifeSpanHandler which we don't set to null
+ //so that ILifeSpanHandler.DoClose will not be invoked. Previously we set LifeSpanHandler = null
+ //after managedCefBrowserAdapter.Dispose, there's still cases where the LifeSpanHandler was null
+ //when DoClose was called Issue https://github.com/cefsharp/CefSharp.Wpf.HwndHost/issues/10
+ FindHandler = null;
+ DialogHandler = null;
+ RequestHandler = null;
+ DisplayHandler = null;
+ LoadHandler = null;
+ KeyboardHandler = null;
+ JsDialogHandler = null;
+ DragHandler = null;
+ DownloadHandler = null;
+ MenuHandler = null;
+ FocusHandler = null;
+ ResourceRequestHandlerFactory = null;
+ RenderProcessMessageHandler = null;
+
+ this.DisposeDevToolsContext();
+
+ browser = null;
+ BrowserCore = null;
+
+ if (CleanupElement != null)
+ {
+ CleanupElement.Unloaded -= OnCleanupElementUnloaded;
+ }
+
+ managedCefBrowserAdapter?.Dispose();
+ managedCefBrowserAdapter = null;
+ }
+
+ Cef.RemoveDisposable(this);
+ }
+
+ ///
+ /// Sets the address.
+ ///
+ /// The instance containing the event data.
+ void IWebBrowserInternal.SetAddress(AddressChangedEventArgs args)
+ {
+ UiThreadRunAsync(() =>
+ {
+ ignoreUriChange = true;
+ SetCurrentValue(AddressProperty, args.Address);
+ ignoreUriChange = false;
+
+ // The tooltip should obviously also be reset (and hidden) when the address changes.
+ SetCurrentValue(TooltipTextProperty, null);
+ });
+ }
+
+ ///
+ /// Sets the loading state change.
+ ///
+ /// The instance containing the event data.
+ void IWebBrowserInternal.SetLoadingStateChange(LoadingStateChangedEventArgs args)
+ {
+ if (removeExNoActivateStyle && InternalIsBrowserInitialized())
+ {
+ removeExNoActivateStyle = false;
+
+ var host = this.GetBrowserHost();
+ var hwnd = host.GetWindowHandle();
+ //Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside the
+ //browser correctly activate and focus the browser.
+ //https://github.com/chromiumembedded/cef/blob/9df4a54308a88fd80c5774d91c62da35afb5fd1b/tests/cefclient/browser/root_window_win.cc#L1088
+ RemoveExNoActivateStyle(hwnd);
+ }
+
+ UiThreadRunAsync(() =>
+ {
+ SetCurrentValue(CanGoBackProperty, args.CanGoBack);
+ SetCurrentValue(CanGoForwardProperty, args.CanGoForward);
+ SetCurrentValue(IsLoadingProperty, args.IsLoading);
+
+ ((DelegateCommand)BackCommand).RaiseCanExecuteChanged();
+ ((DelegateCommand)ForwardCommand).RaiseCanExecuteChanged();
+ ((DelegateCommand)ReloadCommand).RaiseCanExecuteChanged();
+ });
+
+ LoadingStateChanged?.Invoke(this, args);
+
+ initialLoadAction?.Invoke(args.IsLoading, null);
+ }
+
+ ///
+ /// Sets the title.
+ ///
+ /// The instance containing the event data.
+ void IWebBrowserInternal.SetTitle(TitleChangedEventArgs args)
+ {
+ UiThreadRunAsync(() => SetCurrentValue(TitleProperty, args.Title));
+ }
+
+ ///
+ /// Sets the tooltip text.
+ ///
+ /// The tooltip text.
+ void IWebBrowserInternal.SetTooltipText(string tooltipText)
+ {
+ UiThreadRunAsync(() => SetCurrentValue(TooltipTextProperty, tooltipText));
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The instance containing the event data.
+ void IWebBrowserInternal.OnFrameLoadStart(FrameLoadStartEventArgs args)
+ {
+ FrameLoadStart?.Invoke(this, args);
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The instance containing the event data.
+ void IWebBrowserInternal.OnFrameLoadEnd(FrameLoadEndEventArgs args)
+ {
+ FrameLoadEnd?.Invoke(this, args);
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The instance containing the event data.
+ void IWebBrowserInternal.OnConsoleMessage(ConsoleMessageEventArgs args)
+ {
+ ConsoleMessage?.Invoke(this, args);
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The instance containing the event data.
+ void IWebBrowserInternal.OnStatusMessage(StatusMessageEventArgs args)
+ {
+ StatusMessage?.Invoke(this, args);
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The instance containing the event data.
+ void IWebBrowserInternal.OnLoadError(LoadErrorEventArgs args)
+ {
+ LoadError?.Invoke(this, args);
+
+ initialLoadAction?.Invoke(null, args.ErrorCode);
+ }
+
+ void IWebBrowserInternal.SetCanExecuteJavascriptOnMainFrame(string frameId, bool canExecute)
+ {
+ //When loading pages of a different origin the frameId changes
+ //For the first loading of a new origin the messages from the render process
+ //Arrive in a different order than expected, the OnContextCreated message
+ //arrives before the OnContextReleased, then the message for OnContextReleased
+ //incorrectly overrides the value
+ //https://github.com/cefsharp/CefSharp/issues/3021
+
+ var chromiumChildProcessId = GetChromiumChildProcessId(frameId);
+
+ if (chromiumChildProcessId > canExecuteJavascriptInMainFrameChildProcessId && !canExecute)
+ {
+ return;
+ }
+
+ canExecuteJavascriptInMainFrameChildProcessId = chromiumChildProcessId;
+ CanExecuteJavascriptInMainFrame = canExecute;
+ }
+
+ void IWebBrowserInternal.SetJavascriptMessageReceived(JavascriptMessageReceivedEventArgs args)
+ {
+ JavascriptMessageReceived?.Invoke(this, args);
+ }
+
+ ///
+ /// Gets the browser adapter.
+ ///
+ /// The browser adapter.
+ IBrowserAdapter IWebBrowserInternal.BrowserAdapter
+ {
+ get { return managedCefBrowserAdapter; }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether this instance has parent.
+ ///
+ /// true if this instance has parent; otherwise, false.
+ bool IWebBrowserInternal.HasParent { get; set; }
+
+ ///
+ /// Called when [after browser created].
+ ///
+ /// The browser.
+ void IWebBrowserInternal.OnAfterBrowserCreated(IBrowser browser)
+ {
+ if (IsDisposed || browser.IsDisposed)
+ {
+ return;
+ }
+
+ this.browser = browser;
+ BrowserCore = browser;
+ initialLoadAction = InitialLoad;
+ Interlocked.Exchange(ref browserInitialized, 1);
+
+ UiThreadRunAsync(() =>
+ {
+ if (!IsDisposed)
+ {
+ OnIsBrowserInitializedChanged(false, true);
+ //To Minic the WPF behaviour this happens after OnIsBrowserInitializedChanged
+ IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty);
+
+ // Only call Load if initialAddress is null and Address is not empty
+ if (string.IsNullOrEmpty(initialAddress) && !string.IsNullOrEmpty(Address) && !initialLoadCalled)
+ {
+ Load(Address);
+ }
+ }
+ });
+
+ ResizeBrowser((int)ActualWidth, (int)ActualHeight);
+
+ if (initialFocus)
+ {
+ browser.GetHost()?.SetFocus(true);
+ }
+ }
+
+ ///
+ /// A flag that indicates whether the state of the control current supports the GoBack action (true) or not (false).
+ ///
+ /// true if this instance can go back; otherwise, false.
+ /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
+ /// binding.
+ public bool CanGoBack
+ {
+ get { return (bool)GetValue(CanGoBackProperty); }
+ }
+
+ ///
+ /// The can go back property
+ ///
+ public static DependencyProperty CanGoBackProperty = DependencyProperty.Register(nameof(CanGoBack), typeof(bool), typeof(ChromiumWebBrowser));
+
+ ///
+ /// A flag that indicates whether the state of the control currently supports the GoForward action (true) or not (false).
+ ///
+ /// true if this instance can go forward; otherwise, false.
+ /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
+ /// binding.
+ public bool CanGoForward
+ {
+ get { return (bool)GetValue(CanGoForwardProperty); }
+ }
+
+ ///
+ /// The can go forward property
+ ///
+ public static DependencyProperty CanGoForwardProperty = DependencyProperty.Register(nameof(CanGoForward), typeof(bool), typeof(ChromiumWebBrowser));
+
+ ///
+ /// The address (URL) which the browser control is currently displaying.
+ /// Will automatically be updated as the user navigates to another page (e.g. by clicking on a link).
+ ///
+ /// The address.
+ /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
+ /// binding.
+ public string Address
+ {
+ get { return (string)GetValue(AddressProperty); }
+ set { SetValue(AddressProperty, value); }
+ }
+
+ ///
+ /// The address property
+ ///
+ public static readonly DependencyProperty AddressProperty =
+ DependencyProperty.Register(nameof(Address), typeof(string), typeof(ChromiumWebBrowser),
+ new UIPropertyMetadata(null, OnAddressChanged));
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private static void OnAddressChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
+ {
+ var owner = (ChromiumWebBrowser)sender;
+ var oldValue = (string)args.OldValue;
+ var newValue = (string)args.NewValue;
+
+ owner.OnAddressChanged(oldValue, newValue);
+ }
+
+ ///
+ /// Called when [address changed].
+ ///
+ /// The old value.
+ /// The new value.
+ protected virtual void OnAddressChanged(string oldValue, string newValue)
+ {
+ if (ignoreUriChange || newValue == null)
+ {
+ return;
+ }
+
+ Load(newValue);
+ }
+
+ ///
+ /// A flag that indicates whether the control is currently loading one or more web pages (true) or not (false).
+ ///
+ /// true if this instance is loading; otherwise, false.
+ /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
+ /// binding.
+ public bool IsLoading
+ {
+ get { return (bool)GetValue(IsLoadingProperty); }
+ }
+
+ ///
+ /// The is loading property
+ ///
+ public static readonly DependencyProperty IsLoadingProperty =
+ DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(ChromiumWebBrowser), new PropertyMetadata(false));
+
+ ///
+ /// A flag that indicates whether the WebBrowser is initialized (true) or not (false).
+ ///
+ /// true if this instance is browser initialized; otherwise, false.
+ /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
+ /// binding.
+ public bool IsBrowserInitialized
+ {
+ get { return InternalIsBrowserInitialized(); }
+ }
+
+ ///
+ /// Event called after the underlying CEF browser instance has been created and
+ /// when the instance has been Disposed.
+ /// will be true when the underlying CEF Browser
+ /// has been created and false when the browser is being Disposed.
+ ///
+ public event EventHandler IsBrowserInitializedChanged;
+
+ ///
+ /// Called when [is browser initialized changed].
+ ///
+ /// if set to true [old value].
+ /// if set to true [new value].
+ protected virtual void OnIsBrowserInitializedChanged(bool oldValue, bool newValue)
+ {
+ if (newValue && !IsDisposed)
+ {
+ var task = this.GetZoomLevelAsync();
+ task.ContinueWith(previous =>
+ {
+ if (previous.Status == TaskStatus.RanToCompletion)
+ {
+ UiThreadRunAsync(() =>
+ {
+ if (!IsDisposed)
+ {
+ SetCurrentValue(ZoomLevelProperty, previous.Result);
+ }
+ });
+ }
+ else
+ {
+ throw new InvalidOperationException("Unexpected failure of calling CEF->GetZoomLevelAsync", previous.Exception);
+ }
+ }, TaskContinuationOptions.ExecuteSynchronously);
+ }
+ }
+
+ ///
+ /// The title of the web page being currently displayed.
+ ///
+ /// The title.
+ /// This property is implemented as a Dependency Property and fully supports data binding.
+ public string Title
+ {
+ get { return (string)GetValue(TitleProperty); }
+ set { SetValue(TitleProperty, value); }
+ }
+
+ ///
+ /// The title property
+ ///
+ public static readonly DependencyProperty TitleProperty =
+ DependencyProperty.Register(nameof(Title), typeof(string), typeof(ChromiumWebBrowser), new PropertyMetadata(null, OnTitleChanged));
+
+ ///
+ /// Event handler that will get called when the browser title changes
+ ///
+ public event DependencyPropertyChangedEventHandler TitleChanged;
+
+ ///
+ /// Handles the event.
+ ///
+ /// The d.
+ /// The instance containing the event data.
+ private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var owner = (ChromiumWebBrowser)d;
+
+ owner.TitleChanged?.Invoke(owner, e);
+ }
+
+ ///
+ /// The zoom level at which the browser control is currently displaying.
+ /// Can be set to 0 to clear the zoom level (resets to default zoom level).
+ ///
+ /// The zoom level.
+ public double ZoomLevel
+ {
+ get { return (double)GetValue(ZoomLevelProperty); }
+ set { SetValue(ZoomLevelProperty, value); }
+ }
+
+ ///
+ /// The zoom level property
+ ///
+ public static readonly DependencyProperty ZoomLevelProperty =
+ DependencyProperty.Register(nameof(ZoomLevel), typeof(double), typeof(ChromiumWebBrowser),
+ new UIPropertyMetadata(0d, OnZoomLevelChanged));
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private static void OnZoomLevelChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
+ {
+ var owner = (ChromiumWebBrowser)sender;
+ var oldValue = (double)args.OldValue;
+ var newValue = (double)args.NewValue;
+
+ owner.OnZoomLevelChanged(oldValue, newValue);
+ }
+
+ ///
+ /// Called when [zoom level changed].
+ ///
+ /// The old value.
+ /// The new value.
+ protected virtual void OnZoomLevelChanged(double oldValue, double newValue)
+ {
+ this.SetZoomLevel(newValue);
+ }
+
+ ///
+ /// Specifies the amount used to increase/decrease to ZoomLevel by
+ /// By Default this value is 0.10
+ ///
+ /// The zoom level increment.
+ public double ZoomLevelIncrement
+ {
+ get { return (double)GetValue(ZoomLevelIncrementProperty); }
+ set { SetValue(ZoomLevelIncrementProperty, value); }
+ }
+
+ ///
+ /// The zoom level increment property
+ ///
+ public static readonly DependencyProperty ZoomLevelIncrementProperty =
+ DependencyProperty.Register(nameof(ZoomLevelIncrement), typeof(double), typeof(ChromiumWebBrowser), new PropertyMetadata(0.10));
+
+ ///
+ /// The CleanupElement controls when the Browser will be Disposed.
+ /// The will be Disposed when is called.
+ /// Be aware that this Control is not usable anymore after it has been disposed.
+ ///
+ /// The cleanup element.
+ public FrameworkElement CleanupElement
+ {
+ get { return (FrameworkElement)GetValue(CleanupElementProperty); }
+ set { SetValue(CleanupElementProperty, value); }
+ }
+
+ ///
+ /// The cleanup element property
+ ///
+ public static readonly DependencyProperty CleanupElementProperty =
+ DependencyProperty.Register(nameof(CleanupElement), typeof(FrameworkElement), typeof(ChromiumWebBrowser), new PropertyMetadata(null, OnCleanupElementChanged));
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private static void OnCleanupElementChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
+ {
+ var owner = (ChromiumWebBrowser)sender;
+ var oldValue = (FrameworkElement)args.OldValue;
+ var newValue = (FrameworkElement)args.NewValue;
+
+ owner.OnCleanupElementChanged(oldValue, newValue);
+ }
+
+ ///
+ /// Called when [cleanup element changed].
+ ///
+ /// The old value.
+ /// The new value.
+ protected virtual void OnCleanupElementChanged(FrameworkElement oldValue, FrameworkElement newValue)
+ {
+ if (oldValue != null)
+ {
+ oldValue.Unloaded -= OnCleanupElementUnloaded;
+ }
+
+ if (newValue != null)
+ {
+ newValue.Unloaded += OnCleanupElementUnloaded;
+ }
+ }
+
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private void OnCleanupElementUnloaded(object sender, RoutedEventArgs e)
+ {
+ Dispose();
+ }
+
+ ///
+ /// The text that will be displayed as a ToolTip
+ ///
+ /// The tooltip text.
+ public string TooltipText
+ {
+ get { return (string)GetValue(TooltipTextProperty); }
+ }
+
+ ///
+ /// The tooltip text property
+ ///
+ public static readonly DependencyProperty TooltipTextProperty =
+ DependencyProperty.Register(nameof(TooltipText), typeof(string), typeof(ChromiumWebBrowser));
+
+ ///
+ /// Gets or sets the WebBrowser.
+ ///
+ /// The WebBrowser.
+ public IWebBrowser WebBrowser
+ {
+ get { return (IWebBrowser)GetValue(WebBrowserProperty); }
+ set { SetValue(WebBrowserProperty, value); }
+ }
+
+ ///
+ /// The WebBrowser property
+ ///
+ public static readonly DependencyProperty WebBrowserProperty =
+ DependencyProperty.Register(nameof(WebBrowser), typeof(IWebBrowser), typeof(ChromiumWebBrowser), new UIPropertyMetadata(defaultValue: null));
+
+ ///
+ /// Override this method to handle creation of WindowInfo. This method can be used to customise aspects of
+ /// browser creation including configuration of settings such as .
+ /// Window Activation is disabled by default, you can re-enable it by overriding and removing the
+ /// WS_EX_NOACTIVATE style from .
+ ///
+ /// Window handle for the Control
+ /// Window Info
+ ///
+ /// To re-enable Window Activation then remove WS_EX_NOACTIVATE from ExStyle
+ ///
+ /// const uint WS_EX_NOACTIVATE = 0x08000000;
+ /// windowInfo.ExStyle &= ~WS_EX_NOACTIVATE;
+ ///
+ ///
+ protected virtual IWindowInfo CreateBrowserWindowInfo(IntPtr handle)
+ {
+ var windowInfo = Core.ObjectFactory.CreateWindowInfo();
+ windowInfo.RuntimeStyle = CefSharpSettings.RuntimeStyle ?? CefRuntimeStyle.Alloy;
+ windowInfo.SetAsChild(handle);
+
+ if (!ActivateBrowserOnCreation)
+ {
+ //Disable Window activation by default
+ //https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window
+ windowInfo.ExStyle |= WS_EX_NOACTIVATE;
+ }
+
+ return windowInfo;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private void CreateBrowser()
+ {
+ browserCreated = true;
+
+ if (((IWebBrowserInternal)this).HasParent == false)
+ {
+ var windowInfo = CreateBrowserWindowInfo(hwndHost);
+
+ //We actually check if WS_EX_NOACTIVATE was set for instances
+ //the user has override CreateBrowserWindowInfo and not called base.CreateBrowserWindowInfo
+ removeExNoActivateStyle = (windowInfo.ExStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE;
+
+ //If initialAddress is set then we use that value, later in OnAfterBrowserCreated then we will
+ //call Load(url) if initial address was empty.
+ managedCefBrowserAdapter.CreateBrowser(windowInfo, browserSettings as BrowserSettings, requestContext as RequestContext, initialAddress);
+ }
+ }
+
+ ///
+ /// Runs the specific Action on the Dispatcher in an async fashion
+ ///
+ /// The action.
+ /// The priority.
+ internal void UiThreadRunAsync(Action action, DispatcherPriority priority = DispatcherPriority.DataBind)
+ {
+ if (Dispatcher.CheckAccess())
+ {
+ action();
+ }
+ else if (!Dispatcher.HasShutdownStarted)
+ {
+ Dispatcher.BeginInvoke(action, priority);
+ }
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
+ {
+ var isVisible = (bool)args.NewValue;
+
+ if (InternalIsBrowserInitialized())
+ {
+ var host = browser.GetHost();
+
+ if (isVisible)
+ {
+ ResizeBrowser((int)ActualWidth, (int)ActualHeight);
+
+ //Fix for #1778 - When browser becomes visible we update the zoom level
+ //browsers of the same origin will share the same zoomlevel and
+ //we need to track the update, so our ZoomLevelProperty works
+ //properly
+ host.GetZoomLevelAsync().ContinueWith(t =>
+ {
+ if (!IsDisposed)
+ {
+ SetCurrentValue(ZoomLevelProperty, t.Result);
+ }
+ },
+ CancellationToken.None,
+ TaskContinuationOptions.OnlyOnRanToCompletion,
+ TaskScheduler.FromCurrentSynchronizationContext());
+ }
+ else
+ {
+ //Hide browser
+ ResizeBrowser(0, 0);
+ }
+ }
+ }
+
+ ///
+ /// Loads the specified URL.
+ ///
+ /// The URL to be loaded.
+ public void Load(string url)
+ {
+ if (IsDisposed)
+ {
+ return;
+ }
+
+ //If the browser is already initialized then we can call LoadUrl directly
+ if (InternalIsBrowserInitialized())
+ {
+ var b = browser;
+ // Added null check -> binding-triggered changes of Address will lead to a nullref after Dispose has been called
+ if (b != null)
+ {
+ initialLoadCalled = true;
+
+ using (var frame = b.MainFrame)
+ {
+ frame.LoadUrl(url);
+ }
+ }
+ }
+ //If CreateBrowser was called and InternalIsBrowserInitialized() == false then we need to set the Address
+ //property so in OnAfterBrowserCreated the Url is loaded. If initialAddress was
+ //set then the Url set here will be ignored. If we called Load(url) then historically
+ //an aborted error would be raised as per https://github.com/cefsharp/CefSharp/issues/2300
+ //So we ignore the call for now.
+ else if (browserCreated)
+ {
+ UiThreadRunAsync(() =>
+ {
+ Address = url;
+ });
+ }
+ //Before browser created, set the intialAddress
+ else
+ {
+ initialAddress = url;
+ }
+ }
+
+ ///
+ public Task LoadUrlAsync(string url)
+ {
+ //LoadUrlAsync is actually a static method so that CefSharp.Wpf.HwndHost can reuse the code
+ //It's not actually an extension method so we can have it included as part of the
+ //IWebBrowser interface
+ return CefSharp.WebBrowserExtensions.LoadUrlAsync(this, url);
+ }
+
+ ///
+ /// Zooms the browser in.
+ ///
+ private void ZoomIn()
+ {
+ UiThreadRunAsync(() =>
+ {
+ ZoomLevel = ZoomLevel + ZoomLevelIncrement;
+ });
+ }
+
+ ///
+ /// Zooms the browser out.
+ ///
+ private void ZoomOut()
+ {
+ UiThreadRunAsync(() =>
+ {
+ ZoomLevel = ZoomLevel - ZoomLevelIncrement;
+ });
+ }
+
+ ///
+ /// Reset the browser's zoom level to default.
+ ///
+ private void ZoomReset()
+ {
+ UiThreadRunAsync(() =>
+ {
+ ZoomLevel = 0;
+ });
+ }
+
+ ///
+ /// The javascript object repository, one repository per ChromiumWebBrowser instance.
+ ///
+ public IJavascriptObjectRepository JavascriptObjectRepository
+ {
+ get { return managedCefBrowserAdapter?.JavascriptObjectRepository; }
+ }
+
+ ///
+ public IBrowser BrowserCore { get; private set; }
+
+ ///
+ /// Used by CefSharp.Puppeteer to associate a single DevToolsContext with a ChromiumWebBrowser instance.
+ ///
+ IDisposable IWebBrowserInternal.DevToolsContext { get; set; }
+
+ ///
+ /// Returns the current IBrowser Instance
+ ///
+ /// browser instance or null
+ public IBrowser GetBrowser()
+ {
+ return browser;
+ }
+
+ private static void InitializeCefInternal()
+ {
+ if (Cef.IsInitialized == null)
+ {
+ if (!Cef.Initialize(new CefSettings()))
+ {
+ throw new InvalidOperationException(CefInitializeFailedErrorMessage);
+ }
+ }
+
+ if (Cef.IsInitialized == false)
+ {
+ throw new InvalidOperationException(CefIsInitializedFalseErrorMessage);
+ }
+ }
+
+ ///
+ /// Check is browserisinitialized
+ ///
+ /// true if browser is initialized
+ private bool InternalIsBrowserInitialized()
+ {
+ // Use CompareExchange to read the current value - if disposeCount is 1, we set it to 1, effectively a no-op
+ // Volatile.Read would likely use a memory barrier which I believe is unnecessary in this scenario
+ return Interlocked.CompareExchange(ref browserInitialized, 0, 0) == 1;
+ }
+
+ ///
+ /// Resizes the browser.
+ ///
+ private void ResizeBrowser(int width, int height)
+ {
+ if (InternalIsBrowserInitialized())
+ {
+ if (dpiScale > 1)
+ {
+ width = (int)Math.Round(width * dpiScale);
+ height = (int)Math.Round(height * dpiScale);
+ }
+
+ managedCefBrowserAdapter.Resize(width, height);
+ }
+ }
+
+ private void OnWindowStateChanged(object sender, EventArgs e)
+ {
+ var window = (Window)sender;
+
+ switch (window.WindowState)
+ {
+ case WindowState.Normal:
+ case WindowState.Maximized:
+ {
+ if (previousWindowState == WindowState.Minimized && InternalIsBrowserInitialized())
+ {
+ ResizeBrowser((int)ActualWidth, (int)ActualHeight);
+ }
+ break;
+ }
+ case WindowState.Minimized:
+ {
+ if (InternalIsBrowserInitialized())
+ {
+ //Set the browser size to 0,0 to reduce CPU usage
+ ResizeBrowser(0, 0);
+ }
+ break;
+ }
+ }
+
+ previousWindowState = window.WindowState;
+ }
+
+ private void OnWindowLocationChanged(object sender, EventArgs e)
+ {
+ if (InternalIsBrowserInitialized())
+ {
+ var host = browser.GetHost();
+
+ host.NotifyMoveOrResizeStarted();
+ }
+ }
+
+ ///
+ public void LoadUrl(string url)
+ {
+ Load(url);
+ }
+
+ ///
+ public Task WaitForInitialLoadAsync()
+ {
+ return initialLoadTaskCompletionSource.Task;
+ }
+
+ private void InitialLoad(bool? isLoading, CefErrorCode? errorCode)
+ {
+ if (IsDisposed)
+ {
+ initialLoadAction = null;
+
+ initialLoadTaskCompletionSource.TrySetCanceled();
+
+ return;
+ }
+
+ if (isLoading.HasValue)
+ {
+ if (isLoading.Value)
+ {
+ return;
+ }
+
+ initialLoadAction = null;
+
+ var host = browser?.GetHost();
+
+ var navEntry = host?.GetVisibleNavigationEntry();
+
+ int statusCode = navEntry?.HttpStatusCode ?? -1;
+
+ //By default 0 is some sort of error, we map that to -1
+ //so that it's clearer that something failed.
+ if (statusCode == 0)
+ {
+ statusCode = -1;
+ }
+
+ initialLoadTaskCompletionSource.TrySetResult(new LoadUrlAsyncResponse(CefErrorCode.None, statusCode));
+ }
+ else if (errorCode.HasValue)
+ {
+ //Actions that trigger a download will raise an aborted error.
+ //Generally speaking Aborted is safe to ignore
+ if (errorCode == CefErrorCode.Aborted)
+ {
+ return;
+ }
+
+ initialLoadAction = null;
+
+ initialLoadTaskCompletionSource.TrySetResult(new LoadUrlAsyncResponse(errorCode.Value, -1));
+ }
+ }
+
+ ///
+ public bool TryGetBrowserCoreById(int browserId, out IBrowser browser)
+ {
+ var browserAdapter = managedCefBrowserAdapter;
+
+ if (IsDisposed || browserAdapter == null || browserAdapter.IsDisposed)
+ {
+ browser = null;
+
+ return false;
+ }
+
+ browser = browserAdapter.GetBrowser(browserId);
+
+ return browser != null;
+ }
+
+ ///
+ public async Task GetContentSizeAsync()
+ {
+ ThrowExceptionIfDisposed();
+ ThrowExceptionIfBrowserNotInitialized();
+
+ using (var devToolsClient = browser.GetDevToolsClient())
+ {
+ //Get the content size
+ var layoutMetricsResponse = await devToolsClient.Page.GetLayoutMetricsAsync().ConfigureAwait(continueOnCapturedContext: false);
+
+ var rect = layoutMetricsResponse.CssContentSize;
+
+ return new Structs.DomRect(rect.X, rect.Y, rect.Width, rect.Height);
+ }
+ }
+
+ ///
+ public Task WaitForNavigationAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default)
+ {
+ //WaitForNavigationAsync is actually a static method so that CefSharp.Wpf.HwndHost can reuse the code
+ return CefSharp.WebBrowserExtensions.WaitForNavigationAsync(this, timeout, cancellationToken);
+ }
+
+ ///
+ /// Capture page screenshot.
+ ///
+ /// Image compression format (defaults to png).
+ /// Compression quality from range [0..100] (jpeg only).
+ /// Capture the screenshot of a given region only.
+ /// Capture the screenshot from the surface, rather than the view. Defaults to true.
+ /// Capture the screenshot beyond the viewport. Defaults to false.
+ /// A task that can be awaited to obtain the screenshot as a byte[].
+ public async Task CaptureScreenshotAsync(CaptureScreenshotFormat format = CaptureScreenshotFormat.Png, int? quality = null, Viewport viewPort = null, bool fromSurface = true, bool captureBeyondViewport = false)
+ {
+ ThrowExceptionIfDisposed();
+ ThrowExceptionIfBrowserNotInitialized();
+
+ if (viewPort != null && viewPort.Scale <= 0)
+ {
+ throw new ArgumentException($"{nameof(viewPort)}.{nameof(viewPort.Scale)} must be greater than 0.");
+ }
+
+ using (var devToolsClient = browser.GetDevToolsClient())
+ {
+ var screenShot = await devToolsClient.Page.CaptureScreenshotAsync(format, quality, viewPort, fromSurface, captureBeyondViewport).ConfigureAwait(continueOnCapturedContext: false);
+
+ return screenShot.Data;
+ }
+ }
+
+ ///
+ /// Throw exception if browser not initialized.
+ ///
+ /// Thrown when an exception error condition occurs.
+ private void ThrowExceptionIfBrowserNotInitialized()
+ {
+ if (!InternalIsBrowserInitialized())
+ {
+ throw new Exception(BrowserNotInitializedExceptionErrorMessage);
+ }
+ }
+
+ ///
+ /// Throw exception if disposed.
+ ///
+ /// Thrown when a supplied object has been disposed.
+ private void ThrowExceptionIfDisposed()
+ {
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException("browser", "Browser has been disposed");
+ }
+ }
+
+ private int GetChromiumChildProcessId(string frameIdentifier)
+ {
+ try
+ {
+ var parts = frameIdentifier.Split('-');
+
+ if (int.TryParse(parts[0], out var childProcessId))
+ return childProcessId;
+ }
+ catch
+ {
+
+ }
+
+ return -1;
+ }
+ }
+}
diff --git a/CefSharp.Wpf/HwndHost/FocusHandler.cs b/CefSharp.Wpf/HwndHost/FocusHandler.cs
new file mode 100644
index 0000000000..34a41a87d2
--- /dev/null
+++ b/CefSharp.Wpf/HwndHost/FocusHandler.cs
@@ -0,0 +1,84 @@
+// Copyright © 2020 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System.Windows.Input;
+
+namespace CefSharp.Wpf.HwndHost
+{
+ ///
+ /// Focus Handler
+ /// The methods of this class will be called on the CEF UI thread.
+ ///
+ public class FocusHandler : IFocusHandler
+ {
+ void IFocusHandler.OnGotFocus(IWebBrowser chromiumWebBrowser, IBrowser browser)
+ {
+ OnGotFocus(chromiumWebBrowser, browser);
+ }
+
+ ///
+ /// Called when the browser component has received focus.
+ ///
+ /// the ChromiumWebBrowser control
+ /// the browser object
+ protected virtual void OnGotFocus(IWebBrowser chromiumWebBrowser, IBrowser browser)
+ {
+
+ }
+
+ bool IFocusHandler.OnSetFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFocusSource source)
+ {
+ return OnSetFocus(chromiumWebBrowser, browser, source);
+ }
+
+ ///
+ /// Called when the browser component is requesting focus.
+ ///
+ /// the ChromiumWebBrowser control
+ /// the browser object, do not keep a reference to this object outside of this method
+ /// Indicates where the focus request is originating from.
+ /// Return false to allow the focus to be set or true to cancel setting the focus.
+ protected virtual bool OnSetFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFocusSource source)
+ {
+ //We don't deal with popups as they're rendered by default entirely by CEF
+ if (browser.IsPopup)
+ {
+ return false;
+ }
+ // Do not let the browser take focus when a Load method has been called
+ return source == CefFocusSource.FocusSourceNavigation;
+ }
+
+
+
+ void IFocusHandler.OnTakeFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next)
+ {
+ OnTakeFocus(chromiumWebBrowser, browser, next);
+ }
+
+ ///
+ /// Called when the browser component is about to lose focus.
+ /// For instance, if focus was on the last HTML element and the user pressed the TAB key.
+ ///
+ /// the ChromiumWebBrowser control
+ /// the browser object
+ /// Will be true if the browser is giving focus to the next component
+ /// and false if the browser is giving focus to the previous component.
+ protected virtual void OnTakeFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next)
+ {
+ //We don't deal with popups as they're rendered by default entirely by CEF
+ if (browser.IsPopup)
+ {
+ return;
+ }
+
+ var hwndChromiumWebBrowser = (ChromiumWebBrowser)chromiumWebBrowser;
+
+ var request = new TraversalRequest(next ? FocusNavigationDirection.Next : FocusNavigationDirection.Previous);
+
+ // NOTE: OnTakeFocus means leaving focus / not taking focus
+ hwndChromiumWebBrowser.UiThreadRunAsync(() => hwndChromiumWebBrowser.MoveFocus(request));
+ }
+ }
+}
diff --git a/CefSharp.Wpf/HwndHost/Handler/IntegratedMessageLoopBrowserProcessHandler.cs b/CefSharp.Wpf/HwndHost/Handler/IntegratedMessageLoopBrowserProcessHandler.cs
new file mode 100644
index 0000000000..0a16e09aa1
--- /dev/null
+++ b/CefSharp.Wpf/HwndHost/Handler/IntegratedMessageLoopBrowserProcessHandler.cs
@@ -0,0 +1,131 @@
+// Copyright © 2022 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
+using System.Timers;
+using System.Windows.Threading;
+using CefSharp.Handler;
+
+namespace CefSharp.Wpf.HwndHost.Handler
+{
+ ///
+ /// A implementation that can be used to
+ /// integreate CEF into the WPF message loop (Dispatcher).
+ /// Currently it's a very basic implementation.
+ /// See the following link for the CEF reference implementation.
+ /// https://bitbucket.org/chromiumembedded/cef/commits/1ff26aa02a656b3bc9f0712591c92849c5909e04?at=2785
+ ///
+ public class IntegratedMessageLoopBrowserProcessHandler
+ : BrowserProcessHandler
+ {
+ ///
+ /// Sixty Times per second
+ ///
+ public const int SixtyTimesPerSecond = 1000 / 60; // 60fps
+ ///
+ /// Thirty Times per second
+ ///
+ public const int ThirtyTimesPerSecond = 1000 / 30; //30fps
+
+ private DispatcherTimer dispatcherTimer;
+ private Dispatcher dispatcher;
+ private readonly DispatcherPriority dispatcherPriority;
+ private int interval;
+
+ ///
+ /// Default constructor
+ ///
+ /// WPF Dispatcher
+ /// Priority at which is called using the Dispatcher
+ /// the in miliseconds (frame rate), for 30/60 times per second use
+ /// / respectively.
+ public IntegratedMessageLoopBrowserProcessHandler(Dispatcher dispatcher, DispatcherPriority dispatcherPriority = DispatcherPriority.Render, int interval = ThirtyTimesPerSecond)
+ {
+ if (dispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(dispatcher));
+ }
+
+ if (interval < 10)
+ {
+ throw new ArgumentOutOfRangeException(nameof(interval), "Argument less than 10. ");
+ }
+
+ dispatcherTimer = new DispatcherTimer(dispatcherPriority, dispatcher)
+ {
+ Interval = TimeSpan.FromMilliseconds(interval)
+ };
+
+ dispatcherTimer.Tick += OnDispatcherTimerTick;
+ dispatcherTimer.Start();
+
+ this.dispatcher = dispatcher;
+ this.dispatcher.ShutdownStarted += DispatcherShutdownStarted;
+ this.dispatcherPriority = dispatcherPriority;
+ this.interval = interval;
+
+ Cef.ShutdownStarted += OnCefShutdownStarted;
+ }
+
+ private void OnCefShutdownStarted(object sender, EventArgs e)
+ {
+ InternalDispose();
+ }
+
+ private void DispatcherShutdownStarted(object sender, EventArgs e)
+ {
+ //If the dispatcher is shutting down then we will cleanup
+ InternalDispose();
+ }
+
+ private void OnDispatcherTimerTick(object sender, EventArgs e)
+ {
+ // Execute Cef.DoMessageLoopWork on the UI Thread
+ // Typically this would happen 30/60 times per second (frame rate)
+ Cef.DoMessageLoopWork();
+ }
+
+ ///
+ protected override void OnScheduleMessagePumpWork(long delay)
+ {
+ // If the delay is greater than the Maximum then use ThirtyTimesPerSecond
+ // instead - we do this to achieve a minimum number of FPS
+ if (delay > interval)
+ {
+ delay = interval;
+ }
+
+ // When delay <= 0 we'll execute Cef.DoMessageLoopWork immediately
+ // if it's greater than we'll just let the Timer which fires 30 times per second
+ // care of the call
+ if (delay <= 0)
+ {
+ dispatcher?.InvokeAsync(() => Cef.DoMessageLoopWork(), dispatcherPriority);
+ }
+ }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ InternalDispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ private void InternalDispose()
+ {
+ if (dispatcher != null)
+ {
+ dispatcher.ShutdownStarted -= DispatcherShutdownStarted;
+ dispatcher = null;
+ }
+
+ dispatcherTimer?.Stop();
+ dispatcherTimer = null;
+ }
+ }
+}
diff --git a/CefSharp.Wpf/HwndHost/IWpfWebBrowser.cs b/CefSharp.Wpf/HwndHost/IWpfWebBrowser.cs
new file mode 100644
index 0000000000..d4d323c42b
--- /dev/null
+++ b/CefSharp.Wpf/HwndHost/IWpfWebBrowser.cs
@@ -0,0 +1,18 @@
+// Copyright © 2019 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System.Windows;
+
+namespace CefSharp.Wpf.HwndHost
+{
+ ///
+ /// WPF specific implementation, has reference to some of the commands
+ /// and properties the exposes.
+ ///
+ ///
+ public interface IWpfWebBrowser : IWpfChromiumWebBrowser, IInputElement
+ {
+
+ }
+}
diff --git a/CefSharp.Wpf/HwndHost/Internals/DelegateCommand.cs b/CefSharp.Wpf/HwndHost/Internals/DelegateCommand.cs
new file mode 100644
index 0000000000..ddc16f8678
--- /dev/null
+++ b/CefSharp.Wpf/HwndHost/Internals/DelegateCommand.cs
@@ -0,0 +1,68 @@
+// Copyright © 2019 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
+using System.Windows.Input;
+
+namespace CefSharp.Wpf.HwndHost.Internals
+{
+ ///
+ /// DelegateCommand
+ ///
+ ///
+ internal class DelegateCommand : ICommand
+ {
+ ///
+ /// The command handler
+ ///
+ private readonly Action commandHandler;
+ ///
+ /// The can execute handler
+ ///
+ private readonly Func canExecuteHandler;
+
+ ///
+ /// Occurs when changes occur that affect whether or not the command should execute.
+ ///
+ public event EventHandler CanExecuteChanged;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The command handler.
+ /// The can execute handler.
+ public DelegateCommand(Action commandHandler, Func canExecuteHandler = null)
+ {
+ this.commandHandler = commandHandler;
+ this.canExecuteHandler = canExecuteHandler;
+ }
+
+ ///
+ /// Defines the method to be called when the command is invoked.
+ ///
+ /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
+ public void Execute(object parameter)
+ {
+ commandHandler();
+ }
+
+ ///
+ /// Defines the method that determines whether the command can execute in its current state.
+ ///
+ /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
+ /// true if this command can be executed; otherwise, false.
+ public bool CanExecute(object parameter)
+ {
+ return canExecuteHandler == null || canExecuteHandler();
+ }
+
+ ///
+ /// Raises the can execute changed.
+ ///
+ public void RaiseCanExecuteChanged()
+ {
+ CanExecuteChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+}
diff --git a/CefSharp.Wpf/HwndHost/Internals/NoCloseLifespanHandler.cs b/CefSharp.Wpf/HwndHost/Internals/NoCloseLifespanHandler.cs
new file mode 100644
index 0000000000..b2a9b7b50f
--- /dev/null
+++ b/CefSharp.Wpf/HwndHost/Internals/NoCloseLifespanHandler.cs
@@ -0,0 +1,26 @@
+// Copyright © 2022 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+namespace CefSharp.Wpf.HwndHost.Internals
+{
+ ///
+ /// LifeSpanHandler used internally
+ /// - Cancels sending of WM_CLOSE message for main browser
+ /// - Allows popups to close
+ ///
+ public class NoCloseLifespanHandler
+ : CefSharp.Handler.LifeSpanHandler
+ {
+ ///
+ protected override bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
+ {
+ if(browser.IsPopup)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/CefSharp.Wpf/IWpfChromiumWebBrowser.cs b/CefSharp.Wpf/IWpfChromiumWebBrowser.cs
new file mode 100644
index 0000000000..59626bc576
--- /dev/null
+++ b/CefSharp.Wpf/IWpfChromiumWebBrowser.cs
@@ -0,0 +1,151 @@
+// Copyright © 2013 The CefSharp Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+using System;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Threading;
+using CefSharp.Enums;
+using CefSharp.Internals;
+
+namespace CefSharp.Wpf
+{
+ ///
+ /// WPF specific implementation, has reference to some of the commands
+ /// and properties the exposes.
+ ///
+ ///
+ public interface IWpfChromiumWebBrowser : IWebBrowser, IInputElement
+ {
+ ///
+ /// Navigates to the previous page in the browser history. Will automatically be enabled/disabled depending on the
+ /// browser state.
+ ///
+ /// The back command.
+ ICommand BackCommand { get; }
+
+ ///
+ /// Navigates to the next page in the browser history. Will automatically be enabled/disabled depending on the
+ /// browser state.
+ ///
+ /// The forward command.
+ ICommand ForwardCommand { get; }
+
+ ///
+ /// Reloads the content of the current page. Will automatically be enabled/disabled depending on the browser state.
+ ///
+ /// The reload command.
+ ICommand ReloadCommand { get; }
+
+ ///
+ /// Prints the current browser contents.
+ ///
+ /// The print command.
+ ICommand PrintCommand { get; }
+
+ ///
+ /// Increases the zoom level.
+ ///
+ /// The zoom in command.
+ ICommand ZoomInCommand { get; }
+
+ ///
+ /// Decreases the zoom level.
+ ///
+ /// The zoom out command.
+ ICommand ZoomOutCommand { get; }
+
+ ///
+ /// Resets the zoom level to the default. (100%)
+ ///
+ /// The zoom reset command.
+ ICommand ZoomResetCommand { get; }
+
+ ///
+ /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web
+ /// page is shown.
+ ///
+ /// The view source command.
+ ICommand ViewSourceCommand { get; }
+
+ ///
+ /// Command which cleans up the Resources used by the ChromiumWebBrowser
+ ///
+ /// The cleanup command.
+ ICommand CleanupCommand { get; }
+
+ ///
+ /// Stops loading the current page.
+ ///
+ /// The stop command.
+ ICommand StopCommand { get; }
+
+ ///
+ /// Cut selected text to the clipboard.
+ ///
+ /// The cut command.
+ ICommand CutCommand { get; }
+
+ ///
+ /// Copy selected text to the clipboard.
+ ///
+ /// The copy command.
+ ICommand CopyCommand { get; }
+
+ ///
+ /// Paste text from the clipboard.
+ ///
+ /// The paste command.
+ ICommand PasteCommand { get; }
+
+ ///
+ /// Select all text.
+ ///
+ /// The select all command.
+ ICommand SelectAllCommand { get; }
+
+ ///
+ /// Undo last action.
+ ///
+ /// The undo command.
+ ICommand UndoCommand { get; }
+
+ ///
+ /// Redo last action.
+ ///
+ /// The redo command.
+ ICommand RedoCommand { get; }
+
+ ///
+ /// Toggles the audio mute for the current browser.
+ ///
+ ICommand ToggleAudioMuteCommand { get; }
+
+ ///
+ /// Gets the associated with this instance.
+ ///
+ /// The dispatcher.
+ Dispatcher Dispatcher { get; }
+
+ ///
+ /// The zoom level at which the browser control is currently displaying.
+ /// Can be set to 0 to clear the zoom level (resets to default zoom level).
+ ///
+ /// The zoom level.
+ double ZoomLevel { get; set; }
+
+ ///
+ /// The increment at which the property will be incremented/decremented.
+ ///
+ /// The zoom level increment.
+ double ZoomLevelIncrement { get; set; }
+
+ ///
+ /// The title of the web page being currently displayed.
+ ///
+ /// The title.
+ /// This property is implemented as a Dependency Property and fully supports data binding.
+ string Title { get; }
+ }
+}
diff --git a/CefSharp.Wpf/IWpfWebBrowser.cs b/CefSharp.Wpf/IWpfWebBrowser.cs
index 0fb0c0e4c1..ec0966eb69 100644
--- a/CefSharp.Wpf/IWpfWebBrowser.cs
+++ b/CefSharp.Wpf/IWpfWebBrowser.cs
@@ -16,138 +16,8 @@ namespace CefSharp.Wpf
/// and properties the exposes.
///
///
- public interface IWpfWebBrowser : IWebBrowser, IInputElement
+ public interface IWpfWebBrowser : IWpfChromiumWebBrowser, IInputElement
{
- ///
- /// Navigates to the previous page in the browser history. Will automatically be enabled/disabled depending on the
- /// browser state.
- ///
- /// The back command.
- ICommand BackCommand { get; }
-
- ///
- /// Navigates to the next page in the browser history. Will automatically be enabled/disabled depending on the
- /// browser state.
- ///
- /// The forward command.
- ICommand ForwardCommand { get; }
-
- ///
- /// Reloads the content of the current page. Will automatically be enabled/disabled depending on the browser state.
- ///
- /// The reload command.
- ICommand ReloadCommand { get; }
-
- ///
- /// Prints the current browser contents.
- ///
- /// The print command.
- ICommand PrintCommand { get; }
-
- ///
- /// Increases the zoom level.
- ///
- /// The zoom in command.
- ICommand ZoomInCommand { get; }
-
- ///
- /// Decreases the zoom level.
- ///
- /// The zoom out command.
- ICommand ZoomOutCommand { get; }
-
- ///
- /// Resets the zoom level to the default. (100%)
- ///
- /// The zoom reset command.
- ICommand ZoomResetCommand { get; }
-
- ///
- /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web
- /// page is shown.
- ///
- /// The view source command.
- ICommand ViewSourceCommand { get; }
-
- ///
- /// Command which cleans up the Resources used by the ChromiumWebBrowser
- ///
- /// The cleanup command.
- ICommand CleanupCommand { get; }
-
- ///
- /// Stops loading the current page.
- ///
- /// The stop command.
- ICommand StopCommand { get; }
-
- ///
- /// Cut selected text to the clipboard.
- ///
- /// The cut command.
- ICommand CutCommand { get; }
-
- ///
- /// Copy selected text to the clipboard.
- ///
- /// The copy command.
- ICommand CopyCommand { get; }
-
- ///
- /// Paste text from the clipboard.
- ///
- /// The paste command.
- ICommand PasteCommand { get; }
-
- ///
- /// Select all text.
- ///
- /// The select all command.
- ICommand SelectAllCommand { get; }
-
- ///
- /// Undo last action.
- ///
- /// The undo command.
- ICommand UndoCommand { get; }
-
- ///
- /// Redo last action.
- ///
- /// The redo command.
- ICommand RedoCommand { get; }
-
- ///
- /// Toggles the audio mute for the current browser.
- ///
- ICommand ToggleAudioMuteCommand { get; }
-
- ///
- /// Gets the associated with this instance.
- ///
- /// The dispatcher.
- Dispatcher Dispatcher { get; }
-
- ///
- /// The zoom level at which the browser control is currently displaying.
- /// Can be set to 0 to clear the zoom level (resets to default zoom level).
- ///
- /// The zoom level.
- double ZoomLevel { get; set; }
-
- ///
- /// The increment at which the property will be incremented/decremented.
- ///
- /// The zoom level increment.
- double ZoomLevelIncrement { get; set; }
-
- ///
- /// The title of the web page being currently displayed.
- ///
- /// The title.
- /// This property is implemented as a Dependency Property and fully supports data binding.
- string Title { get; }
-
///
/// Raised every time is called. You can access the underlying buffer, though it's
/// preferable to either override or implement your own as there is no outwardly
diff --git a/CefSharp3.netcore.sln b/CefSharp3.netcore.sln
index 881b29414b..2dec6f6c64 100644
--- a/CefSharp3.netcore.sln
+++ b/CefSharp3.netcore.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30223.230
+# Visual Studio Version 17
+VisualStudioVersion = 17.11.35431.28
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.netcore", "CefSharp\CefSharp.netcore.csproj", "{39E385AD-DC5C-451E-B061-09AF3EE038EB}"
EndProject
@@ -73,6 +73,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Core.netcore", "Ce
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Core.Runtime.RefAssembly.netcore", "CefSharp.Core.Runtime.RefAssembly\CefSharp.Core.Runtime.RefAssembly.netcore.csproj", "{A4AFD158-0B6F-4579-AE79-EC386C8BEA58}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Wpf.HwndHost.Example.netcore", "CefSharp.Wpf.HwndHost.Example\CefSharp.Wpf.HwndHost.Example.netcore.csproj", "{D137B43E-077C-4820-AC4F-34BA074FFCF8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|arm64 = Debug|arm64
@@ -243,6 +245,18 @@ Global
{A4AFD158-0B6F-4579-AE79-EC386C8BEA58}.Release|x64.Build.0 = Release|Any CPU
{A4AFD158-0B6F-4579-AE79-EC386C8BEA58}.Release|x86.ActiveCfg = Release|Any CPU
{A4AFD158-0B6F-4579-AE79-EC386C8BEA58}.Release|x86.Build.0 = Release|Any CPU
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Debug|arm64.ActiveCfg = Debug|arm64
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Debug|arm64.Build.0 = Debug|arm64
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Debug|x64.ActiveCfg = Debug|x64
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Debug|x64.Build.0 = Debug|x64
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Debug|x86.ActiveCfg = Debug|x86
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Debug|x86.Build.0 = Debug|x86
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Release|arm64.ActiveCfg = Release|arm64
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Release|arm64.Build.0 = Release|arm64
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Release|x64.ActiveCfg = Release|x64
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Release|x64.Build.0 = Release|x64
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Release|x86.ActiveCfg = Release|x86
+ {D137B43E-077C-4820-AC4F-34BA074FFCF8}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/CefSharp3.sln b/CefSharp3.sln
index f0a612cc70..14092052a6 100644
--- a/CefSharp3.sln
+++ b/CefSharp3.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30804.86
+# Visual Studio Version 17
+VisualStudioVersion = 17.11.35431.28
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Example", "CefSharp.Example\CefSharp.Example.csproj", "{A4394E7B-1155-43A6-989E-8AB72DDDC9E4}"
ProjectSection(ProjectDependencies) = postProject
@@ -85,6 +85,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Test", "CefSharp.T
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Core", "CefSharp.Core\CefSharp.Core.csproj", "{B760B002-D277-4525-9F63-67ED2065034A}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Wpf.HwndHost.Example", "CefSharp.Wpf.HwndHost.Example\CefSharp.Wpf.HwndHost.Example.csproj", "{B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -197,6 +199,14 @@ Global
{B760B002-D277-4525-9F63-67ED2065034A}.Release|Win32.Build.0 = Release|Any CPU
{B760B002-D277-4525-9F63-67ED2065034A}.Release|x64.ActiveCfg = Release|Any CPU
{B760B002-D277-4525-9F63-67ED2065034A}.Release|x64.Build.0 = Release|Any CPU
+ {B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}.Debug|Win32.ActiveCfg = Debug|x86
+ {B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}.Debug|Win32.Build.0 = Debug|x86
+ {B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}.Debug|x64.ActiveCfg = Debug|x64
+ {B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}.Debug|x64.Build.0 = Debug|x64
+ {B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}.Release|Win32.ActiveCfg = Release|x86
+ {B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}.Release|Win32.Build.0 = Release|x86
+ {B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}.Release|x64.ActiveCfg = Release|x64
+ {B019C1F9-3015-4B7B-B4A4-B07DF4220CD3}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/UpdateNugetPackages.ps1 b/UpdateNugetPackages.ps1
index 818953a2d2..fe0d8210f9 100644
--- a/UpdateNugetPackages.ps1
+++ b/UpdateNugetPackages.ps1
@@ -92,7 +92,7 @@ foreach($file in $vcxprojFiles)
$CefSharpCorePackagesXml = [xml](Get-Content (Resolve-Path 'CefSharp.Core.Runtime\packages.CefSharp.Core.Runtime.config'))
$RedistVersion = $CefSharpCorePackagesXml.SelectSingleNode("//packages/package[@id='cef.sdk']/@version").value
-$csprojFiles = @('CefSharp.WinForms.Example\CefSharp.WinForms.Example.netcore.csproj','CefSharp.Wpf.Example\CefSharp.Wpf.Example.netcore.csproj','CefSharp.OffScreen.Example\CefSharp.OffScreen.Example.netcore.csproj', 'CefSharp.Test\CefSharp.Test.netcore.csproj', 'CefSharp.WinForms.Example\CefSharp.WinForms.Example.csproj','CefSharp.Wpf.Example\CefSharp.Wpf.Example.csproj','CefSharp.OffScreen.Example\CefSharp.OffScreen.Example.csproj', 'CefSharp.Test\CefSharp.Test.csproj')
+$csprojFiles = @('CefSharp.WinForms.Example\CefSharp.WinForms.Example.netcore.csproj','CefSharp.Wpf.Example\CefSharp.Wpf.Example.netcore.csproj','CefSharp.OffScreen.Example\CefSharp.OffScreen.Example.netcore.csproj', 'CefSharp.Test\CefSharp.Test.netcore.csproj', 'CefSharp.WinForms.Example\CefSharp.WinForms.Example.csproj','CefSharp.Wpf.Example\CefSharp.Wpf.Example.csproj','CefSharp.OffScreen.Example\CefSharp.OffScreen.Example.csproj', 'CefSharp.Test\CefSharp.Test.csproj,'CefSharp.Wpf.HwndHost.Example\CefSharp.Wpf.HwndHost.Example.netcore.csproj'')
#Loop through the net core example projects and update the package version number