WindowsFormsHost窃取的重点是激活应用程序,甚至通过应用程序的其他WPF表单激活



再现我的案例(.net 4.0)

  1. 创建WPF应用程序(MainWindow.xaml)
  2. 添加包含文本框的Winform用户控件(UserConrol1.cs-Winform)
  3. 使用windowsformshost将UserControl1放入MainWindow.xaml
  4. 将另一个包含文本框(WPF)的WPF窗口添加到项目(Window1.xaml)
  5. 在MainWindow InitializeComponent之后创建并显示Window1

你的项目已经准备好了,

  1. 在MainWindow.xaml(WindowsFormsHost中的文本框)中运行Project并设置文本框焦点
  2. 通过打开窗口(Windows文件资源管理器、记事本、winamp等)停用应用程序
  3. 用鼠标单击文本框,尝试在Window1窗口中的文本框中写入

您将看到,您无法在Window1中的文本框上设置焦点,因为MainWindow-Texbox(在winformshost中,会在应用程序激活时窃取您的焦点)

知道吗?

主窗口.xaml

<Window x:Class="WinFormsHostFocusProblem.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WinFormsHostFocusProblem"
        xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
      <my:WindowsFormsHost  Focusable="False"  >
         <local:UserControl1>
         </local:UserControl1>
      </my:WindowsFormsHost>
   </Grid>
</Window>

主窗口.xaml.cs

namespace WinFormsHostFocusProblem
{
   public partial class MainWindow : Window
   {
      public MainWindow()
      {
         InitializeComponent();
         Window1 window1 = new Window1();
         window1.Show();
      }
   }
}

Window1.xaml

<Window x:Class="WinFormsHostFocusProblem.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WinFormsHostFocusProblem"
        xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
        SizeToContent="WidthAndHeight" 
        ResizeMode="NoResize"
        Topmost="True"
        Title="Window1" Height="300" Width="300" Background="Red">
    <Grid>
      <TextBox Height="25">asd</TextBox>
   </Grid>
</Window>

Window1.xaml.cs

namespace WinFormsHostFocusProblem
{
   public partial class Window1 : Window
   {
      public Window1()
      {
         InitializeComponent();
      }
   }
}

我使用MSDN支持合同来获得这个问题的答案。工程师能够从yunusayd的样本中进行重新编程,并确认这几乎可以肯定是WindowsFormsHost中的一个错误。

感谢yunus提供了最少的repo样本,感谢微软的Keith在不到一天的时间内解决了这个问题并提供了解决方案。

解决方案代码如下。它的工作原理是使用.NET反射来更改WindowsFormsHost中使用的私有变量,并禁用该错误的触发器。据与我共事的工程师说,这依赖于WPF内部,但他与产品团队成员进行了交谈,使用起来应该是安全的。当然,不能保证没有副作用,但到目前为止,我在多个WPF窗口中使用多个WindowsFormsHost进行测试时没有发现任何问题(也许嵌套会更棘手)。我修改了原来的解决方法,使其可以通用于多个窗口。您可以在Application_Dactivated事件中轻松地硬编码对特定窗口和命名WindowsFormsHost控件的引用,并跳过整个"LastActive"方案和扩展方法。

// App.xaml.cs: you must hook up to Application.Deactivated
void Application_Deactivated(object sender, EventArgs e)
{
    foreach (Window w in windows)
    {
        foreach (var host in UI.DependencyObjectExtension.AllLogicalChildren(w).
                     Where(c => c is WindowsFormsHost))
        {
            FIELD_FOCUSED_CHILD.SetValue(host, null);
        }
    }
}

public readonly static FieldInfo FIELD_FOCUSED_CHILD = typeof(System.Windows.Forms.Integration.WindowsFormsHost).
    GetField("_focusedChild", BindingFlags.NonPublic | BindingFlags.Instance);
public static class DependencyObjectExtension
{
    /// <summary>
    /// Returns a collection of o's logical children, recursively.
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static IEnumerable<DependencyObject> AllLogicalChildren(this DependencyObject o)
    {
        foreach (var child in LogicalTreeHelper.GetChildren(o))
        {
            if (child is DependencyObject)
            {
                yield return (DependencyObject)child;
                if (child is DependencyObject)
                {
                    foreach (var innerChild in AllLogicalChildren((DependencyObject)child))
                    {
                        yield return innerChild;
                    }
                }
            }
        }
    }
}

我们在一个应用程序中遇到了类似的问题,发现升级到.net 4.5似乎已经修复了我们应用程序的WPF/WinForms焦点问题的很大一部分,包括类似的问题。

此外,_focusedChild字段在.net 4.5版本的WindowsFormsHost 中不再存在

最新更新