再现我的案例(.net 4.0)
- 创建WPF应用程序(MainWindow.xaml)
- 添加包含文本框的Winform用户控件(UserConrol1.cs-Winform)
- 使用windowsformshost将UserControl1放入MainWindow.xaml
- 将另一个包含文本框(WPF)的WPF窗口添加到项目(Window1.xaml)
- 在MainWindow InitializeComponent之后创建并显示Window1
你的项目已经准备好了,
- 在MainWindow.xaml(WindowsFormsHost中的文本框)中运行Project并设置文本框焦点
- 通过打开窗口(Windows文件资源管理器、记事本、winamp等)停用应用程序
- 用鼠标单击文本框,尝试在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 中不再存在