diff --git a/CefSharp.Core/Internals/CefBrowserHostWrapper.cpp b/CefSharp.Core/Internals/CefBrowserHostWrapper.cpp index 278f4bf38f..e4bdc032c6 100644 --- a/CefSharp.Core/Internals/CefBrowserHostWrapper.cpp +++ b/CefSharp.Core/Internals/CefBrowserHostWrapper.cpp @@ -333,7 +333,7 @@ void CefBrowserHostWrapper::ImeSetComposition(String^ text, cli::arrayImeSetComposition(StringUtils::ToNative(text), underlinesVector, CefRange(), range); + _browserHost->ImeSetComposition(StringUtils::ToNative(text), underlinesVector, CefRange(UINT32_MAX, UINT32_MAX), range); } void CefBrowserHostWrapper::ImeCommitText(String^ text) @@ -341,7 +341,7 @@ void CefBrowserHostWrapper::ImeCommitText(String^ text) ThrowIfDisposed(); //Range and cursor position are Mac OSX only - _browserHost->ImeCommitText(StringUtils::ToNative(text), CefRange(), NULL); + _browserHost->ImeCommitText(StringUtils::ToNative(text), CefRange(UINT32_MAX, UINT32_MAX), NULL); } void CefBrowserHostWrapper::ImeFinishComposingText(bool keepSelection) diff --git a/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj b/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj index a16ddbce91..68b8e44c48 100644 --- a/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj +++ b/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj @@ -107,15 +107,19 @@ Designer + + + + SimpleMainWindow.xaml diff --git a/CefSharp.Wpf.Example/Controls/ChromiumWebBrowserIMESupport.cs b/CefSharp.Wpf.Example/Controls/ChromiumWebBrowserIMESupport.cs new file mode 100644 index 0000000000..5c1360a386 --- /dev/null +++ b/CefSharp.Wpf.Example/Controls/ChromiumWebBrowserIMESupport.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; +using CefSharp.Structs; +using CefSharp.Wpf.Example.Handlers; + +namespace CefSharp.Wpf.Example.Controls +{ + public class ChromiumWebBrowserIMESupport : ChromiumWebBrowser + { + public ChromiumWebBrowserIMESupport() + { + WpfKeyboardHandler = new IMEWpfKeyboardHandler(this); + } + + static ChromiumWebBrowserIMESupport() + { + InputMethod.IsInputMethodEnabledProperty.OverrideMetadata( + typeof(ChromiumWebBrowserIMESupport), + new FrameworkPropertyMetadata( + true, + FrameworkPropertyMetadataOptions.Inherits, + (obj, e) => + { + var browser = obj as ChromiumWebBrowserIMESupport; + if ((bool)e.NewValue && browser.GetBrowserHost() != null && Keyboard.FocusedElement == browser) + { + browser.GetBrowserHost().SendFocusEvent(true); + InputMethod.SetIsInputMethodSuspended(browser, true); + } + })); + + InputMethod.IsInputMethodSuspendedProperty.OverrideMetadata( + typeof(ChromiumWebBrowserIMESupport), + new FrameworkPropertyMetadata( + true, + FrameworkPropertyMetadataOptions.Inherits)); + } + + protected override void OnImeCompositionRangeChanged(Range selectedRange, Structs.Rect[] characterBounds) + { + var imeKeyboardHandler = WpfKeyboardHandler as IMEWpfKeyboardHandler; + if (imeKeyboardHandler.IsActive) + { + var screenInfo = GetScreenInfo(); + var scaleFactor = screenInfo.HasValue ? screenInfo.Value.DeviceScaleFactor : 1.0f; + + UiThreadRunSync(() => + { + var parentWindow = GetParentWindow(); + if (parentWindow != null) + { + var point = TransformToAncestor(parentWindow).Transform(new System.Windows.Point(0, 0)); + var rects = new List(); + + foreach (var item in characterBounds) + rects.Add(new Structs.Rect( + (int)((point.X + item.X) * scaleFactor), + (int)((point.Y + item.Y) * scaleFactor), + (int)(item.Width * scaleFactor), + (int)(item.Height * scaleFactor))); + + imeKeyboardHandler.ChangeCompositionRange(selectedRange, rects); + } + }); + } + + Visual GetParentWindow() + { + var current = VisualTreeHelper.GetParent(this); + while (current != null && !(current is Window)) + current = VisualTreeHelper.GetParent(current); + + return current as Window; + } + } + } +} diff --git a/CefSharp.Wpf.Example/Handlers/IMEWpfKeyboardHandler.cs b/CefSharp.Wpf.Example/Handlers/IMEWpfKeyboardHandler.cs new file mode 100644 index 0000000000..b4414dffd5 --- /dev/null +++ b/CefSharp.Wpf.Example/Handlers/IMEWpfKeyboardHandler.cs @@ -0,0 +1,321 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Input; +using System.Windows.Interop; +using CefSharp.Structs; +using CefSharp.Wpf.Example.IME; +using CefSharp.Wpf.Internals; + +namespace CefSharp.Wpf.Example.Handlers +{ + public class IMEWpfKeyboardHandler : WpfKeyboardHandler + { + int _languageCodeId; + bool _systemCaret; + bool _isDisposed; + int _cursorIndex; + Range _compositionRange; + Rect _imeRect = new Rect(-1, -1, 0, 0); + List _compositionBounds = new List(); + HwndSource _source; + IntPtr _defaultContext; + IntPtr _browserContext; + Func _loWord; + + internal bool IsActive { get; set; } + + /// + /// The source hook + /// + private HwndSourceHook sourceHook; + + public IMEWpfKeyboardHandler(ChromiumWebBrowser owner) : base(owner) + { + if (IntPtr.Size == sizeof(Int32)) + _loWord = x => x.ToInt32(); + else + _loWord = LOWORD64; + } + + private void Owner_LostFocus(object sender, System.Windows.RoutedEventArgs e) + { + IsActive = false; + InputMethod.SetIsInputMethodEnabled(owner, false); + InputMethod.SetIsInputMethodSuspended(owner, false); + } + + private void Owner_GotFocus(object sender, System.Windows.RoutedEventArgs e) + { + InputMethod.SetIsInputMethodEnabled(owner, true); + InputMethod.SetIsInputMethodSuspended(owner, true); + IsActive = true; + } + + public override void Setup(HwndSource source) + { + _source = source; + sourceHook = SourceHook; + source.AddHook(SourceHook); + + owner.GotFocus += Owner_GotFocus; + owner.LostFocus += Owner_LostFocus; + + _defaultContext = NativeIME.ImmGetContext(_source.Handle); + _browserContext = NativeIME.ImmCreateContext(); + + NativeIME.ImmAssociateContext(_source.Handle, _browserContext); + + // TODO: need to find a better way to trigger setting context on the window + owner.Focus(); + } + + public override void Dispose() + { + if (_isDisposed) + return; + + _isDisposed = true; + + owner.GotFocus -= Owner_GotFocus; + owner.LostFocus -= Owner_LostFocus; + + NativeIME.ImmAssociateContext(_source.Handle, _defaultContext); + NativeIME.ImmDestroyContext(_browserContext); + + if (_source != null && sourceHook != null) + { + _source.RemoveHook(sourceHook); + _source = null; + } + } + + private IntPtr SourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + if (handled || owner == null || owner.GetBrowserHost() == null || owner.IsDisposed || !IsActive || _isDisposed) + return IntPtr.Zero; + + switch (msg) + { + case NativeIME.WM_IME_SETCONTEXT: + OnIMESetContext(hwnd, (uint) msg, wParam, lParam); + handled = true; + break; + + case NativeIME.WM_IME_STARTCOMPOSITION: + OnIMEStartComposition(hwnd); + handled = true; + break; + + case NativeIME.WM_IME_COMPOSITION: + OnIMEComposition(hwnd, _loWord(lParam)); + handled = true; + break; + case NativeIME.WM_IME_ENDCOMPOSITION: + OnIMEEndComposition(hwnd); + handled = true; + break; + } + + return handled ? IntPtr.Zero : new IntPtr(1); + } + + private void OnIMEComposition(IntPtr hwnd, int lParam) + { + string text = string.Empty; + + using (var handler = IMEHandler.Create(hwnd)) + { + if (handler.GetResult((uint)lParam, out text)) + { + owner.GetBrowserHost().ImeCommitText(text); + return; + } + } + + using (var handler = IMEHandler.Create(hwnd)) + { + var underlines = new List(); + int compositionStart = 0; + + if (handler.GetComposition((uint)lParam, underlines, ref compositionStart, out text)) + { + owner.GetBrowserHost().ImeSetComposition(text, underlines.ToArray(), new Range(compositionStart, compositionStart)); + + UpdateCaretPosition(compositionStart - 1); + } + } + } + + private void OnIMEEndComposition(IntPtr hwnd) + { + owner.GetBrowserHost().ImeFinishComposingText(false); + ResetComposition(); + DestroyImeWindow(hwnd); + } + + private void OnIMESetContext(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam) + { + // We handle the IME Composition Window ourselves (but let the IME Candidates + // Window be handled by IME through DefWindowProc()), so clear the + // ISC_SHOWUICOMPOSITIONWINDOW flag: + lParam = (IntPtr)(lParam.ToInt64() & ~NativeIME.ISC_SHOWUICOMPOSITIONWINDOW); + NativeIME.DefWindowProc(hwnd, msg, wParam, lParam); + // TODO: should we call ImmNotifyIME? + + CreateImeWindow(hwnd); + MoveImeWindow(hwnd); + } + + private void OnIMEStartComposition(IntPtr hwnd) + { + CreateImeWindow(hwnd); + MoveImeWindow(hwnd); + ResetComposition(); + } + + private void ResetComposition() + { + _cursorIndex = -1; + } + + private void CreateImeWindow(IntPtr hwnd) + { + // Chinese/Japanese IMEs somehow ignore function calls to + // ::ImmSetCandidateWindow(), and use the position of the current system + // caret instead -::GetCaretPos(). + // Therefore, we create a temporary system caret for Chinese IMEs and use + // it during this input context. + // Since some third-party Japanese IME also uses ::GetCaretPos() to determine + // their window position, we also create a caret for Japanese IMEs. + _languageCodeId = PrimaryLangId(InputLanguageManager.Current.CurrentInputLanguage.KeyboardLayoutId); + + if (_languageCodeId == NativeIME.LANG_JAPANESE || _languageCodeId == NativeIME.LANG_CHINESE) + if (!_systemCaret) + if (NativeIME.CreateCaret(hwnd, IntPtr.Zero, 1, 1)) + _systemCaret = true; + } + + private int PrimaryLangId(int lgid) + { + return (lgid & 0x3ff); + } + + private void MoveImeWindow(IntPtr hwnd) + { + if (!owner.IsFocused) + return; + + Rect rc = _imeRect; + int location = _cursorIndex; + + // If location is not specified fall back to the composition range start. + if (location == -1) + location = _compositionRange.From; + + // Offset location by the composition range start if required. + if (location >= _compositionRange.From) + location -= _compositionRange.From; + + if (location < _compositionBounds.Count) + rc = _compositionBounds[location]; + else + return; + + int caretMargin = 1; + + if (_languageCodeId == NativeIME.LANG_CHINESE) + { + var formPoint = new NativeIME.TagCompositionForm + { + DwStyle = NativeIME.CFS_POINT, + PtCurrentPos = new NativeIME.TagPoint + { + X = rc.X, + Y = rc.Y + }, + RcArea = new NativeIME.TagRect + { + Left = rc.X, + Top = rc.Y, + Right = rc.X + rc.Width, + Bottom = rc.Y + rc.Height + } + }; + + using (var handler = IMEHandler.Create(hwnd)) + { + NativeIME.ImmSetCompositionWindow(handler._hIMC, ref formPoint); + } + } + + if (_systemCaret) + { + if (_languageCodeId == NativeIME.LANG_JAPANESE) + { + var firstRc = _compositionBounds[0]; + NativeIME.SetCaretPos(firstRc.X, firstRc.Y + firstRc.Height); + } + else + NativeIME.SetCaretPos(rc.X, rc.Y); + } + + if (_languageCodeId == NativeIME.LANG_KOREAN) + rc = new Rect(rc.X, rc.Y + caretMargin, rc.Width, rc.Height); + + var form = new NativeIME.TagCompositionForm + { + DwStyle = NativeIME.CFS_RECT, + PtCurrentPos = new NativeIME.TagPoint + { + X = rc.X, + Y = rc.Y + }, + RcArea = new NativeIME.TagRect + { + Left = rc.X, + Top = rc.Y, + Right = rc.X + rc.Width, + Bottom = rc.Y + rc.Height + } + }; + + using (var handler = IMEHandler.Create(hwnd)) + NativeIME.ImmSetCompositionWindow(handler._hIMC, ref form); + } + + private void DestroyImeWindow(IntPtr hwnd) + { + if (_systemCaret) + { + NativeIME.DestroyCaret(); + _systemCaret = false; + } + } + + internal void ChangeCompositionRange(Range selectionRange, List bounds) + { + _compositionRange = selectionRange; + _compositionBounds = bounds; + MoveImeWindow(_source.Handle); + } + + private void UpdateCaretPosition(int index) + { + _cursorIndex = index; + MoveImeWindow(_source.Handle); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private static Int32 HIWORD64(IntPtr ptr) + { + return (Int32)((ptr.ToInt64() >> 16) & 0xFFFFFFFF); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private static Int32 LOWORD64(IntPtr ptr) + { + return (Int32)(ptr.ToInt64() & 0xFFFFFFFF); + } + } +} diff --git a/CefSharp.Wpf.Example/IME/IMEHandler.cs b/CefSharp.Wpf.Example/IME/IMEHandler.cs new file mode 100644 index 0000000000..1fdc5ac2fa --- /dev/null +++ b/CefSharp.Wpf.Example/IME/IMEHandler.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Text; +using CefSharp.Structs; +using static CefSharp.Wpf.Example.IME.NativeIME; + +namespace CefSharp.Wpf.Example.IME +{ + public class IMEHandler : IDisposable + { + internal const uint ColorUNDERLINE = 0xFFFFFFFF; // Black SkColor value for underline, + internal const uint ColorBKCOLOR = 0xFF000000; // White SkColor value for background, + + internal IntPtr _hIMC; + IntPtr _hWnd; + + internal static IMEHandler Create(IntPtr hWnd) + { + return new IMEHandler(hWnd); + } + + private IMEHandler(IntPtr hWnd) + { + _hWnd = hWnd; + _hIMC = ImmGetContext(_hWnd); + } + + public void Dispose() + { + ImmReleaseContext(_hWnd, _hIMC); + } + + private bool GetString(uint lParam, uint type, out string text) + { + text = string.Empty; + + if (!IsParam(lParam, type)) + return false; + + var strLen = ImmGetCompositionString(_hIMC, type, null, 0); + if (strLen <= 0) + return false; + + strLen += sizeof(char); // For trailing NULL - ImmGetCompositionString excludes that. + + // buffer contains char (2 bytes) + byte[] buffer = new byte[strLen]; + ImmGetCompositionString(_hIMC, type, buffer, strLen); + text = Encoding.Unicode.GetString(buffer); + text = text.Remove(text.Length - 1); // Remove trailing null + return true; + } + + internal bool GetResult(uint lParam, out string text) + { + return GetString(lParam, GCS_RESULTSTR, out text); + } + + internal bool GetComposition(uint lParam, List underlines, ref int compositionStart, out string text) + { + bool ret = GetString(lParam, GCS_COMPSTR, out text); + if (ret) + GetCompositionInfo(lParam, text, underlines, ref compositionStart); + + return ret; + } + + private void GetCompositionInfo(uint lParam, string text, List underlines, ref int compositionStart) + { + underlines.Clear(); + + int targetStart = text.Length; + int targetEnd = text.Length; + if (IsParam(lParam, GCS_COMPATTR)) + GetCompositionSelectionRange(ref targetStart, ref targetEnd); + + // Retrieve the selection range information. If CS_NOMOVECARET is specified + // it means the cursor should not be moved and we therefore place the caret at + // the beginning of the composition string. Otherwise we should honour the + // GCS_CURSORPOS value if it's available. + if (!IsParam(lParam, CS_NOMOVECARET) && IsParam(lParam, GCS_CURSORPOS)) + { + // IMM32 does not support non-zero-width selection in a composition. So + // always use the caret position as selection range. + int cursor = (int)ImmGetCompositionString(_hIMC, GCS_CURSORPOS, null, 0); + compositionStart = cursor; + } + else + compositionStart = 0; + + if (IsParam(lParam, GCS_COMPCLAUSE)) + GetCompositionUnderlines(targetStart, targetEnd, underlines); + + if (underlines.Count < 1) + { + Range range = new Range(); + bool thick = false; + if (targetStart > 0) + range = new Range(0, targetStart); + if (targetEnd > targetStart) + { + range = new Range(targetStart, targetEnd); + thick = true; + } + if (targetEnd < text.Length) + range = new Range(targetEnd, text.Length); + + underlines.Add(new CompositionUnderline(range, ColorUNDERLINE, ColorBKCOLOR, thick)); + } + } + + private void GetCompositionUnderlines(int targetStart, int targetEnd, List underlines) + { + var clauseSize = ImmGetCompositionString(_hIMC, GCS_COMPCLAUSE, null, 0); + int clauseLength = (int)clauseSize / sizeof(Int32); + if (clauseLength > 0) + { + // buffer contains 32 bytes (4 bytes) array + var clauseData = new byte[(int)clauseSize]; + ImmGetCompositionString(_hIMC, GCS_COMPCLAUSE, clauseData, clauseSize); + + for (int i = 0; i < clauseLength - 1; i++) + { + int from = BitConverter.ToInt32(clauseData, i* sizeof(Int32)); + int to = BitConverter.ToInt32(clauseData, (i + 1) * sizeof(Int32)); + + var range = new Range(from, to); + bool thick = (range.From >= targetStart && range.To <= targetEnd); + + underlines.Add(new CompositionUnderline(range, ColorUNDERLINE, ColorBKCOLOR, thick)); + } + } + } + + private void GetCompositionSelectionRange(ref int targetStart, ref int targetEnd) + { + var attribute_size = ImmGetCompositionString(_hIMC, GCS_COMPATTR, null, 0); + if (attribute_size > 0) + { + int start = 0; + int end = 0; + + // Buffer contains 8bit array + var attributeData = new byte[attribute_size]; + ImmGetCompositionString(_hIMC, GCS_COMPATTR, attributeData, attribute_size); + + for (start = 0; start < attribute_size; ++start) + if (IsSelectionAttribute(attributeData[start])) + break; + + for (end = start; end < attribute_size; ++end) + if (!IsSelectionAttribute(attributeData[end])) + break; + + targetStart = start; + targetEnd = end; + } + } + + private bool IsSelectionAttribute(byte attribute) + { + return (attribute == ATTR_TARGET_CONVERTED || + attribute == ATTR_TARGET_NOTCONVERTED); + } + + internal static bool IsParam(uint lParam, uint type) + { + return (lParam & type) == type; + } + } +} diff --git a/CefSharp.Wpf.Example/IME/NativeIME.cs b/CefSharp.Wpf.Example/IME/NativeIME.cs new file mode 100644 index 0000000000..0086e5f5d8 --- /dev/null +++ b/CefSharp.Wpf.Example/IME/NativeIME.cs @@ -0,0 +1,128 @@ +using System; +using System.Runtime.InteropServices; +using System.Security; + +namespace CefSharp.Wpf.Example.IME +{ + public static class NativeIME + { + public const int WM_INPUTLANGCHANGE = 0x51; + public const int WM_KEYDOWN = 0x0100; + public const int WM_KEYUP = 0x101; + public const int WM_CHAR = 0x102; + public const int WM_SYSKEYDOWN = 0x0104; + public const int WM_SYSKEYUP = 0x105; + public const int WM_IME_STARTCOMPOSITION = 0x10D; + public const int WM_IME_ENDCOMPOSITION = 0x10E; + public const int WM_IME_COMPOSITION = 0x10F; + public const int WM_IME_SETCONTEXT = 0x281; + public const int WM_IME_NOTIFY = 0x282; + public const int WM_IME_CONTROL = 0x283; + public const int WM_IME_COMPOSITIONFULL = 0x284; + public const int WM_IME_SELECT = 0x285; + public const int WM_IME_CHAR = 0x286; + public const int WM_IME_REQUEST = 0x0288; + public const int WM_IME_KEYDOWN = 0x290; + public const int WM_IME_KEYUP = 0x291; + public const int WM_SYSCHAR = 0x0106; + + internal const uint GCS_RESULTSTR = 0x0800; + internal const uint GCS_COMPSTR = 0x0008; + internal const uint GCS_COMPATTR = 0x0010; + internal const uint GCS_CURSORPOS = 0x0080; + internal const uint GCS_COMPCLAUSE = 0x0020; + internal const uint CS_NOMOVECARET = 0x4000; + + internal const uint ATTR_TARGET_CONVERTED = 0x01; + internal const uint ATTR_TARGET_NOTCONVERTED = 0x03; + + internal const uint ISC_SHOWUICOMPOSITIONWINDOW = 0x80000000; + + internal const uint CFS_DEFAULT = 0x0000; + internal const uint CFS_RECT = 0x0001; + internal const uint CFS_POINT = 0x0002; + internal const uint CFS_FORCE_POSITION = 0x0020; + + internal const uint LANG_JAPANESE = 0x11; + internal const uint LANG_CHINESE = 0x04; + internal const uint LANG_KOREAN = 0x12; + + [StructLayout(LayoutKind.Sequential)] + internal struct TagPoint + { + public int X; + public int Y; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct TagRect + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct TagCompositionForm + { + public uint DwStyle; + public TagPoint PtCurrentPos; + public TagRect RcArea; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct TagCandidateForm + { + public uint DwIndex; + public uint DwStyle; + public TagPoint PtCurrentPos; + public TagRect RcArea; + } + + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + [DllImport("imm32.dll")] + internal static extern IntPtr ImmCreateContext(); + + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + [DllImport("imm32.dll")] + internal static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC); + + [DllImport("imm32.dll")] + internal static extern bool ImmDestroyContext(IntPtr hIMC); + + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + [DllImport("imm32.dll")] + internal static extern IntPtr ImmGetContext(IntPtr hWnd); + + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + [DllImport("Imm32.dll")] + internal static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC); + + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + [DllImport("imm32.dll", CharSet = CharSet.Unicode)] + internal static extern uint ImmGetCompositionString(IntPtr hIMC, uint dwIndex, byte[] lpBuf, uint dwBufLen); + + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + [DllImport("imm32.dll")] + internal static extern int ImmSetCompositionWindow(IntPtr hIMC, ref TagCompositionForm lpCompForm); + + [DllImport("user32.dll")] + internal static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight); + + [DllImport("user32.dll")] + internal static extern bool DestroyCaret(); + + [DllImport("user32.dll")] + internal static extern bool SetCaretPos(int x, int y); + + [DllImport("user32.dll")] + internal static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam); + } +} diff --git a/CefSharp.Wpf.Example/SimpleMainWindow.xaml b/CefSharp.Wpf.Example/SimpleMainWindow.xaml index 13143e342e..130612cfdd 100644 --- a/CefSharp.Wpf.Example/SimpleMainWindow.xaml +++ b/CefSharp.Wpf.Example/SimpleMainWindow.xaml @@ -1,14 +1,14 @@ - + xmlns:controls="clr-namespace:CefSharp.Wpf.Example.Controls" + Title="SimpleMainWindow"> - diff --git a/CefSharp.Wpf/ChromiumWebBrowser.cs b/CefSharp.Wpf/ChromiumWebBrowser.cs index aa1dccf5a7..f62e04eb57 100644 --- a/CefSharp.Wpf/ChromiumWebBrowser.cs +++ b/CefSharp.Wpf/ChromiumWebBrowser.cs @@ -1725,7 +1725,7 @@ private void UiThreadRunAsync(Action action, DispatcherPriority priority = Dispa /// /// The action. /// The priority. - private void UiThreadRunSync(Action action, DispatcherPriority priority = DispatcherPriority.DataBind) + protected void UiThreadRunSync(Action action, DispatcherPriority priority = DispatcherPriority.DataBind) { if (Dispatcher.CheckAccess()) { diff --git a/CefSharp.Wpf/Internals/WpfKeyboardHandler.cs b/CefSharp.Wpf/Internals/WpfKeyboardHandler.cs index 545f9d2321..eb53e53e21 100644 --- a/CefSharp.Wpf/Internals/WpfKeyboardHandler.cs +++ b/CefSharp.Wpf/Internals/WpfKeyboardHandler.cs @@ -12,19 +12,19 @@ public class WpfKeyboardHandler : IWpfKeyboardHandler /// /// The owner browser instance /// - private readonly ChromiumWebBrowser owner; + protected readonly ChromiumWebBrowser owner; public WpfKeyboardHandler(ChromiumWebBrowser owner) { this.owner = owner; } - public void Setup(HwndSource source) + public virtual void Setup(HwndSource source) { // nothing to do here } - public void Dispose() + public virtual void Dispose() { // nothing to do here } @@ -105,4 +105,4 @@ public virtual void HandleTextInput(TextCompositionEventArgs e) } } } -} \ No newline at end of file +}