WPF 和 Windows 窗体之间的互作要求这两种技术都具有适当的键盘输入处理。 本主题介绍了这些技术如何实现键盘和消息处理,以实现混合应用程序中的平滑互作。
本主题包含以下小节:
无模式窗体和对话框
WindowsFormsHost 的键盘和消息处理
ElementHost 键盘和消息处理
无模式窗体和对话框
在WindowsFormsHost元素上调用EnableWindowsFormsInterop方法,以从基于 WPF 的应用程序打开无模式窗体或对话框。
在基于 Windows 窗体的应用程序中,通过对ElementHost控件调用EnableModelessKeyboardInterop方法来打开一个无模式 WPF 页面。
WindowsFormsHost 键盘和消息处理
在由 WPF 应用程序托管的情况下,Windows 窗体的键盘和消息处理包括以下内容:
该 WindowsFormsHost 类从 WPF 消息循环获取消息,该循环由 ComponentDispatcher 该类实现。
该 WindowsFormsHost 类创建代理 Windows 窗体消息循环,以确保执行普通 Windows 窗体键盘处理。
该 WindowsFormsHost 类实现接口 IKeyboardInputSink ,以便与 WPF 协调焦点管理。
控件 WindowsFormsHost 自行注册并启动其消息循环。
以下部分更详细地介绍了该过程的这些部分。
从 WPF 消息循环获取消息
该 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一个需要实现的成员。
默认情况下,类HwndHostfalse
返回其IKeyboardInputSink.TabInto实现。 阻止 WPF 控件与 Windows 窗体控件之间的 Tab 键导航。
方法 WindowsFormsHost 的 IKeyboardInputSink.TabInto 实现执行以下步骤:
查找由 WindowsFormsHost 控件包含的、能够获得焦点的第一个或最后一个 Windows 窗体控件。 控制选择取决于遍历信息。
将焦点设置为控件并返回
true
。如果没有控件可以接收焦点,则返回
false
。
WindowsFormsHost 注册登记
当为 WindowsFormsHost 控件创建窗口句柄时,WindowsFormsHost 控件将调用一个内部静态方法,以注册其在消息循环中的存在。
在注册期间,控件 WindowsFormsHost 将检查消息循环。 如果消息循环尚未启动,则会创建 ComponentDispatcher.ThreadFilterMessage 事件处理程序。 附加事件处理程序时 ComponentDispatcher.ThreadFilterMessage ,消息循环被视为正在运行。
当窗口句柄被销毁时,控件 WindowsFormsHost 将从注册中删除自身。
ElementHost 键盘和消息处理
当由 Windows 窗体应用程序托管时,WPF 键盘和消息处理包括以下内容:
HwndSource接口实现、IKeyboardInputSink接口实现和IKeyboardInputSite接口实现。
Tabbing 和箭头键。
命令键和对话框键。
Windows 窗体加速器处理。
以下部分更详细地介绍了这些部分。
接口实现
在 Windows 窗体中,键盘消息将路由到具有焦点的控件的窗口句柄。 在控件中 ElementHost ,这些消息将路由到托管元素。 为此,该 ElementHost 控件提供了一个 HwndSource 实例。 如果ElementHost控件具有焦点,HwndSource实例会将大多数键盘输入传递给 WPF InputManager类进行处理。
该 HwndSource 类实现 IKeyboardInputSink 和 IKeyboardInputSite 接口。
键盘互作依赖于实现 OnNoMoreTabStops 方法来处理 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 窗体加速器处理必须连接到 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 控件。