Windows 窗体和 WPF 互操作性输入体系结构

WPF 与 Windows 窗体之间的互操作要求这两种技术都具有适当的键盘输入处理。 本主题介绍这些技术如何实现键盘和消息处理,以便在混合应用程序中实现平滑互操作。

本主题包含以下小节:

  • 无模式窗体和对话框

  • WindowsFormsHost 键盘和消息处理

  • ElementHost 键盘和消息处理

无模式窗体和对话框

WindowsFormsHost 元素上调用 EnableWindowsFormsInterop 方法以从基于 WPF 的应用程序打开无模式窗体或对话框。

ElementHost 控件上调用 EnableModelessKeyboardInterop 方法,以在基于Windows 窗体的应用程序中打开无模式 WPF 页面。

WindowsFormsHost 键盘和消息处理

当由基于 WPF 的应用程序承载时,Windows 窗体键盘和消息处理包括以下内容:

以下部分更详细地描述了该过程的这些部分。

从 WPF 消息循环中获取消息

ComponentDispatcher 类实现 WPF 的消息循环管理器。 ComponentDispatcher 类提供挂钩,使外部客户端能够在 WPF 处理消息之前对其进行筛选。

互操作实现处理 ComponentDispatcher.ThreadFilterMessage 事件,使 Windows 窗体控件能够在 WPF 控件之前处理消息。

代理 Windows 窗体消息循环

默认情况下,System.Windows.Forms.Application 类包含 Windows 窗体应用程序的主要消息循环。 在互操作期间,Windows 窗体消息循环不处理消息。 因此,必须重现此逻辑。 ComponentDispatcher.ThreadFilterMessage 事件的处理程序执行以下步骤:

  1. 使用 IMessageFilter 接口筛选消息。

  2. 调用 Control.PreProcessMessage 方法。

  3. 如果需要,转换并调度消息。

  4. 如果没有其他控件处理消息,则将消息传递给承载控件。

IKeyboardInputSink 实现

代理消息循环处理键盘管理。 因此,IKeyboardInputSink.TabInto 方法是唯一需要在 WindowsFormsHost 类中实现的 IKeyboardInputSink 成员。

默认情况下,HwndHost 类为其 IKeyboardInputSink.TabInto 实现返回 false。 这可以防止从 WPF 控件到 Windows 窗体控件的 Tab 键输入。

IKeyboardInputSink.TabInto 方法的 WindowsFormsHost 实现执行以下步骤:

  1. 查找 WindowsFormsHost 控件包含并且可以接收焦点的第一个或最后一个 Windows 窗体控件。 控件选项取决于遍历信息。

  2. 将焦点设置为控件并返回 true

  3. 如果没有控件可以接收焦点,则返回 false

WindowsFormsHost 注册

创建 WindowsFormsHost 控件的窗口句柄时,WindowsFormsHost 控件调用一个内部静态方法,该方法为消息循环注册其存在。

在注册期间,WindowsFormsHost 控件检查消息循环。 如果尚未启动消息循环,则创建 ComponentDispatcher.ThreadFilterMessage 事件处理程序。 当附加 ComponentDispatcher.ThreadFilterMessage 事件处理程序时,消息循环被认为正在运行。

当窗口句柄被销毁时,WindowsFormsHost 控件将自己从注册中移除。

ElementHost 键盘和消息处理

当由 Windows 窗体应用程序承载时,WPF 键盘和消息处理包括以下内容:

以下部分更详细地描述了这些部分。

接口实现

在 Windows 窗体中,键盘消息被路由到具有焦点的控件的窗口句柄。 在 ElementHost 控件中,这些消息被路由到托管元素。 为此,ElementHost 控件提供了一个 HwndSource 实例。 如果 ElementHost 控件具有焦点,则 HwndSource 实例会路由大多数键盘输入,以便 WPF InputManager 类可以处理它。

HwndSource 类实现 IKeyboardInputSinkIKeyboardInputSite 接口。

键盘互操作依赖于实现 OnNoMoreTabStops 方法来处理将焦点移出托管元素的 Tab 键和箭头键输入。

Tab 键和箭头键

Windows 窗体选择逻辑映射到 IKeyboardInputSink.TabIntoOnNoMoreTabStops 方法以实现 Tab 键和箭头键导航。 重写 Select 方法可完成此映射。

命令键和对话框键

为了让 WPF 第一次有机会处理命令键和对话框键,Windows 窗体命令预处理连接到 TranslateAccelerator 方法。 重写 Control.ProcessCmdKey 方法可连接这两种技术。

使用 TranslateAccelerator 方法,托管元素可以处理任何键消息,例如 WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN 或 WM_SYSKEYUP,包括命令键,例如 Tab、Enter、Esc 和箭头键。 如果未处理键消息,则将其向上发送到 Windows 窗体上次层次结构以进行处理。

快捷键处理

若要正确处理快捷键,Windows 窗体快捷键处理必须连接到 WPF AccessKeyManager 类。 此外,所有 WM_CHAR 消息必须正确路由到托管元素。

由于 TranslateChar 方法的默认 HwndSource 实现返回 false,因此使用以下逻辑处理 WM_CHAR 消息:

当用户按下 Alt 键时,快捷键视觉提示会显示在整个窗体上。 为了支持这种行为,活动窗体上的所有 ElementHost 控件都会接收 WM_SYSKEYDOWN 消息,而不管哪个控件具有焦点。

消息仅发送到活动窗体中的 ElementHost 控件。

另请参阅