Windows 窗体和 WPF 互操作性输入结构
更新:2007 年 11 月
WPF 与 Windows 窗体之间的互操作要求这两项技术都有相应的键盘输入处理。本主题介绍这两项技术如何实现键盘和消息处理,以在混合应用程序中启用平稳的互操作。
本主题包含以下小节:
无模式窗体和对话框
WindowsFormsHost 键盘和消息处理
ElementHost 键盘和消息处理
无模式窗体和对话框
调用 WindowsFormsHost 元素上的 EnableWindowsFormsInterop 方法可以从基于 WPF 的应用程序打开无模式窗体或对话框。
调用 ElementHost 控件上的 EnableModelessKeyboardInterop 方法可以在基于 Windows 窗体的应用程序中打开无模式的 WPF 页面。
WindowsFormsHost 键盘和消息处理
当由基于 WPF 的应用程序承载时,Windows 窗体键盘和消息处理将由以下各项组成:
WindowsFormsHost 类获取来自 WPF 消息循环的消息,这由 ComponentDispatcher 类实现。
WindowsFormsHost 类创建代理项 Windows 窗体消息循环,以确保 Windows 窗体的例常键盘处理的进行。
WindowsFormsHost 类实现 IKeyboardInputSink 接口,以便与 WPF 协调焦点管理。
WindowsFormsHost 控件注册其自身并启动其消息循环。
后面几节更详细地介绍了该过程中的这些部分。
获取来自 Windows Presentation Foundation 消息循环的消息
ComponentDispatcher 类为 WPF 实现消息循环管理器。ComponentDispatcher 类提供挂钩以使外部客户端可以在 WPF 处理消息之前对这些消息进行筛选。
实现的互操作可处理 ComponentDispatcher.ThreadFilterMessage 事件,从而允许 Windows 窗体控件先于 WPF 控件处理消息。
代理项 Windows 窗体消息循环
默认情况下,System.Windows.Forms.Application 类包含 Windows 窗体应用程序的主消息循环。在互操作过程中,Windows 窗体消息循环不会处理消息。因此,必须重新生成此逻辑。ComponentDispatcher.ThreadFilterMessage 事件的处理程序执行下面的步骤:
使用 IMessageFilter 接口筛选消息。
调用 Control.PreProcessMessage 方法。
如果需要,转换并调度该消息。
如果没有其他控件处理该消息,将该消息传递给承载控件。
IKeyboardInputSink 实现
代理项消息循环处理键盘管理。因此,IKeyboardInputSink.TabInto 方法是 WindowsFormsHost 类中唯一需要实现的 IKeyboardInputSink 成员。
默认情况下,HwndHost 类对其 IKeyboardInputSink.TabInto 实现返回 false。这会阻止使用 Tab 键从 WPF 控件导航到 Windows 窗体控件。
IKeyboardInputSink.TabInto 方法的 WindowsFormsHost 实现执行下面的步骤:
查找 WindowsFormsHost 控件包含的并且可以接收焦点的第一个或最后一个 Windows 窗体控件。该控件选择取决于遍历信息。
将焦点设置到该控件并返回 true。
如果控件收不到焦点,则返回 false。
WindowsFormsHost 注册
在创建 WindowsFormsHost 控件的窗口句柄时,WindowsFormsHost 控件将调用为消息循环注册其存在的内部静态方法。
在注册过程中,WindowsFormsHost 控件将检查消息循环。如果消息循环尚未启动,则将创建 ComponentDispatcher.ThreadFilterMessage 事件处理程序。消息循环被视为在附加 ComponentDispatcher.ThreadFilterMessage 事件处理程序时运行。
如果窗口句柄被损坏,则 WindowsFormsHost 控件将从注册中移除其自身。
ElementHost 键盘和消息处理
当由 Windows 窗体应用程序承载时,WPF 键盘和消息处理将由以下各项组成:
Tab 键导航和箭头键。
命令键和对话框键。
Windows 窗体快捷键处理。
以下几节更详细地介绍了这些部分。
接口实现
在 Windows 窗体中,键盘消息将路由到获得焦点的控件的窗口句柄。在 ElementHost 控件中,这些消息将路由到承载的元素。为实现此目标,ElementHost 控件提供了一个 HwndSource 实例。如果 ElementHost 控件获得焦点,则 HwndSource 实例会路由大多数键盘输入,以使其可以由 WPFInputManager 类处理。
HwndSource 类实现 IKeyboardInputSink 和 IKeyboardInputSite 接口。
键盘互操作依赖于 OnNoMoreTabStops 方法的实现来处理将焦点移出承载的元素的 Tab 键和箭头键输入。
Tab 键导航和箭头键
Windows 窗体选择逻辑将映射到 IKeyboardInputSink.TabInto 和 OnNoMoreTabStops 方法以实现 Tab 和箭头键导航。重写 Select 方法即可实现此映射。
命令键和对话框键
为了向 WPF 提供处理命令键和对话框键的第一次机会,Windows 窗体命令预处理连接到 TranslateAccelerator 方法。重写 Control.ProcessCmdKey 方法会连接两项技术。
使用 TranslateAccelerator 方法,承载的元素可以处理任何按键消息(如 WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN 或 WM_SYSKEYUP),包括命令键(如 Tab、Enter、Esc 以及箭头键)。如果按键消息未被处理,则将其向上发送到 Windows 窗体的上级层次结构进行处理。
快捷键处理
若要正确处理快捷键,Windows 窗体快捷键处理必须连接到 WPFAccessKeyManager 类。另外,所有 WM_CHAR 消息都必须正确路由到承载的元素。
由于 TranslateChar 方法的默认 HwndSource 实现返回 false,因此将使用以下逻辑处理 WM_CHAR 消息:
重写 Control.IsInputChar 方法,以确保所有 WM_CHAR 消息转发到承载的元素。
如果按下 Alt 键,则显示 WM_SYSCHAR 消息。Windows 窗体不会通过 IsInputChar 方法预处理此消息。因此,重写 ProcessMnemonic 方法,以在 WPFAccessKeyManager 中查询已注册的快捷键。如果找到已注册的快捷键,则 AccessKeyManager 将对其进行处理。
如果未按 Alt 键,则 WPFInputManager 类将处理尚未处理的输入。如果输入为快捷键,则 AccessKeyManager 将对其进行处理。为尚未处理的 WM_CHAR 消息处理 PostProcessInput 事件。
在用户按下 Alt 键时,快捷键的可视化提示将显示在整个窗体上。若要支持此行为,活动窗体上的所有 ElementHost 控件都需要接收 WM_SYSKEYDOWN 消息,而不考虑哪个控件获得了焦点。
消息将仅发送给活动窗体中的 ElementHost 控件。
请参见
概念
演练:在 Windows Presentation Foundation 中承载 Windows 窗体复合控件
演练:在 Windows 窗体中承载 Windows Presentation Foundation 控件