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

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

本主题包含以下小节:

  • 无模式窗体和对话框

  • WindowsFormsHost 的键盘和消息处理

  • ElementHost 键盘和消息处理

无模式窗体和对话框

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

在基于 Windows 窗体的应用程序中,通过对ElementHost控件调用EnableModelessKeyboardInterop方法来打开一个无模式 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一个需要实现的成员。

默认情况下,类HwndHostfalse返回其IKeyboardInputSink.TabInto实现。 阻止 WPF 控件与 Windows 窗体控件之间的 Tab 键导航。

方法 WindowsFormsHostIKeyboardInputSink.TabInto 实现执行以下步骤:

  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 键和箭头键输入,以便将焦点移出托管元素。

选项卡操作和箭头键

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消息正确路由到托管元素。

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

  • Control.IsInputChar 方法被重写,以确保所有 WM_CHAR 消息都转发到承载元素。

  • 如果按下 ALT 键,则消息为 WM_SYSCHAR。 Windows 窗体不会通过 IsInputChar 方法对该消息进行预处理。 因此,重写ProcessMnemonic方法以在 WPF AccessKeyManager中查询已注册的加速器。 如果找到已注册的加速器,AccessKeyManager 将对其进行处理。

  • 如果未按下 ALT 键,WPF InputManager 类将处理未处理的输入。 如果输入是加速器,则 AccessKeyManager 处理它。 处理准确PostProcessInput事件是针对未被处理的WM_CHAR消息。

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

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

另请参阅