Убедитесь, что форма не получает фокус при запуске процедуры - PullRequest
2 голосов
/ 15 июля 2011

У меня есть 2 формы - первая форма имеет кучу правок, списков и т.д., а вторая форма имеет веб-браузер.

У меня есть подпрограмма во 2-й форме, которая загружает HTML в веб-браузер, и эта подпрограмма запускается в событии OnChange всех моих элементов управления в первой форме.

Проблема в том, что мой фокусированный элемент управления в моей первой форме теряет фокус, когда 2-я форма загружает HTML в веб-браузер.

Как я могу убедиться, что 2-я форма не получает фокус при запуске процедуры? Или, что еще важнее, убедитесь, что фокусированный элемент управления в моей первой форме не потерял фокус?

Ответы [ 3 ]

2 голосов
/ 15 июля 2011

То, что вы пытаетесь сделать, идет вразрез с VCL и, вероятно, идет вразрез с обычными ожиданиями пользователей: когда отображается новое окно, если оно не является окном инструмента, обычное (и ожидаемое) поведение заключается в перемещении фокуса на него.Win32 Api для отображения окна - ShowWindow , и оно активирует окно, если не указан флаг SW_SHOWNOACTIVATE (или один из его вариантов).

Когда вы создаете VCLформа видна, вызов этой функции также сделан.Вызов ShowWindow похоронен в procedure TCustomForm.CMShowingChanged(var Message: TMessage), процедуре из 135 строк, которая жестко кодирует флаг SW_SHOWNORMAL (т. Е. Флаг активации) для вызова ShowWindow, который выполняет VCL.К сожалению, это большой кусок кода, и переопределить его будет непросто.Если бы это была моя программа, я бы, вероятно, попытался изменить код на месте: я бы добавил флаг DoNotActivate:Boolean на TCustomForm и изменил бы одну строку кода в CMShowingChanged, которая вызывает ShowWindow чтобы формы не-MDI учитывали этот флаг и просто вызывали 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;

Есть еще одна загвоздка с этим кодом: убедитесь, что вы переходите на веб-страницу, на которой нет формы, которая автоматически фокусируется.Переход к библиотеке MSDN от Microsoft может быть необычным для примера кода, но этот канонический пример (www.google.com) фокусируется на форме поиска.

2 голосов
/ 15 июля 2011

Одним из простых решений может быть отключение формы, содержащей элемент управления веб-браузера.Отключенное окно не получит фокус.

Когда происходит событие OnDocumentComplete TWebControl, элемент управления браузера готов к получению фокуса.Отключите форму здесь и отправьте себе сообщение, чтобы вы могли в ближайшее время включить форму:

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 может быть запущен несколько раз,Но так как каждый вызов будет получать соответствующее сообщение пользователя, это не будет проблемой.

1 голос
/ 15 июля 2011
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.

...