确保例程运行时表单不会获得焦点

发布于 2024-11-24 15:40:14 字数 263 浏览 1 评论 0原文

我有 2 个表单 - 第一个表单有一堆编辑、组合框等,第二个表单有一个 Web 浏览器。

我在第二个表单中有一个例程,它将 HTML 加载到 Web 浏览器中,并且该例程在第一个表单上的所有控件的 OnChange 事件中触发。

问题是,当第二个表单将 HTML 加载到网络浏览器时,我对第一个表单的集中控制失去了焦点。

如何确保例程触发时第二种形式不会获得焦点?或者,更重要的是 - 确保我的第一个表单上的聚焦控件不会失去焦点?

I have 2 forms - The first form has a bunch of edits, comboboxes, etc, and the 2nd form has a Webbrowser.

I have a routine in the 2nd form, that loads HTML into the Webbrowser, and that routine is fired in the OnChange event of all my controls on the first form.

The problem is, that my focused control on my first form looses focus, when the 2nd form is loading the HTML into the webbrowser.

How can I make sure that the 2nd form does not get focus when the routine is fired? Or, more importantly - make sure that the focused control on my first form, does not loose focus?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

孤蝉 2024-12-01 15:40:14

您尝试做的事情违背了 VCL,并且可能违背了通常的用户期望:当显示新窗口时,除非它是工具窗口,否则通常(和预期)的行为是将焦点移至它。显示窗口的 Win32 Api 是 ShowWindow 并且它将激活窗口,除非指定了 SW_SHOWNOACTIVATE 标志(或其变体之一)。

当您使 VCL 窗体可见时,也会调用该函数。对 ShowWindow 的调用隐藏在 procedure TCustomForm.CMShowingChanged(var Message: TMessage) 中,这是一个 135 行的过程,对 SW_SHOWNORMAL 标志进行硬编码(即:激活标志)用于 VCL 进行的 ShowWindow 调用。不幸的是,这是一大段代码,覆盖它并不容易。如果这是我的程序,我可能会尝试更改代码:我会向 TCustomForm 添加一个 DoNotActivate:Boolean 标志并更改CMShowingChanged 中的单行代码调用非 MDI 表单的 ShowWindow 来考虑该标志,只需调用 ShowWindow(Handle, SW_SHOWNOACTIVATE)。如果更改 VCL 不是一件轻松的事情,您可以使用以下 hacky 解决方案:

我建议的技巧是创建新表单(包含 TWebBrowser 的表单),但是不要将其 Visible 属性设置为 True。相反,手动调用 ShowWindow(Handle, SW_SHOWNOACTIVATE) 来显示表单而不激活它。由于此代码将不再通过通常的 Delphi VCL 进行编码,因此不会自动创建和显示拥有的控件,因此需要对所有 递归调用 ShowWindow(...)表单的 TWinControl 后代:

procedure TForm15.Button1Click(Sender: TObject);
var F: TForm16;

  procedure RecursiveShowNoActivate(W: TWinControl);
  var i:Integer;
  begin
    ShowWindow(W.Handle, SW_SHOWNOACTIVATE);
    for i:=0 to W.ControlCount-1 do
      if W.Controls[i] is TWinControl then
        RecursiveShowNoActivate(TWinControl(W.Controls[i]));
  end;

begin
  F := TForm16.Create(Application);
  F.Top := Top + height; // So the new form doesn't overlap mine
  RecursiveShowNoActivate(F);
  F.WebBrowser1.Navigate('http://msdn.microsoft.com/en-us/library/ms123401');
end;

此代码还有一个问题:确保导航到的网页没有自动聚焦的表单。对于代码示例来说,导航到 Microsoft 的 MSDN 库可能不太常见,但该规范示例 (www.google.com) 将焦点设置在搜索表单上。

What you're trying to do goes against the VCL, and probably goes against usual user expectations: When a new window is shown, unless it's a tool window, the usual (and expected) behavior is to move focus to it. The Win32 Api to show a window is ShowWindow and it'll activate the window, unless the SW_SHOWNOACTIVATE flag (or one of it's variants) is specified.

When you make a VCL form visible, a call to that function is also made. The call to ShowWindow is buried in procedure TCustomForm.CMShowingChanged(var Message: TMessage), a 135 lines procedure that hard-codes the SW_SHOWNORMAL flag (ie: an Activating flag) for the ShowWindow call that the VCL makes. Unfortunately that's a big piece of code and overriding it is not going to be easy. If this was my program I'd probably attempt changing the code in place: I'd add a DoNotActivate:Boolean flag to TCustomForm and change the single line of code in CMShowingChanged that calls ShowWindow for non-MDI forms to take that flag into account and simply call ShowWindow(Handle, SW_SHOWNOACTIVATE). If changing the VCL is not something you'd do light-hearted, you can use the following hacky solution:

The trick I'm suggesting is to create the new form (the one holding TWebBrowser) but do NOT set it's Visible property to True. Instead, make manual calls to ShowWindow(Handle, SW_SHOWNOACTIVATE) to show the form without activating it. Because this code would no longer code through the usual Delphi VCL, owned controls would not automatically be created and shown, so the ShowWindow(...) call needs to be made recursively, for all TWinControl descendants of the form:

