0
votes

I have a text-box like control being hosted in my WPF application by the WindowsFormsHost control. The windows forms control is ScintillaNET. But I suspect the problem isn't there (it was working fine in my old WinForms project).

The problem is that when I have the text field focused and I try to focus another window, the window grabs focus immediately after.

I have tracked this down to the text field being in focus by switching focus to another control (via click) and then switching windows.

Is there any workaround for this? I am using MVVM so simply setting another control to be in focus in the code is not an option.

2

2 Answers

1
votes

This kind of "focus" stealing issue still exists in current version of .Net Framework. Problem seems to be related to WindowsFormsHost control which permanently steals focus once it gets it (in example by clicking on the TextBox control within it). Issue probably started appearing in .Net 4.0 and was probably partially fixed in 4.5, but in different scenarios still exists. The only way we found to fix the problem is through Windows API (as WPF window has separate hWnd from WindowsFormsHost control that is rendered as a window within a window). Adding following 2 methods to the Window's class (and invoking it when focus on the WPF window needs to be regained) helps remedy the issue (by returning focus to WPF window and its controls)

    /// <summary>
    /// Native Win32 API setFocus method.
    /// <param name="hWnd"></param>
    /// <returns></returns>
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    static extern IntPtr SetFocus(IntPtr hWnd);        

    /// <summary>
    /// Win32 API call to set focus (workaround for WindowsFormsHost permanently stealing focus).
    /// </summary>
    public void Win32SetFocus()
    {
        var wih = new WindowInteropHelper(this); // "this" being class that inherits from WPF Window
        IntPtr windowHandle = wih.Handle;

        SetFocus(windowHandle);
    }

This will also help when navigating from a page with WindowsFormsHost control to another one that doesn't have it, though there is a good chance you'll need to invoke Win32SetFocus with a delay (using DispatcherTimer).

NOTE: This code was tested only with x86 (32bit) build, but the same solution should work with x64 (64bit) build. If you need same DLL/EXE code to work on both platforms, improve this code to load proper (32bit or 64bit) unmanaged DLL.

0
votes

The following code works as expected. Maybe you can post an example to reproduce your problem.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using F=System.Windows.Forms;

namespace SimpleForm {

class Window1 : Window {

    F.TextBox tb = new F.TextBox();
    WindowsFormsHost host = new WindowsFormsHost();

    public Window1() {
        this.Width = 500;
        this.Height = 500;
        this.Title = "Title";

        host.Child = tb;

        Button btn = new Button { Content = "Button" };
        StackPanel panel = new StackPanel();
        panel.Orientation = Orientation.Vertical;
        panel.Children.Add(host);
        panel.Children.Add(btn);
        this.Content = panel;

        btn.Click += delegate {
            Window w2 = new Window { Width = 400, Height = 400 };
            w2.Content = new TextBox();
            w2.Show();
        };
    }

    [STAThread]
    static void Main(String[] args) {
        F.Application.EnableVisualStyles();
        F.Application.SetCompatibleTextRenderingDefault(false);
        var w1 = new Window1();
        System.Windows.Application app = new System.Windows.Application();
        app.Run(w1);
    }
}
}