拖动时如何避免表单获得焦点
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;
我有一个只包含 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;