拖动时如何避免表单获得焦点

How can I avoid a form getting focus when dragged

我有一个只包含 TTouchKeyboard 的简单表格。形式 BorderStyle 设置为 bsToolWindow。为了避免单击触摸键盘时表单获得焦点,我使用此实现处理 WM_MOUSEACTIVATE 消息:

procedure TKeyboardForm.WMMouseActivate(var Message: TWMMouseActivate);
begin
  Message.Result := MA_NOACTIVATE;
end;

BorderStyle 设置允许表单与标题栏一起拖动,但在那种情况下表单仍会获得焦点。有没有办法避免这种情况?

更新: 我尝试在 CreateParams 中将 WS_EX_NOACTIVATE 添加到 ExStyle,但不幸的是,这并不妨碍表单在拖动时获得焦点。

procedure TKeyboardForm.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.ExStyle := Params.ExStyle or WS_EX_NOACTIVATE;
end;

以下 WMMouseActivate()WMNCActivate() 和重设焦点的组合似乎可以满足您的愿望:

键盘表单(带有 BorderStyle = bsToolWindow)具有 WM_MOUSEACTIVATE(您已经拥有)和 WM_NCACTIVATE 的消息处理程序。后者用于使用编辑控件将焦点重置到 window。

此外,键盘窗体将跟踪哪个窗体持有具有焦点的编辑(或其他)控件,并通过引入一种新的显示方法来实现这一点,我称之为 ShowUnfocused() 和一个名为FocusedForm: THandle.

procedure TKbdForm.ShowUnfocused(FocusedWindow: THandle);
begin
  FocusedForm := FocusedWindow;
  Show;
end;

procedure TKbdForm.FormShow(Sender: TObject);
begin
  SetForegroundWindow(FocusedForm);
end;

procedure TKbdForm.WMMouseActivate(var Message: TWMMouseActivate);
begin
  Message.Result := MA_NOACTIVATE;
end;

procedure TKbdForm.WMNCActivate(var Message: TWMNCActivate);
begin
  Message.Result := 1; // important
  SetForegroundWindow(FocusedForm);
end;

键盘窗体由编辑控件的以下公共代码调用:

procedure TForm17.EditClick(Sender: TObject);
begin
  KbdForm.ShowUnfocused(self.Handle);
  (Sender as TWinControl).SetFocus;
end;

上面所说的替代方法,可以是设置 BorderStyle = bsNone 并直接从表单表面(或者可能是模仿的面板)安排带有 Mouse Down, Move, Up 事件的表单拖动顶部框架),并添加一个关闭按钮。挑战在于让它在视觉上可以接受。

因为我对需要我在键盘表单中手动更新焦点表单变量的方法不是很满意,所以我搜索了一个更透明的解决方案并想出了这个方法。

更新: 以前的方法在 VCL 样式方面存在一些问题。此外,并非所有的消息处理程序都是真正必需的,尽管其他消息处理程序也很有用。此版本与 VCL 样式配合良好,尽可能避免任何闪烁:

type
  TKeyboardForm = class(TForm)
    TouchKeyboard1: TTouchKeyboard;
  private
    FLastFocusedForm: TCustomForm;
    procedure SetLastFocusedForm(const Value: TCustomForm);
  protected
    procedure WMActivate(var Message: TWMActivate); message WM_ACTIVATE;
    procedure WMMouseActivate(var Message: TWMMouseActivate); message WM_MOUSEACTIVATE;
    procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    property LastFocusedForm: TCustomForm read FLastFocusedForm write SetLastFocusedForm;
  public
    class constructor Create;
    destructor Destroy; override;
    function SetFocusedControl(Control: TWinControl): Boolean; override;
  end;

type
  TKeyboardFormStyleHook = class(TFormStyleHook)
  protected
    procedure WMNCActivate(var Message: TWMNCActivate); message WM_NCACTIVATE;
  end;

procedure TKeyboardFormStyleHook.WMNCActivate(var Message: TWMNCActivate);
begin
  { avoids the title bar being drawn active for blink }
  Message.Active := False;
  inherited;
end;

class constructor TKeyboardForm.Create;
begin
  TCustomStyleEngine.RegisterStyleHook(TKeyboardForm, TKeyboardFormStyleHook);
end;

destructor TKeyboardForm.Destroy;
begin
  LastFocusedForm := nil;
  inherited;
end;

procedure TKeyboardForm.Notification(AComponent: TComponent; Operation: TOperation);
begin
  if (Operation = opRemove) and (AComponent = FLastFocusedForm) then begin
    FLastFocusedForm := nil;
  end;
  inherited;
end;

function TKeyboardForm.SetFocusedControl(Control: TWinControl): Boolean;
begin
  LastFocusedForm := Screen.FocusedForm;
  result := inherited;
end;

procedure TKeyboardForm.SetLastFocusedForm(const Value: TCustomForm);
begin
  if FLastFocusedForm <> Value then
  begin
    if FLastFocusedForm <> nil then begin
      FLastFocusedForm.RemoveFreeNotification(Self);
    end;
    FLastFocusedForm := Value;
    if FLastFocusedForm <> nil then begin
      FLastFocusedForm.FreeNotification(Self);
    end;
  end;
end;

procedure TKeyboardForm.WMActivate(var Message: TWMActivate);
begin
  Message.Active := WA_INACTIVE;
  inherited;
end;

procedure TKeyboardForm.WMMouseActivate(var Message: TWMMouseActivate);
begin
  inherited;
  Message.Result := MA_NOACTIVATE;
end;

procedure TKeyboardForm.WMSetFocus(var Message: TWMSetFocus);
begin
  inherited;
  if (FLastFocusedForm <> nil) and (message.FocusedWnd <> FLastFocusedForm.Handle) then begin
    SendMessage(FLastFocusedForm.Handle, WM_SETFOCUS, 0, 0);
    Message.FocusedWnd := FLastFocusedForm.Handle;
  end;
end;