【问题标题】:WindowsFormsHost forces FocusWindowsFormsHost 强制焦点
【发布时间】:2014-10-29 23:20:19
【问题描述】:

我有一个类似文本框的控件,由 WindowsFormsHost 控件托管在我的 WPF 应用程序中。 Windows 窗体控件是 ScintillaNET。但我怀疑问题不存在(它在我的旧 WinForms 项目中运行良好)。

问题在于,当我将文本字段聚焦并尝试聚焦另一个窗口时,该窗口会立即获得焦点。

我已经通过将焦点切换到另一个控件(通过单击)然后切换窗口来跟踪文本字段的焦点。

有什么解决方法吗?我正在使用 MVVM,因此不能简单地将另一个控件设置为在代码中成为焦点。

【问题讨论】:

    标签: c# wpf mvvm windowsformshost


    【解决方案1】:

    这种“焦点”窃取问题在当前版本的 .Net Framework 中仍然存在。问题似乎与WindowsFormsHost 控件有关,该控件一旦获得焦点就会永久窃取焦点(例如,通过单击其中的 TextBox 控件)。问题可能开始出现在 .Net 4.0 中,并且可能在 4.5 中部分修复,但在不同的场景中仍然存在。我们发现解决该问题的唯一方法是通过 Windows API(因为 WPF 窗口具有独立的 hWnd 与 WindowsFormsHost 控件,该控件呈现为窗口中的窗口)。将以下 2 个方法添加到 Window 的类(并在需要重新获得 WPF 窗口上的焦点时调用它)有助于解决问题(通过将焦点返回到 WPF 窗口及其控件)

        /// <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);
        }
    

    这在从带有WindowsFormsHost 控件的页面导航到另一个没有它的页面时也会有所帮助,尽管您很有可能需要延迟调用 Win32SetFocus(使用DispatcherTimer)。

    注意:此代码仅在 x86(32 位)版本中进行了测试,但相同的解决方案应适用于 x64(64 位)版本。如果您需要相同的 DLL/EXE 代码在两个平台上工作,请改进此代码以加载正确的(32 位或 64 位)非托管 DLL。

    【讨论】:

      【解决方案2】:

      以下代码按预期工作。也许您可以发布一个示例来重现您的问题。

      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);
          }
      }
      }
      

      【讨论】:

      • 因此,当您打开第一个窗口上的文本框并且已经打开另一个窗口时,您是否可以直接 alt-tab 或从任务栏打开该窗口(而 WinForms 文本框有焦点)?可能是 ScintillaNET 做了一些我的情况所揭示的黑客行为。
      • 一定是 ScintillaNET 做了一些主机无法正确处理的奇怪事情。
      • 我遇到了同样的问题,我使用了 ScintillaNET 控件。它似乎将焦点保留在先前激活的 ScintillaNET 控件中,而不是切换到新激活的控件。即使在新的 ScintillaNET 实例上显式调用 Focus() 也无法将焦点更改为新创建的控件。
      猜你喜欢
      • 2017-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多