diff --git a/CefSharp.Wpf/CefSharp.Wpf.csproj b/CefSharp.Wpf/CefSharp.Wpf.csproj
index 99fecfbc03..9cccaeeaa1 100644
--- a/CefSharp.Wpf/CefSharp.Wpf.csproj
+++ b/CefSharp.Wpf/CefSharp.Wpf.csproj
@@ -78,6 +78,10 @@
+
+
+
+
diff --git a/CefSharp.Wpf/ChromiumWebBrowser.cs b/CefSharp.Wpf/ChromiumWebBrowser.cs
index c3a0baf02d..92ee166b18 100644
--- a/CefSharp.Wpf/ChromiumWebBrowser.cs
+++ b/CefSharp.Wpf/ChromiumWebBrowser.cs
@@ -2,10 +2,8 @@
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
-using CefSharp.Internals;
-using CefSharp.Wpf.Internals;
-using Microsoft.Win32.SafeHandles;
using System;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -16,10 +14,12 @@
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
-using System.Runtime.CompilerServices;
+using CefSharp.Internals;
+using CefSharp.Wpf.Internals;
using CefSharp.Wpf.Rendering;
+using Microsoft.Win32.SafeHandles;
-namespace CefSharp.Wpf
+namespace CefSharp.Wpf
{
///
/// ChromiumWebBrowser is the WPF web browser control
@@ -34,10 +34,6 @@ public class ChromiumWebBrowser : ContentControl, IRenderWebBrowser, IWpfWebBrow
///
private HwndSource source;
///
- /// The source hook
- ///
- private HwndSourceHook sourceHook;
- ///
/// The tooltip timer
///
private DispatcherTimer tooltipTimer;
@@ -94,6 +90,10 @@ public class ChromiumWebBrowser : ContentControl, IRenderWebBrowser, IWpfWebBrow
/// user attempts to set after browser created)
///
private IRequestContext requestContext;
+ ///
+ /// Handles keypress and text typing
+ ///
+ private IWpfKeyboardHandler wpfKeyboardHandler;
///
/// A flag that indicates whether or not the designer is active
@@ -207,6 +207,26 @@ public IRequestContext RequestContext
///
/// The find handler.
public IFindHandler FindHandler { get; set; }
+ ///
+ /// Legacy keyboard handler uses WindowProc callback interceptor to handle keypress events.
+ /// Only use this method if you find problems with the default keyboard handling mechanism.
+ /// This might be removed in the future.
+ ///
+ public bool UseLegacyKeyboardHandler
+ {
+ get
+ {
+ return wpfKeyboardHandler is WpfLegacyKeyboardHandler;
+ }
+ set
+ {
+ if (value != UseLegacyKeyboardHandler) {
+ wpfKeyboardHandler.Dispose();
+ wpfKeyboardHandler = CreateWpfKeyboardHandler(this, value);
+ wpfKeyboardHandler.Setup(source);
+ }
+ }
+ }
///
/// Event handler for receiving Javascript console messages being sent from web pages.
@@ -470,6 +490,8 @@ private void NoInliningConstructor()
BrowserSettings = new BrowserSettings();
BitmapFactory = new InteropBitmapFactory();
+ wpfKeyboardHandler = CreateWpfKeyboardHandler(this);
+
PresentationSource.AddSourceChangedHandler(this, PresentationSourceChangedHandler);
RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.HighQuality);
@@ -584,7 +606,8 @@ protected virtual void Dispose(bool isDisposing)
Cef.RemoveDisposable(this);
- RemoveSourceHook();
+ wpfKeyboardHandler.Dispose();
+ source = null;
}
}
@@ -1530,8 +1553,8 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
var notifyDpiChanged = DpiScaleFactor > 0 && !DpiScaleFactor.Equals(matrix.M11);
DpiScaleFactor = source.CompositionTarget.TransformToDevice.M11;
- sourceHook = SourceHook;
- source.AddHook(sourceHook);
+
+ wpfKeyboardHandler.Setup(source);
if (notifyDpiChanged && browser != null)
{
@@ -1566,7 +1589,7 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA
}
else if (args.OldSource != null)
{
- RemoveSourceHook();
+ wpfKeyboardHandler.Dispose();
var window = args.OldSource.RootVisual as Window;
if (window != null)
@@ -1618,18 +1641,6 @@ private void OnWindowLocationChanged(object sender, EventArgs e)
UpdateBrowserScreenLocation();
}
- ///
- /// Removes the source hook.
- ///
- private void RemoveSourceHook()
- {
- if (source != null && sourceHook != null)
- {
- source.RemoveHook(sourceHook);
- source = null;
- }
- }
-
///
/// Create the underlying Browser instance, can be overriden to defer control creation
/// The browser will only be created when size > Size(0,0). If you specify a positive
@@ -1838,60 +1849,6 @@ private Popup CreatePopup()
return newPopup;
}
- ///
- /// WindowProc callback interceptor. Handles Windows messages intended for the source hWnd, and passes them to the
- /// contained browser as needed.
- ///
- /// The source handle.
- /// The message.
- /// Additional message info.
- /// Even more message info.
- /// if set to true, the event has already been handled by someone else.
- /// IntPtr.
- protected virtual IntPtr SourceHook(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
- {
- if (handled)
- {
- return IntPtr.Zero;
- }
-
- switch ((WM)message)
- {
- case WM.SYSCHAR:
- case WM.SYSKEYDOWN:
- case WM.SYSKEYUP:
- case WM.KEYDOWN:
- case WM.KEYUP:
- case WM.CHAR:
- case WM.IME_CHAR:
- {
- if (!IsKeyboardFocused)
- {
- break;
- }
-
- if (message == (int)WM.SYSKEYDOWN &&
- wParam.ToInt32() == KeyInterop.VirtualKeyFromKey(Key.F4))
- {
- // We don't want CEF to receive this event (and mark it as handled), since that makes it impossible to
- // shut down a CefSharp-based app by pressing Alt-F4, which is kind of bad.
- return IntPtr.Zero;
- }
-
- if (browser != null)
- {
- browser.GetHost().SendKeyEvent(message, wParam.CastToInt32(), lParam.CastToInt32());
- handled = true;
- }
-
- break;
- }
- }
-
- return IntPtr.Zero;
- }
-
- ///
/// Converts a .NET Drag event to a CefSharp MouseEvent
///
/// The instance containing the event data.
@@ -2005,7 +1962,7 @@ protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (!e.Handled)
{
- OnPreviewKey(e);
+ wpfKeyboardHandler.HandleKeyPress(e);
}
base.OnPreviewKeyDown(e);
@@ -2020,37 +1977,24 @@ protected override void OnPreviewKeyUp(KeyEventArgs e)
{
if (!e.Handled)
{
- OnPreviewKey(e);
+ wpfKeyboardHandler.HandleKeyPress(e);
}
base.OnPreviewKeyUp(e);
}
///
- /// Handles the event.
+ /// Handles the event.
///
- /// The instance containing the event data.
- private void OnPreviewKey(KeyEventArgs e)
+ /// The instance containing the event data.
+ protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
- // As KeyDown and KeyUp bubble, it appears they're being handled before they get a chance to
- // trigger the appropriate WM_ messages handled by our SourceHook, so we have to handle these extra keys here.
- // Hooking the Tab key like this makes the tab focusing in essence work like
- // KeyboardNavigation.TabNavigation="Cycle"; you will never be able to Tab out of the web browser control.
- // We also add the condition to allow ctrl+a to work when the web browser control is put inside listbox.
- if (e.Key == Key.Tab || e.Key == Key.Home || e.Key == Key.End || e.Key == Key.Up
- || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right
- || (e.Key == Key.A && Keyboard.Modifiers == ModifierKeys.Control))
+ if (!e.Handled)
{
- var modifiers = e.GetModifiers();
- var message = (int)(e.IsDown ? WM.KEYDOWN : WM.KEYUP);
- var virtualKey = KeyInterop.VirtualKeyFromKey(e.Key);
-
- if(browser != null)
- {
- browser.GetHost().SendKeyEvent(message, virtualKey, (int)modifiers);
- e.Handled = true;
- }
+ wpfKeyboardHandler.HandleTextInput(e);
}
+
+ base.OnPreviewTextInput(e);
}
///
@@ -2323,6 +2267,18 @@ private bool InternalIsBrowserInitialized()
return Interlocked.CompareExchange(ref browserInitialized, 0, 0) == 1;
}
+ private static IWpfKeyboardHandler CreateWpfKeyboardHandler(ChromiumWebBrowser owner, bool useLegacyHandler = false)
+ {
+ if (useLegacyHandler)
+ {
+ return new WpfLegacyKeyboardHandler(owner);
+ }
+ else
+ {
+ return new WpfKeyboardHandler(owner);
+ }
+ }
+
//protected override void OnManipulationDelta(ManipulationDeltaEventArgs e)
//{
// base.OnManipulationDelta(e);
diff --git a/CefSharp.Wpf/Internals/IWpfKeyboardHandler.cs b/CefSharp.Wpf/Internals/IWpfKeyboardHandler.cs
new file mode 100644
index 0000000000..3038f83b33
--- /dev/null
+++ b/CefSharp.Wpf/Internals/IWpfKeyboardHandler.cs
@@ -0,0 +1,20 @@
+// Copyright © 2010-2017 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;
+using System.Windows.Interop;
+
+namespace CefSharp.Wpf.Internals
+{
+ ///
+ /// Implement this interface to handle events related to keyboard input.
+ ///
+ internal interface IWpfKeyboardHandler : IDisposable
+ {
+ void Setup(HwndSource source);
+ void HandleKeyPress(KeyEventArgs e);
+ void HandleTextInput(TextCompositionEventArgs e);
+ }
+}
\ No newline at end of file
diff --git a/CefSharp.Wpf/Internals/VirtualKeys.cs b/CefSharp.Wpf/Internals/VirtualKeys.cs
new file mode 100644
index 0000000000..34f3b35e7a
--- /dev/null
+++ b/CefSharp.Wpf/Internals/VirtualKeys.cs
@@ -0,0 +1,405 @@
+// Copyright © 2010-2017 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.Internals
+{
+ ///
+ /// Enumeration for virtual keys taken from http://www.pinvoke.net/default.aspx/Enums/VirtualKeys.html
+ ///
+ public enum VirtualKeys : ushort
+ {
+ ///
+ LeftButton = 0x01,
+ ///
+ RightButton = 0x02,
+ ///
+ Cancel = 0x03,
+ ///
+ MiddleButton = 0x04,
+ ///
+ ExtraButton1 = 0x05,
+ ///
+ ExtraButton2 = 0x06,
+ ///
+ Back = 0x08,
+ ///
+ Tab = 0x09,
+ ///
+ Clear = 0x0C,
+ ///
+ Return = 0x0D,
+ ///
+ Shift = 0x10,
+ ///
+ Control = 0x11,
+ ///
+ Menu = 0x12,
+ ///
+ Pause = 0x13,
+ ///
+ CapsLock = 0x14,
+ ///
+ Kana = 0x15,
+ ///
+ Hangeul = 0x15,
+ ///
+ Hangul = 0x15,
+ ///
+ Junja = 0x17,
+ ///
+ Final = 0x18,
+ ///
+ Hanja = 0x19,
+ ///
+ Kanji = 0x19,
+ ///
+ Escape = 0x1B,
+ ///
+ Convert = 0x1C,
+ ///
+ NonConvert = 0x1D,
+ ///
+ Accept = 0x1E,
+ ///
+ ModeChange = 0x1F,
+ ///
+ Space = 0x20,
+ ///
+ Prior = 0x21,
+ ///
+ Next = 0x22,
+ ///
+ End = 0x23,
+ ///
+ Home = 0x24,
+ ///
+ Left = 0x25,
+ ///
+ Up = 0x26,
+ ///
+ Right = 0x27,
+ ///
+ Down = 0x28,
+ ///
+ Select = 0x29,
+ ///
+ Print = 0x2A,
+ ///
+ Execute = 0x2B,
+ ///
+ Snapshot = 0x2C,
+ ///
+ Insert = 0x2D,
+ ///
+ Delete = 0x2E,
+ ///
+ Help = 0x2F,
+ ///
+ N0 = 0x30,
+ ///
+ N1 = 0x31,
+ ///
+ N2 = 0x32,
+ ///
+ N3 = 0x33,
+ ///
+ N4 = 0x34,
+ ///
+ N5 = 0x35,
+ ///
+ N6 = 0x36,
+ ///
+ N7 = 0x37,
+ ///
+ N8 = 0x38,
+ ///
+ N9 = 0x39,
+ ///
+ A = 0x41,
+ ///
+ B = 0x42,
+ ///
+ C = 0x43,
+ ///
+ D = 0x44,
+ ///
+ E = 0x45,
+ ///
+ F = 0x46,
+ ///
+ G = 0x47,
+ ///
+ H = 0x48,
+ ///
+ I = 0x49,
+ ///
+ J = 0x4A,
+ ///
+ K = 0x4B,
+ ///
+ L = 0x4C,
+ ///
+ M = 0x4D,
+ ///
+ N = 0x4E,
+ ///
+ O = 0x4F,
+ ///
+ P = 0x50,
+ ///
+ Q = 0x51,
+ ///
+ R = 0x52,
+ ///
+ S = 0x53,
+ ///
+ T = 0x54,
+ ///
+ U = 0x55,
+ ///
+ V = 0x56,
+ ///
+ W = 0x57,
+ ///
+ X = 0x58,
+ ///
+ Y = 0x59,
+ ///
+ Z = 0x5A,
+ ///
+ LeftWindows = 0x5B,
+ ///
+ RightWindows = 0x5C,
+ ///
+ Application = 0x5D,
+ ///
+ Sleep = 0x5F,
+ ///
+ Numpad0 = 0x60,
+ ///
+ Numpad1 = 0x61,
+ ///
+ Numpad2 = 0x62,
+ ///
+ Numpad3 = 0x63,
+ ///
+ Numpad4 = 0x64,
+ ///
+ Numpad5 = 0x65,
+ ///
+ Numpad6 = 0x66,
+ ///
+ Numpad7 = 0x67,
+ ///
+ Numpad8 = 0x68,
+ ///
+ Numpad9 = 0x69,
+ ///
+ Multiply = 0x6A,
+ ///
+ Add = 0x6B,
+ ///
+ Separator = 0x6C,
+ ///
+ Subtract = 0x6D,
+ ///
+ Decimal = 0x6E,
+ ///
+ Divide = 0x6F,
+ ///
+ F1 = 0x70,
+ ///
+ F2 = 0x71,
+ ///
+ F3 = 0x72,
+ ///
+ F4 = 0x73,
+ ///
+ F5 = 0x74,
+ ///
+ F6 = 0x75,
+ ///
+ F7 = 0x76,
+ ///
+ F8 = 0x77,
+ ///
+ F9 = 0x78,
+ ///
+ F10 = 0x79,
+ ///
+ F11 = 0x7A,
+ ///
+ F12 = 0x7B,
+ ///
+ F13 = 0x7C,
+ ///
+ F14 = 0x7D,
+ ///
+ F15 = 0x7E,
+ ///
+ F16 = 0x7F,
+ ///
+ F17 = 0x80,
+ ///
+ F18 = 0x81,
+ ///
+ F19 = 0x82,
+ ///
+ F20 = 0x83,
+ ///
+ F21 = 0x84,
+ ///
+ F22 = 0x85,
+ ///
+ F23 = 0x86,
+ ///
+ F24 = 0x87,
+ ///
+ NumLock = 0x90,
+ ///
+ ScrollLock = 0x91,
+ ///
+ NEC_Equal = 0x92,
+ ///
+ Fujitsu_Jisho = 0x92,
+ ///
+ Fujitsu_Masshou = 0x93,
+ ///
+ Fujitsu_Touroku = 0x94,
+ ///
+ Fujitsu_Loya = 0x95,
+ ///
+ Fujitsu_Roya = 0x96,
+ ///
+ LeftShift = 0xA0,
+ ///
+ RightShift = 0xA1,
+ ///
+ LeftControl = 0xA2,
+ ///
+ RightControl = 0xA3,
+ ///
+ LeftMenu = 0xA4,
+ ///
+ RightMenu = 0xA5,
+ ///
+ BrowserBack = 0xA6,
+ ///
+ BrowserForward = 0xA7,
+ ///
+ BrowserRefresh = 0xA8,
+ ///
+ BrowserStop = 0xA9,
+ ///
+ BrowserSearch = 0xAA,
+ ///
+ BrowserFavorites = 0xAB,
+ ///
+ BrowserHome = 0xAC,
+ ///
+ VolumeMute = 0xAD,
+ ///
+ VolumeDown = 0xAE,
+ ///
+ VolumeUp = 0xAF,
+ ///
+ MediaNextTrack = 0xB0,
+ ///
+ MediaPrevTrack = 0xB1,
+ ///
+ MediaStop = 0xB2,
+ ///
+ MediaPlayPause = 0xB3,
+ ///
+ LaunchMail = 0xB4,
+ ///
+ LaunchMediaSelect = 0xB5,
+ ///
+ LaunchApplication1 = 0xB6,
+ ///
+ LaunchApplication2 = 0xB7,
+ ///
+ OEM1 = 0xBA,
+ ///
+ OEMPlus = 0xBB,
+ ///
+ OEMComma = 0xBC,
+ ///
+ OEMMinus = 0xBD,
+ ///
+ OEMPeriod = 0xBE,
+ ///
+ OEM2 = 0xBF,
+ ///
+ OEM3 = 0xC0,
+ ///
+ OEM4 = 0xDB,
+ ///
+ OEM5 = 0xDC,
+ ///
+ OEM6 = 0xDD,
+ ///
+ OEM7 = 0xDE,
+ ///
+ OEM8 = 0xDF,
+ ///
+ OEMAX = 0xE1,
+ ///
+ OEM102 = 0xE2,
+ ///
+ ICOHelp = 0xE3,
+ ///
+ ICO00 = 0xE4,
+ ///
+ ProcessKey = 0xE5,
+ ///
+ ICOClear = 0xE6,
+ ///
+ Packet = 0xE7,
+ ///
+ OEMReset = 0xE9,
+ ///
+ OEMJump = 0xEA,
+ ///
+ OEMPA1 = 0xEB,
+ ///
+ OEMPA2 = 0xEC,
+ ///
+ OEMPA3 = 0xED,
+ ///
+ OEMWSCtrl = 0xEE,
+ ///
+ OEMCUSel = 0xEF,
+ ///
+ OEMATTN = 0xF0,
+ ///
+ OEMFinish = 0xF1,
+ ///
+ OEMCopy = 0xF2,
+ ///
+ OEMAuto = 0xF3,
+ ///
+ OEMENLW = 0xF4,
+ ///
+ OEMBackTab = 0xF5,
+ ///
+ ATTN = 0xF6,
+ ///
+ CRSel = 0xF7,
+ ///
+ EXSel = 0xF8,
+ ///
+ EREOF = 0xF9,
+ ///
+ Play = 0xFA,
+ ///
+ Zoom = 0xFB,
+ ///
+ Noname = 0xFC,
+ ///
+ PA1 = 0xFD,
+ ///
+ OEMClear = 0xFE
+ }
+}
\ No newline at end of file
diff --git a/CefSharp.Wpf/Internals/WpfKeyboardHandler.cs b/CefSharp.Wpf/Internals/WpfKeyboardHandler.cs
new file mode 100644
index 0000000000..9986d5f155
--- /dev/null
+++ b/CefSharp.Wpf/Internals/WpfKeyboardHandler.cs
@@ -0,0 +1,100 @@
+// Copyright © 2010-2017 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;
+using System.Windows.Interop;
+
+namespace CefSharp.Wpf.Internals
+{
+ internal class WpfKeyboardHandler : IWpfKeyboardHandler
+ {
+ ///
+ /// The owner browser instance
+ ///
+ private readonly ChromiumWebBrowser owner;
+
+ public WpfKeyboardHandler(ChromiumWebBrowser owner)
+ {
+ this.owner = owner;
+ }
+
+ public void Setup(HwndSource source)
+ {
+ // nothing to do here
+ }
+
+ public void Dispose()
+ {
+ // nothing to do here
+ }
+
+ public void HandleKeyPress(KeyEventArgs e)
+ {
+ var browser = owner.GetBrowser();
+ var key = e.SystemKey != Key.None ? e.SystemKey : e.Key;
+ if (browser != null)
+ {
+ int message;
+ int virtualKey = 0;
+ var modifiers = e.GetModifiers();
+
+ switch (key)
+ {
+ case Key.LeftAlt:
+ case Key.RightAlt:
+ virtualKey = (int) VirtualKeys.Menu;
+ break;
+
+ case Key.LeftCtrl:
+ case Key.RightCtrl:
+ virtualKey = (int) VirtualKeys.Control;
+ break;
+
+ case Key.LeftShift:
+ case Key.RightShift:
+ virtualKey = (int) VirtualKeys.Shift;
+ break;
+
+ default:
+ virtualKey = KeyInterop.VirtualKeyFromKey(key);
+ break;
+ }
+
+ if (e.IsDown)
+ {
+ message = (int)(e.SystemKey != Key.None ? WM.SYSKEYDOWN : WM.KEYDOWN);
+ } else {
+ message = (int)(e.SystemKey != Key.None ? WM.SYSKEYUP : WM.KEYUP);
+ }
+
+ browser.GetHost().SendKeyEvent(message, virtualKey, (int)modifiers);
+ }
+
+ // Hooking the Tab key like this makes the tab focusing in essence work like
+ // KeyboardNavigation.TabNavigation="Cycle"; you will never be able to Tab out of the web browser control.
+ // We also add the condition to allow ctrl+a to work when the web browser control is put inside listbox.
+ // Prevent keyboard navigation using arrows and home and end keys
+ if (key == Key.Tab || key == Key.Home || key == Key.End || key == Key.Up
+ || key == Key.Down || key == Key.Left || key == Key.Right
+ || (key == Key.A && Keyboard.Modifiers == ModifierKeys.Control))
+ {
+ e.Handled = true;
+ }
+ }
+
+ public void HandleTextInput(TextCompositionEventArgs e)
+ {
+ var browser = owner.GetBrowser();
+ if (browser != null)
+ {
+ var browserHost = browser.GetHost();
+ for (int i = 0; i < e.Text.Length; i++)
+ {
+ browserHost.SendKeyEvent((int)WM.CHAR, e.Text[i], 0);
+ }
+ e.Handled = true;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CefSharp.Wpf/Internals/WpfLegacyKeyboardHandler.cs b/CefSharp.Wpf/Internals/WpfLegacyKeyboardHandler.cs
new file mode 100644
index 0000000000..7e9ae2002c
--- /dev/null
+++ b/CefSharp.Wpf/Internals/WpfLegacyKeyboardHandler.cs
@@ -0,0 +1,136 @@
+// Copyright © 2010-2017 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;
+using System.Windows.Interop;
+using CefSharp.Internals;
+
+namespace CefSharp.Wpf.Internals
+{
+ internal class WpfLegacyKeyboardHandler : IWpfKeyboardHandler
+ {
+ ///
+ /// The source hook
+ ///
+ private HwndSourceHook sourceHook;
+
+ ///
+ /// The source
+ ///
+ private HwndSource source;
+
+ ///
+ /// The owner browser instance
+ ///
+ private readonly ChromiumWebBrowser owner;
+
+ public WpfLegacyKeyboardHandler(ChromiumWebBrowser owner)
+ {
+ this.owner = owner;
+ }
+
+ public void Setup(HwndSource source)
+ {
+ this.source = source;
+ if (source != null)
+ {
+ sourceHook = SourceHook;
+ source.AddHook(SourceHook);
+ }
+ }
+
+ public void Dispose()
+ {
+ if (source != null && sourceHook != null)
+ {
+ source.RemoveHook(sourceHook);
+ }
+ source = null;
+ }
+
+ ///
+ /// WindowProc callback interceptor. Handles Windows messages intended for the source hWnd, and passes them to the
+ /// contained browser as needed.
+ ///
+ /// The source handle.
+ /// The message.
+ /// Additional message info.
+ /// Even more message info.
+ /// if set to true, the event has already been handled by someone else.
+ /// IntPtr.
+ protected IntPtr SourceHook(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
+ {
+ if (handled)
+ {
+ return IntPtr.Zero;
+ }
+
+ switch ((WM)message)
+ {
+ case WM.SYSCHAR:
+ case WM.SYSKEYDOWN:
+ case WM.SYSKEYUP:
+ case WM.KEYDOWN:
+ case WM.KEYUP:
+ case WM.CHAR:
+ case WM.IME_CHAR:
+ {
+ if (!owner.IsKeyboardFocused)
+ {
+ break;
+ }
+
+ if (message == (int)WM.SYSKEYDOWN &&
+ wParam.ToInt32() == KeyInterop.VirtualKeyFromKey(Key.F4))
+ {
+ // We don't want CEF to receive this event (and mark it as handled), since that makes it impossible to
+ // shut down a CefSharp-based app by pressing Alt-F4, which is kind of bad.
+ return IntPtr.Zero;
+ }
+
+ var browser = owner.GetBrowser();
+ if (browser != null)
+ {
+ browser.GetHost().SendKeyEvent(message, wParam.CastToInt32(), lParam.CastToInt32());
+ handled = true;
+ }
+
+ break;
+ }
+ }
+
+ return IntPtr.Zero;
+ }
+
+ public void HandleKeyPress(KeyEventArgs e)
+ {
+ // As KeyDown and KeyUp bubble, it appears they're being handled before they get a chance to
+ // trigger the appropriate WM_ messages handled by our SourceHook, so we have to handle these extra keys here.
+ // Hooking the Tab key like this makes the tab focusing in essence work like
+ // KeyboardNavigation.TabNavigation="Cycle"; you will never be able to Tab out of the web browser control.
+ // We also add the condition to allow ctrl+a to work when the web browser control is put inside listbox.
+ if (e.Key == Key.Tab || e.Key == Key.Home || e.Key == Key.End || e.Key == Key.Up
+ || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right
+ || (e.Key == Key.A && Keyboard.Modifiers == ModifierKeys.Control))
+ {
+ var modifiers = e.GetModifiers();
+ var message = (int)(e.IsDown ? WM.KEYDOWN : WM.KEYUP);
+ var virtualKey = KeyInterop.VirtualKeyFromKey(e.Key);
+ var browser = owner.GetBrowser();
+
+ if (browser != null)
+ {
+ browser.GetHost().SendKeyEvent(message, virtualKey, (int)modifiers);
+ e.Handled = true;
+ }
+ }
+ }
+
+ public void HandleTextInput(TextCompositionEventArgs e)
+ {
+ // nothing to do here
+ }
+ }
+}