我有两个表单-第一个表单有一堆编辑,组合框等,第二个表单有一个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上的编辑控件中的插入符号。