确保当例程运行时Form没有得到焦点



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

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

问题是,我的焦点控制在我的第一个表单失去焦点,当第二个表单加载HTML到web浏览器。

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

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

当您使VCL表单可见时,也会调用该函数。对ShowWindow的调用隐藏在procedure TCustomForm.CMShowingChanged(var Message: TMessage)中,这是一个135行的过程,它为VCL发出的ShowWindow调用硬编码SW_SHOWNORMAL标志(即:激活标志)。不幸的是,这是一大块代码,重写它并不容易。如果这是我的程序,我可能会尝试更改代码:我将在TCustomForm中添加DoNotActivate:Boolean标志,并更改CMShowingChanged中为非mdi表单调用ShowWindow的单行代码,以考虑该标志并简单地调用ShowWindow(Handle, SW_SHOWNOACTIVATE)。如果更改VCL不是一件轻松的事情,您可以使用以下简单的解决方案:

我建议的技巧是创建新的形式(一个持有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)将重点放在搜索表单上。

一个简单的解决方案是禁用包含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可以多次触发。但是由于每次调用都会收到匹配的用户消息,所以这不会成为问题。

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上的编辑控件中的插入符号。

最新更新