TWebBrowser 中的方向键切换控件

Posted

技术标签:

【中文标题】TWebBrowser 中的方向键切换控件【英文标题】:Arrow keys switching control in TWebBrowser 【发布时间】:2021-06-16 00:57:53 【问题描述】:

TWebBrowser (MShtml/IE) 处理箭头键时遇到问题。

基本上,如果我托管 TWebBrowser 并加载一个 HTML 文件,它会错误地显示它并且箭头键可以工作。如果我添加注册表项FEATURE_BROWSER_EMULATION 或使用X-UA-Compatible 元标题,它会正确呈现HTML,但箭头键停止工作(它们确实有效,但他们想“制表”到其他控件,因此内容滚动不再有效)。看起来键在TWebBrowser 处理之前(或之后)下降到主窗体。

我通过处理 keydown 事件然后使用类似的东西找到了解决方案:

WebBrowser1->Document->parentWindow->scrollBy(0, 100);

此解决方案有效,但我发现了一些更好的东西,我正在尝试将其转换为 Delphi/C++ Builder:

我找到的这个 C# 代码执行以下操作:

private void webBrowser1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    
    if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
        
            e.IsInputKey = true;
            return;
        
    

我可以在表单上将KeyPreview 设置为true,然后在表单的 KeyDown 事件中处理 VK_LEFT / VK_RIGHT / VK_DOWN / VK_UP 键,或者使用其他消息处理或 ApplicationEvents 来设置0 的密钥(或处理到true,效果相同)例如:

void __fastcall TForm1::ApplicationEventsMessage(tagMSG &Msg, bool &Handled)

if (Msg.message == WM_KEYDOWN && ActiveControl &&
   ActiveControl->InheritsFrom(__classid(TWebBrowser))
   )
    
    if (Msg.wParam == VK_LEFT)  Handled = true; return;
    if (Msg.wParam == VK_RIGHT) Handled = true; return;
    if (Msg.wParam == VK_UP)    Handled = true; return;
    if (Msg.wParam == VK_DOWN)  Handled = true; return;
    

问题是,这不是一回事。 IsInputKey,如果设置为 true,似乎只处理上述 C# 代码中 TWebBrowser 控件的键,但我在 Delphi/C++ Builder 中没有找到这样的等效项。

知道如何删除承载TWebBrowser 的Delphi/C++Builder 主窗体的键处理,只让TWebBrowser 只为上面的箭头键处理键事件吗?

要加载到 WebBrowser 控件中的测试 HTML(如果它不能滚动以使用箭头键进行测试,只需增加 <div> 标记中的字体大小:

<html>
  <head>
    <!-- This meta tag ensures that TWebBrowser runs in IE-11 mode -->
    <!-- Which causes issues with scrolling with arrow keys -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
  </head>
  <body>
    <div style="font-size:36px;">
      01<br>
      02<br>
      03<br>
      04<br>
      05<br>
      06<br>
      07<br>
      08<br>
      09<br>
      10<br>
      11<br>
      12<br>
      13<br>
      14<br>
      15<br>
      16<br>
      17<br>
      18<br>
      19<br>
      20<br>
      21<br>
      22<br>
      23<br>
      24<br>
      25<br>
      26<br>
      27<br>
      28<br>
      29<br>
      30<br>
    </div>
  </body>
</html>

要重现的代码 - 创建一个带有 TWebBrowserTButton 的表单并将其添加到 TButton 代码中:(单击控制 - 鼠标滚轮有效,向上/向下翻页有效,向上/向下箭头想要“tab”进入按钮(或其他控件):

void __fastcall TForm1::Button1Click(TObject *Sender)

WebBrowser1->Navigate("about:<html><head><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"></head><body><div style=\"font-size:36px;\">01<br>02<br>03<br>04<br>05<br>06<br>07<br>08<br>09<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br></div></body></html>");

【问题讨论】:

搜索TEmbeddedWB组件的源码,他们解决了这个问题。 @whosrdaddy TEmbeddedWB 早在 IE11 之前就已经出现了,此外,我已经对其进行了测试,并且使用箭头键会产生同样的问题。 你有我可以测试的示例 html 文件吗? @whosrdaddy 当然,我已经更新了问题。相关部分是 X-UA-Compatible 标记,它使控件在 IE-11 模式下运行,从而在不存在 X-UA-Compatible 元标记时改变箭头键的功能,这与 IE7(默认)模式不同。跨度> 请检查code,它是Delphi,而不是c++ builder。这为我解决了这个问题(IOleInPlaceActiveObject 是关键) 【参考方案1】:

感谢@whosrdaddy - 以下解决方案有效(取自here)。关键是IsDialogMessage 函数。

// FIX voor webbrowser keys, install application wide message handler
constructor TBrowserHelper.Create(ADebug : TDebuggerSlot);
begin
 Debug := ADebug;
 if Assigned(Debug) then
  Debug.OutputL(Self, 'Create', '', LVL_SPARSE);
 OldMessageHandler := Application.OnMessage;
 Application.OnMessage :=  MsgHandler;
end;
 
destructor TBrowserHelper.Destroy;
begin
 if Assigned(Debug) then
  Debug.OutputL(Self, 'Destroy', '', LVL_SPARSE);
 Application.OnMessage := OldMessageHandler;
 inherited;
end;
 
procedure TBrowserHelper.MsgHandler(var Msg: TMsg; var Handled: Boolean);
 
const StdKeys = [VK_BACK, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT];
 
var IOIPAO: IOleInPlaceActiveObject;
    Browser : TEmbeddedWb;
 
begin
 try
  Browser := nil;
  if Assigned(Screen.ActiveForm) then
   begin
    if Screen.ActiveForm is TFrm_Browser then
     Browser := TFrm_Browser(Screen.ActiveForm).Browser;
    if Assigned(Browser) then
     begin
      Handled := IsDialogMessage(Browser.Handle, Msg);
      if Handled then//and (not Browser.Busy) then
       begin
//       if Assigned(Debug) then
//        Debug.OutputL(Self, 'MsgHandler', Format('Message: %x, wParam: %x, lParam: %x', [Msg.message, Msg.wParam, Msg.lParam]), LVL_FULL);
        if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and (Msg.wParam in StdKeys) then
         begin
          //nothing  -  do not pass on Backspace, Left, Right, Up, Down arrows
         end
        else
         begin
          IOIPAO := (Browser.Application as IOleInPlaceActiveObject);
          if Assigned(IOIPAO)then
           IOIPAO.TranslateAccelerator(Msg)
         end;
       end;
     end;
   end;
 except
  //Handled := False;  // leave it for other handlers
 end;
end;
 
initialization
begin
 iCaptSize := GetSystemMetrics(SM_CYCAPTION);
 iBorderSize := GetSystemMetrics(SM_CXBORDER);
 iBorderThick := GetSystemMetrics(SM_CXSIZEFRAME);
 BrowserHelper := TBrowserHelper.Create(nil);
end;
 
finalization
begin
 if Assigned(BrowserHelper) then
  FreeAndNil(BrowserHelper);
end;

【讨论】:

以上是关于TWebBrowser 中的方向键切换控件的主要内容,如果未能解决你的问题,请参考以下文章

在默认浏览器中打开 TWebBrowser 链接

转换为 WPF 的本机控件未捕获制表键

C# 上下左右键 切换控件焦点

如何从 TWebBrowser 获取图像到 TImage

VB 如何用上下键切换控件焦点

C++Builder TWebBrowser 不适用于 Google OAuth 登录