// Copyright © 2021 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.ComponentModel; using System.Drawing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows.Forms; namespace CefSharp.WinForms.Host { /// /// Chromium Browser Host Control, provides base functionality for hosting a /// CefBrowser instance (main browser and popups) in WinForms. /// /// public abstract class ChromiumHostControlBase : Control { [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); /// /// IntPtr that represents the CefBrowser Hwnd /// Used for sending messages to the browser /// e.g. resize /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IntPtr BrowserHwnd { get; set; } /// /// Set to true while handing an activating WM_ACTIVATE message. /// MUST ONLY be cleared by DefaultFocusHandler. /// /// true if this instance is activating; otherwise, false. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), DefaultValue(false)] public bool IsActivating { get; set; } /// /// Event called after the underlying CEF browser instance has been created. /// 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 IsBrowserInitializedChanged; /// /// Gets the default size of the control. /// /// /// The default of the control. /// protected override Size DefaultSize { get { return new Size(200, 100); } } /// /// Makes certain keys as Input keys when CefSettings.MultiThreadedMessageLoop = false /// /// key data /// true for a select list of keys otherwise defers to base.IsInputKey protected override bool IsInputKey(Keys keyData) { //This code block is only called/required when CEF is running in the //same message loop as the WinForms UI (CefSettings.MultiThreadedMessageLoop = false) //Without this code, arrows and tab won't be processed switch (keyData) { case Keys.Right: case Keys.Left: case Keys.Up: case Keys.Down: case Keys.Tab: { return true; } case Keys.Shift | Keys.Tab: case Keys.Shift | Keys.Right: case Keys.Shift | Keys.Left: case Keys.Shift | Keys.Up: case Keys.Shift | Keys.Down: { return true; } } return base.IsInputKey(keyData); } /// /// Raises the event. /// /// An that contains the event data. protected override void OnSizeChanged(EventArgs e) { ResizeBrowser(Width, Height); base.OnSizeChanged(e); } /// protected override void OnVisibleChanged(EventArgs e) { if (Visible) { ShowInternal(); } else { HideInternal(); } base.OnVisibleChanged(e); } /// /// Resizes the browser to the specified and . /// If and are both 0 then the browser /// will be hidden and resource usage will be minimised. /// /// width /// height protected virtual void ResizeBrowser(int width, int height) { if (BrowserHwnd != IntPtr.Zero) { SetWindowPosition(BrowserHwnd, 0, 0, width, height); } } /// /// When minimized set the browser window size to 0x0 to reduce resource usage. /// https://github.com/chromiumembedded/cef/blob/c7701b8a6168f105f2c2d6b239ce3958da3e3f13/tests/cefclient/browser/browser_window_std_win.cc#L87 /// internal virtual void HideInternal() { if (BrowserHwnd != IntPtr.Zero) { SetWindowPosition(BrowserHwnd, 0, 0, 0, 0); } } /// /// Show the browser (called after previous minimised) /// internal virtual void ShowInternal() { if (BrowserHwnd != IntPtr.Zero) { SetWindowPosition(BrowserHwnd, 0, 0, Width, Height); } } /// protected override void Dispose(bool disposing) { if (disposing) { BrowserHwnd = IntPtr.Zero; IsBrowserInitializedChanged = null; } base.Dispose(disposing); } /// /// Trigger the event /// internal void RaiseIsBrowserInitializedChangedEvent() { IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty); } private void SetWindowPosition(IntPtr handle, int x, int y, int width, int height) { const uint SWP_NOMOVE = 0x0002; const uint SWP_NOZORDER = 0x0004; const uint SWP_NOACTIVATE = 0x0010; if (handle != IntPtr.Zero) { if (width == 0 && height == 0) { // For windowed browsers when the frame window is minimized set the // browser window size to 0x0 to reduce resource usage. SetWindowPos(handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); } else { SetWindowPos(handle, IntPtr.Zero, x, y, width, height, SWP_NOZORDER); } } } /// /// Gets the or associated with /// a specific instance. /// /// browser /// returns the assocaited or or null if Disposed or no host found. public static T FromBrowser(IBrowser browser) where T : ChromiumHostControlBase { if (browser.IsDisposed) { return null; } var windowHandle = browser.GetHost().GetWindowHandle(); if (windowHandle == IntPtr.Zero) { return null; } var control = Control.FromChildHandle(windowHandle) as T; return control; } } }