我有 2 个表单 - 第一个表单有一堆编辑、组合框等,第二个表单有一个 Web 浏览器。
我在第二个表单中有一个例程,它将 HTML 加载到 Web 浏览器中,并且该例程在第一个表单上的所有控件的 OnChange 事件中触发。
问题是,当第二个表单将 HTML 加载到网络浏览器时,我对第一个表单的集中控制失去了焦点。
如何确保例程触发时第二种形式不会获得焦点?或者,更重要的是 - 确保我的第一个表单上的聚焦控件不会失去焦点?
最佳答案
您尝试做的事情违背了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) 将焦点设置在搜索表单上。
关于forms - 确保例程运行时表单不会获得焦点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6711275/