Open vgriph opened 2 weeks ago
@vgriph Could you try use SetFocus
to WPF window?
SetFocus(wpfWindowHandle);
[DllImport("User32.dll")]
public static extern IntPtr SetFocus(IntPtr hWnd);
Or use the SetForegroundWindow
[DllImport("USER32.DLL")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ActivatePopup(Popup popup)
{
HwndSource source = (HwndSource)PresentationSource.FromVisual(popup.Child);
IntPtr handle = source.Handle;
SetForegroundWindow(handle);
}
You can find my demo code in : https://github.com/lindexi/lindexi_gd/tree/1666e742fbd5ebda36e840a8e5f4b866251b3004/GakelfojeNairwogewerwhiheecem
@lindexi Just setting the focus did not work correctly in all cases. (The focus was returned to the Windows Forms control)
But using it in combination with UIElement.Focus()
I managed to correctly transfer the focus back to the WPF control. (Except for that the main window below the popup has the title bar in "inactive state" when I focus the WPF text box in the popup after having the Windows Forms text box in the popup focused. (Otherwise it is styled as inactive only when focus is in the windows forms textbox in the popup.
I managed to put together the following work around at application level, which seems to work for multi window applications.
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
namespace WinFormsIteropTest
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private bool ignoreFocusChange;
public App()
{
InitializeComponent();
EventManager.RegisterClassHandler(typeof(Popup), Keyboard.GotKeyboardFocusEvent, new RoutedEventHandler(OnWindowGotFocus));
}
private void OnWindowGotFocus(object sender, RoutedEventArgs e)
{
var popup = (UIElement)sender;
var windowHandle = ((HwndSource)PresentationSource.FromVisual(popup))?.Handle;
var focusedNativeWindow = NativeMethods.GetFocus();
if (ignoreFocusChange)
{
return;
}
if (focusedNativeWindow == windowHandle)
{
return;
}
popup.Dispatcher.InvokeAsync(() => VerifyPopupFocus(popup, e), System.Windows.Threading.DispatcherPriority.Input);
}
private void VerifyPopupFocus(UIElement popup, RoutedEventArgs e)
{
var focusedElement = Keyboard.FocusedElement;
var focusedNativeWindow = NativeMethods.GetFocus();
if (focusedElement is not Visual focusedVisual
|| PresentationSource.FromVisual(focusedVisual) is not HwndSource nativeWindow)
{
// The element with keyboard focus is not a WPF element
return;
}
var focusScope = FocusManager.GetFocusScope((DependencyObject)e.Source);
var focusScopeWindowHandle = ((HwndSource)PresentationSource.FromVisual((Visual)focusScope))?.Handle;
if (focusedNativeWindow == focusScopeWindowHandle)
{
// Focused native window is the focus scope window no change needed
return;
}
var nativeWindowHandle = nativeWindow.Handle;
if (nativeWindowHandle == focusedNativeWindow)
{
// The focus is within WPF. Nothing needs to be done.
return;
}
var activeWindow = System.Windows.Application.Current.Windows.Cast<Window>()
.FirstOrDefault(x => x.IsActive);
var windowToFocus = nativeWindowHandle;
var focusScopeToFocus = focusScope;
if (activeWindow != null)
{
var activeWindowHandle = new WindowInteropHelper(activeWindow).Handle;
if (activeWindowHandle == focusedNativeWindow)
{
// The focus is within WPF. Nothing needs to be done.
return;
}
windowToFocus = activeWindowHandle;
focusScopeToFocus = activeWindow;
}
ForceFocusToWpfElement(windowToFocus, focusScopeToFocus, focusedElement);
}
private void ForceFocusToWpfElement(IntPtr windowHandle, DependencyObject focusScope, IInputElement focusedElement)
{
// The focused HWND is not a WPF window, but WPF think it has Keyboard Focus.
// This means that the focus is out of sync. (The HWND that has focused will receive keyboard input,
// but WPF will render the UI as if the focused element has focus.
// To fox this state, we need to focus the WPF window so that it receives the input.
// We then have to restore the focus to the previously focused element in WPF.
ignoreFocusChange = true;
try
{
NativeMethods.SetFocus(windowHandle);
var elementFocusedByMovingFocusToWpfWindow = FocusManager.GetFocusedElement(focusScope);
if (elementFocusedByMovingFocusToWpfWindow != null
&& elementFocusedByMovingFocusToWpfWindow != focusedElement)
{
// Focus them element from WPF as well, since WPF seems to not fully update its internal state
// when just restoring focus when the main window receives focus by a native call.
elementFocusedByMovingFocusToWpfWindow.Focus();
}
focusedElement.Focus();
}
finally
{
ignoreFocusChange = false;
}
}
private static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
internal static extern IntPtr GetFocus();
}
}
}
@vgriph Thank you. The wpf set popup with WS_EX_NOACTIVATE, and that is the reason of NativeMethods.SetFocus
. But as Microsoft says in https://connect.microsoft.com/VisualStudio/feedback/details/389998/wpf-popup-messes-with-ime-switching, the broken link, it be fixed on .NET Framework 4.6.2
Without more research, I don't know the root cause of the problem.
Description
When using a WindowsFormsHost to host a Windows Forms control in a WPF Popup, focus gets trapped in the Windows forms control.
Reproduction Steps
Create a WPF windows with the following XAML definition
When opened, focus the first (WPF TextBox), then focus the second (Windows Forms) textbox and then focus the WPF textbox again.
Expected behavior
The focus should be moved to the WPF textbox
Actual behavior
The input caret is moved to the WPF textbox, so it looks as if it has focus, but any input goes into the Windows Forms textbox
Regression?
No. The same problem exists in .NET 4.8 and .NET 8
Known Workarounds
Listening to the
Keyboard.GotKeyboardFocusEvent
and, after each such event verify that the Win32 focus is on a WPF owned HWND, and if not force the focus back to the WPF window that WPF though had got the keyboard focus.Impact
No response
Configuration
.NET 8 and .NET 4.8 on Windows 10
Other information
No response