Windows RichEdit拼写更正建议弹窗显示在错误显示器问题排查
双显示器下TRichEdit(RICHEDIT50W)拼写更正弹窗错位问题的成因与解决方法
成因
- DPI感知缺失:若应用未启用高DPI感知,当窗口处于不同DPI的双显示器环境时,系统会对窗口进行缩放,导致RichEdit控件的实际屏幕坐标计算错误,负责渲染拼写弹窗的系统文本服务框架(TSF)会基于错误坐标定位弹窗,最终出现在错误显示器上。
- TSF上下文未同步:RichEdit的拼写弹窗由TSF管理,当控件所在窗口在双显示器间移动或位置/大小变化时,控件未主动将最新的屏幕位置信息同步给TSF,导致TSF仍使用旧坐标计算弹窗位置。
解决方法
方法1:启用应用高DPI感知
在Delphi 10.3中开启高DPI支持:
- 打开项目选项(
Project > Options) - 切换到
Application选项卡,找到Manifest设置 - 勾选
Enable runtime themes,在High DPI下拉菜单中选择Per Monitor v2或Per Monitor(根据目标系统版本选择) - 保存设置后重新编译应用
方法2:子类化RichEdit同步TSF上下文
通过子类化TRichEdit,在窗口位置/大小变化时强制刷新拼写检查配置,触发TSF同步控件最新位置:
unit MyRichEdit; interface uses Vcl.ComCtrls, Winapi.Messages, Winapi.Windows; type TMyRichEdit = class(TRichEdit) private FSpellChecking: Boolean; FAutoCorrection: Boolean; procedure UpdateLangOptions; protected procedure WndProc(var Message: TMessage); override; public property SpellChecking: Boolean read FSpellChecking write FSpellChecking default True; property AutoCorrection: Boolean read FAutoCorrection write FAutoCorrection default True; constructor Create(AOwner: TComponent); override; end; implementation constructor TMyRichEdit.Create(AOwner: TComponent); begin inherited; FSpellChecking := True; FAutoCorrection := True; UpdateLangOptions; end; procedure TMyRichEdit.UpdateLangOptions; var Value: LRESULT; const IMF_SPELLCHECKING = $0800; IMF_TKBAUTOCORRECTION = $2000; begin Value := SendMessage(Handle, EM_GETLANGOPTIONS, 0, 0); if FSpellChecking then Value := Value or IMF_SPELLCHECKING else Value := Value and not IMF_SPELLCHECKING; if FAutoCorrection then Value := Value or IMF_TKBAUTOCORRECTION else Value := Value and not IMF_TKBAUTOCORRECTION; SendMessage(Handle, EM_SETLANGOPTIONS, 0, Value); end; procedure TMyRichEdit.WndProc(var Message: TMessage); begin inherited; // 窗口位置或大小变化时,同步TSF上下文 if (Message.Msg = WM_WINDOWPOSCHANGED) or (Message.Msg = WM_SIZE) then UpdateLangOptions; end; end.
使用时将原TRichEdit控件替换为TMyRichEdit即可。
方法3:手动通过TSF接口设置控件上下文
若前两种方法无效,可直接调用TSF接口同步控件屏幕位置:
uses ActiveX, Winapi.TSF, Winapi.Windows; procedure UpdateRichEditSpellContext(ARichEdit: TRichEdit); var pContext: ITfContext; pOwner: ITfContextOwner; Rect: TRect; begin if SUCCEEDED(ARichEdit.Handle.QueryInterface(IID_ITfContextOwner, pOwner)) then begin if SUCCEEDED(pOwner.GetContext(pContext)) then begin GetWindowRect(ARichEdit.Handle, Rect); pContext.SetSelection(0, 0); pContext.SetTextExtent(@Rect, 1); pContext.Release; end; pOwner.Release; end; end;
在RichEdit的OnEnter或OnResize事件中调用此函数,即可同步控件位置给TSF。
内容的提问来源于stack exchange,提问作者Gerrit Beuze
相关产品推荐
相关产品推荐

