Delphi XE - TRIBbon动作总是将焦点发送给MainForm

Aar*_*ron 6 delphi ribbon focus taction delphi-xe

当我将TRibbon控件放在不是应用程序MainForm的表单上时,TRibbon的操作(即剪切,粘贴)将始终在执行操作后将焦点返回到MainForm.

即使持有TRibbon的TForm不是MainForm的子代,也会发生这种情况.

我使用的是Windows 7 64位,Embarcadero RAD Studio XE版本15.0.3953.35171.

我是否错误地使用了TRibbon控件,或者这是TRibbon的一个问题?

Ser*_*yuz 2

这显然是设计使然。来自“ribbonactnctrls.pas”的示例代码片段:

procedure TRibbonBaseButtonControl.Click;
begin
  inherited;
  SetFocus(Application.MainForm.Handle);
end;
Run Code Online (Sandbox Code Playgroud)

如您所见,没有检查任何条件可以帮助我们避免接到电话。菜单项选择和按键处理程序中也有相同的代码。


我可能会修改注释焦点调用的源代码,并尝试查看是否有任何副作用。

作为替代方案,您可以在切换到主窗体后将焦点恢复到窗体。假设“ActionList1”是包含非主窗体上的标准操作的 TActionList :

type
  TForm2 = class(TForm)
    ..
    procedure ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
  private
   ..

procedure TForm2.ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
begin
  PostMessage(Handle, WM_SETFOCUS, WPARAM(True), 0);
end;
Run Code Online (Sandbox Code Playgroud)

然而,这将导致主窗体在每次执行操作时短暂闪烁。如果您不希望这样,您可以更改设计,以便主窗体知道它何时获得不需要的焦点,并假装它没有获得焦点。

在单元 1 中:

const
  UM_CANCELIGNOREFOCUS = WM_USER + 7;

type
  TForm1 = class(TForm)
    ..
  private
    FIgnoreFocus: Boolean;
    procedure UMCancelIgnoreFocus(var Msg: TMessage); message UM_CANCELIGNOREFOCUS;
    procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCACTIVATE;
  public
    property IgnoreFocus: Boolean write FIgnoreFocus;
  end;

...
uses Unit2;

procedure TForm1.WMNCActivate(var Msg: TWMNCActivate);
begin
  Msg.Result := 0;
  if not (Msg.Active and FIgnoreFocus) then
    inherited;
end;

procedure TForm1.UMCancelIgnoreFocus(var Msg: TMessage);
begin
  FIgnoreFocus := False;
  TForm(Msg.WParam).SetFocus;
end;
Run Code Online (Sandbox Code Playgroud)

在单元2中:

uses
  unit1;

procedure TForm2.ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
begin
  Form1.IgnoreFocus := True;
  PostMessage(Form1.Handle, UM_CANCELIGNOREFOCUS, NativeInt(Self), 0);
end;
Run Code Online (Sandbox Code Playgroud)


但是,如果您没有在项目源代码中设置“MainFormOnTaskBar”,这还不够,因为这样主窗体不仅会获得焦点,而且会被带到前面。在这种情况下,两种形式都可以通过冻结其 z 顺序来响应不需要的焦点更改/激活。然后,unit1 的代码将变为:

const
  UM_CANCELIGNOREFOCUS = WM_USER + 7;

type
  TForm1 = class(TForm)
    ..
  private
    FIgnoreFocus: Boolean;
    procedure UMCancelIgnoreFocus(var Msg: TMessage); message UM_CANCELIGNOREFOCUS;
    procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCACTIVATE;
    procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging);
        message WM_WINDOWPOSCHANGING;
  public
    property IgnoreFocus: Boolean read FIgnoreFocus write FIgnoreFocus;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit2;

procedure TForm1.WMNCActivate(var Msg: TWMNCActivate);
begin
  Msg.Result := 0;
  if not (Msg.Active and FIgnoreFocus) then
    inherited;
end;

procedure TForm1.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
begin
  inherited;
  if FIgnoreFocus then
    Msg.WindowPos.flags := Msg.WindowPos.flags or SWP_NOZORDER;
end;

procedure TForm1.UMCancelIgnoreFocus(var Msg: TMessage);
begin
  FIgnoreFocus := False;
  TForm(Msg.WParam).SetFocus;
end;
Run Code Online (Sandbox Code Playgroud)

对于单元2:

type
  TForm2 = class(TForm)
    ..
    procedure ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
  private
    procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging);
        message WM_WINDOWPOSCHANGING;
  public
  end;

var
  Form2: TForm2;

implementation

uses
  unit1;

{$R *.dfm}

procedure TForm2.ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
begin
  Form1.IgnoreFocus := True;
  PostMessage(Form1.Handle, UM_CANCELIGNOREFOCUS, NativeInt(Self), 0);
end;

procedure TForm2.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
begin
  inherited;
  if Form1.IgnoreFocus then
    Msg.WindowPos.flags := Msg.WindowPos.flags or SWP_NOZORDER;
end;
Run Code Online (Sandbox Code Playgroud)