procedure TForm15.Button1Click(Sender: TObject);
var F: TForm16;

  procedure RecursiveShowNoActivate(W: TWinControl);
  var i:Integer;
  begin
    ShowWindow(W.Handle, SW_SHOWNOACTIVATE);
    for i:=0 to W.ControlCount-1 do
      if W.Controls[i] is TWinControl then
        RecursiveShowNoActivate(TWinControl(W.Controls[i]));
  end;

begin
  F := TForm16.Create(Application);
  F.Top := Top + height; // So the new form doesn't overlap mine
  RecursiveShowNoActivate(F);
  F.WebBrowser1.Navigate('http://msdn.microsoft.com/en-us/library/ms123401');
end;

There's an other catch with this code: Make sure you navigate to a web page that doesn't have a form that's automatically focused. Navigating to microsoft's MSDN library might be unusual for a code sample, but that canonical example (www.google.com) sets focus to the search form.

有深☉意 2024-12-01 15:40:14

一种简单的解决方案是禁用包含 Web 浏览器控件的表单。禁用的窗口将不会获得焦点。

当 TWebControl 的 OnDocumentComplete 事件被触发时,浏览器控件就准备好获得焦点。禁用此处的表单,并向自己发布一条消息,以便您可以尽快启用该表单:

const
  UM_POSTENABLE = WM_USER + 12;

type
  TForm2 = class(TForm)
    WebBrowser1: TWebBrowser;
    procedure WebBrowser1DocumentComplete(ASender: TObject;
      const pDisp: IDispatch; var URL: OleVariant);
  private
    procedure UMPostEnable(var Msg: TMessage); message UM_POSTENABLE;
  end;

var
  Form2: TForm2;

implementation

uses Unit1;

{$R *.dfm}

procedure TForm2.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
begin
  if Screen.ActiveForm = Form1 then begin
    Enabled := False;
    PostMessage(Handle, UM_POSTENABLE, 0, 0);
  end;
end;

procedure TForm2.UMPostEnable(var Msg: TMessage);
begin
  Enabled := True;
end;

请注意,根据 文档, OnDocumentComplete 可以多次触发。但由于每次调用都会收到匹配的用户消息,因此这不会成为问题。

One simple solution can be to disable the form containing the web browser control. A disabled window will not gain the focus.

When the OnDocumentComplete event of TWebControl is fired, the browser control is ready to gain focus. Disable the form here, and post yourself a message so that you can enable the form shortly:

const
  UM_POSTENABLE = WM_USER + 12;

type
  TForm2 = class(TForm)
    WebBrowser1: TWebBrowser;
    procedure WebBrowser1DocumentComplete(ASender: TObject;
      const pDisp: IDispatch; var URL: OleVariant);
  private
    procedure UMPostEnable(var Msg: TMessage); message UM_POSTENABLE;
  end;

var
  Form2: TForm2;

implementation

uses Unit1;

{$R *.dfm}

procedure TForm2.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
begin
  if Screen.ActiveForm = Form1 then begin
    Enabled := False;
    PostMessage(Handle, UM_POSTENABLE, 0, 0);
  end;
end;

procedure TForm2.UMPostEnable(var Msg: TMessage);
begin
  Enabled := True;
end;

Note that, according to the documentation, OnDocumentComplete can be fired multiple times. But since each call will receive a matching user message, this wouldn't be a problem.

来世叙缘 2024-12-01 15:40:14
procedure TForm1.FormCreate(Sender: TObject);
begin
  Screen.OnActiveFormChange := ActiveFormChanged;
end;

procedure TForm1.ActiveFormChanged(Sender: TObject);
begin
  if not (csDestroying in ComponentState) then
    if ActiveControl <> nil then
      ActiveControl.SetFocus
end;

procedure TForm1.EditOrComboChange(Sender: TObject);
begin
  Form2.WebBrowser.SetFocus;
end;

编辑

也许这不是最优雅的方式。作为替代方案,我尝试使用

Form2.WebBrowser.Enabled := False;

来完全阻止焦点交换。这使焦点保持在编辑控件上,奇怪的是禁用的 WebBrowser 会更新到新页面,但更神秘的是,这隐藏了 Form1 上编辑控件中的插入符号。

procedure TForm1.FormCreate(Sender: TObject);
begin
  Screen.OnActiveFormChange := ActiveFormChanged;
end;

procedure TForm1.ActiveFormChanged(Sender: TObject);
begin
  if not (csDestroying in ComponentState) then
    if ActiveControl <> nil then
      ActiveControl.SetFocus
end;

procedure TForm1.EditOrComboChange(Sender: TObject);
begin
  Form2.WebBrowser.SetFocus;
end;

Edit

Maybe this is not the most elegant way. As an alternative I tried

Form2.WebBrowser.Enabled := False;

to prevent the focus exchange at all. This keeps the focus on the edit control and strangely enough does the disabled WebBrowser update to the new page, but more mysteriously, this hides the caret in the edit control on Form1.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文