From f201c694c6d60eb78479e420eeec6fa6629045fc Mon Sep 17 00:00:00 2001 From: Alex Maitland Date: Tue, 3 May 2022 11:28:25 +1000 Subject: [PATCH] WinForms - Add CaptureScreenshotAsync - Simplification of calling the DevTools method - Moved GetContentSizeAsync from OffScreen into partial class (now accessible from WinForms/WPF/OffScreen). - Added menu option Resolves #4081 --- CefSharp.OffScreen/ChromiumWebBrowser.cs | 18 --------- .../BrowserForm.Designer.cs | 22 ++++++++--- CefSharp.WinForms.Example/BrowserForm.cs | 37 +++++++++++++++++++ CefSharp.WinForms/ChromiumWebBrowser.cs | 24 ++++++++++++ CefSharp/IWebBrowser.cs | 6 +++ .../Partial/ChromiumWebBrowser.Partial.cs | 15 ++++++++ 6 files changed, 98 insertions(+), 24 deletions(-) diff --git a/CefSharp.OffScreen/ChromiumWebBrowser.cs b/CefSharp.OffScreen/ChromiumWebBrowser.cs index e040f28052..7c0ad6d71a 100644 --- a/CefSharp.OffScreen/ChromiumWebBrowser.cs +++ b/CefSharp.OffScreen/ChromiumWebBrowser.cs @@ -643,24 +643,6 @@ public Task ResizeAsync(int width, int height, float? deviceScaleFactor = null) return tcs.Task; } - /// - /// Size of scrollable area in CSS pixels - /// - /// A task that can be awaited to get the size of the scrollable area in CSS pixels. - public async Task GetContentSizeAsync() - { - ThrowExceptionIfDisposed(); - ThrowExceptionIfBrowserNotInitialized(); - - using (var devToolsClient = browser.GetDevToolsClient()) - { - //Get the content size - var layoutMetricsResponse = await devToolsClient.Page.GetLayoutMetricsAsync().ConfigureAwait(continueOnCapturedContext:false); - - return layoutMetricsResponse.CssContentSize; - } - } - /// public void Load(string url) { diff --git a/CefSharp.WinForms.Example/BrowserForm.Designer.cs b/CefSharp.WinForms.Example/BrowserForm.Designer.cs index 292cd6e5c9..e562cc056d 100644 --- a/CefSharp.WinForms.Example/BrowserForm.Designer.cs +++ b/CefSharp.WinForms.Example/BrowserForm.Designer.cs @@ -33,6 +33,7 @@ private void InitializeComponent() this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.newTabToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.closeTabToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.hideScrollbarsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.printToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.printToPdfToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -70,7 +71,7 @@ private void InitializeComponent() this.loadExtensionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.javascriptBindingStressTestToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.browserTabControl = new System.Windows.Forms.TabControl(); - this.hideScrollbarsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.takeScreenShotMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuStrip1.SuspendLayout(); this.SuspendLayout(); // @@ -98,6 +99,7 @@ private void InitializeComponent() this.printToolStripMenuItem, this.printToPdfToolStripMenuItem, this.aboutToolStripMenuItem, + this.takeScreenShotMenuItem, this.showDevToolsMenuItem, this.showDevToolsDockedToolStripMenuItem, this.closeDevToolsMenuItem, @@ -123,6 +125,13 @@ private void InitializeComponent() this.closeTabToolStripMenuItem.Text = "&Close Tab"; this.closeTabToolStripMenuItem.Click += new System.EventHandler(this.CloseTabToolStripMenuItemClick); // + // hideScrollbarsMenuItem + // + this.hideScrollbarsMenuItem.Name = "hideScrollbarsMenuItem"; + this.hideScrollbarsMenuItem.Size = new System.Drawing.Size(207, 22); + this.hideScrollbarsMenuItem.Text = "Hide Scrollbars"; + this.hideScrollbarsMenuItem.Click += new System.EventHandler(this.HideScrollbarsToolStripMenuItemClick); + // // printToolStripMenuItem // this.printToolStripMenuItem.Name = "printToolStripMenuItem"; @@ -402,12 +411,12 @@ private void InitializeComponent() this.browserTabControl.Size = new System.Drawing.Size(730, 466); this.browserTabControl.TabIndex = 2; // - // hideScrollbarsMenuItem + // takeScreenShotMenuItem // - this.hideScrollbarsMenuItem.Name = "toolStripMenuItem1"; - this.hideScrollbarsMenuItem.Size = new System.Drawing.Size(207, 22); - this.hideScrollbarsMenuItem.Text = "Hide Scrollbars"; - this.hideScrollbarsMenuItem.Click += new System.EventHandler(this.HideScrollbarsToolStripMenuItemClick); + this.takeScreenShotMenuItem.Name = "takeScreenShotMenuItem"; + this.takeScreenShotMenuItem.Size = new System.Drawing.Size(207, 22); + this.takeScreenShotMenuItem.Text = "Take Screenshot"; + this.takeScreenShotMenuItem.Click += new System.EventHandler(this.TakeScreenShotMenuItemClick); // // BrowserForm // @@ -471,5 +480,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem javascriptBindingStressTestToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem showDevToolsDockedToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem hideScrollbarsMenuItem; + private System.Windows.Forms.ToolStripMenuItem takeScreenShotMenuItem; } } diff --git a/CefSharp.WinForms.Example/BrowserForm.cs b/CefSharp.WinForms.Example/BrowserForm.cs index 0aa3eec3c5..7798e77e4d 100644 --- a/CefSharp.WinForms.Example/BrowserForm.cs +++ b/CefSharp.WinForms.Example/BrowserForm.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -649,5 +650,41 @@ private void HideScrollbarsToolStripMenuItemClick(object sender, EventArgs e) _ = control?.HideScrollbarsAsync(); } + + private async void TakeScreenShotMenuItemClick(object sender, EventArgs e) + { + var control = GetCurrentTabControl(); + + if(control == null) + { + return; + } + + var chromiumWebBrowser = (ChromiumWebBrowser)control.Browser; + + var contentSize = await chromiumWebBrowser.GetContentSizeAsync(); + + //Capture current scrollable area + var viewPort = new DevTools.Page.Viewport + { + Width = contentSize.Width, + Height = contentSize.Height, + Scale = 1.0 + }; + + var data = await chromiumWebBrowser.CaptureScreenshotAsync(viewPort: viewPort, captureBeyondViewport: true); + + // Make a file to save it to (e.g. C:\Users\[user]\Desktop\CefSharp screenshot.png) + var screenshotPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "CefSharp screenshot" + DateTime.Now.Ticks + ".png"); + + File.WriteAllBytes(screenshotPath, data); + + // Tell Windows to launch the saved image. + Process.Start(new ProcessStartInfo(screenshotPath) + { + // UseShellExecute is false by default on .NET Core. + UseShellExecute = true + }); + } } } diff --git a/CefSharp.WinForms/ChromiumWebBrowser.cs b/CefSharp.WinForms/ChromiumWebBrowser.cs index 91d6ce4c84..086dfacba4 100644 --- a/CefSharp.WinForms/ChromiumWebBrowser.cs +++ b/CefSharp.WinForms/ChromiumWebBrowser.cs @@ -12,6 +12,8 @@ using CefSharp.Web; using CefSharp.WinForms.Internals; using CefSharp.WinForms.Host; +using CefSharp.DevTools.Page; +using System.Threading.Tasks; namespace CefSharp.WinForms { @@ -477,6 +479,28 @@ public void Load(string url) } } + /// + /// 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(); + + using (var devToolsClient = browser.GetDevToolsClient()) + { + var screenShot = await devToolsClient.Page.CaptureScreenshotAsync(format, quality, viewPort, fromSurface, captureBeyondViewport).ConfigureAwait(continueOnCapturedContext: false); + + return screenShot.Data; + } + } + /// /// The javascript object repository, one repository per ChromiumWebBrowser instance. /// diff --git a/CefSharp/IWebBrowser.cs b/CefSharp/IWebBrowser.cs index 804531ac5f..eaa28aa909 100644 --- a/CefSharp/IWebBrowser.cs +++ b/CefSharp/IWebBrowser.cs @@ -169,5 +169,11 @@ public interface IWebBrowser : IChromiumWebBrowserBase /// When this method returns, contains the object reference that matches the specified , or null if no matching instance found. /// true if a instance was found matching ; otherwise, false. bool TryGetBrowserCoreById(int browserId, out IBrowser browser); + + /// + /// Size of scrollable area in CSS pixels + /// + /// A task that can be awaited to get the size of the scrollable area in CSS pixels. + Task GetContentSizeAsync(); } } diff --git a/CefSharp/Internals/Partial/ChromiumWebBrowser.Partial.cs b/CefSharp/Internals/Partial/ChromiumWebBrowser.Partial.cs index 33d7e4f3a7..8e41683639 100644 --- a/CefSharp/Internals/Partial/ChromiumWebBrowser.Partial.cs +++ b/CefSharp/Internals/Partial/ChromiumWebBrowser.Partial.cs @@ -399,6 +399,21 @@ public bool TryGetBrowserCoreById(int browserId, out IBrowser browser) 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); + + return layoutMetricsResponse.CssContentSize; + } + } + private void InitialLoad(bool? isLoading, CefErrorCode? errorCode) { if(IsDisposed)