Delphi XE-TRibbon操作始终将焦点发送到MainForm

Delphi XE-TRibbon操作始终将焦点发送到MainForm,delphi,focus,delphi-xe,ribbon,taction,Delphi,Focus,Delphi Xe,Ribbon,Taction,当我在不是应用程序主窗体的窗体上放置TRibbon控件时,该TRibbon的操作(即剪切、粘贴)将始终在执行操作后将焦点返回主窗体 即使保存TRibbon的TForm不是MainForm的子级,也会发生这种情况 我使用的是Windows 7 64位Embarcadero RAD Studio XE版本15.0.3953.35171 我是否错误地使用了TRibbon控件,或者这是TRibbon的问题?这显然是出于设计。“ribbonactnctrls.pas”中的示例代码片段: procedure

当我在不是应用程序主窗体的窗体上放置TRibbon控件时,该TRibbon的操作(即剪切、粘贴)将始终在执行操作后将焦点返回主窗体

即使保存TRibbon的TForm不是MainForm的子级,也会发生这种情况

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


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

这显然是出于设计。“ribbonactnctrls.pas”中的示例代码片段:

procedure TRibbonBaseButtonControl.Click;
begin
  inherited;
  SetFocus(Application.MainForm.Handle);
end;
正如您所见,没有检查任何有助于我们避免呼叫的条件。菜单项选择和按键处理程序中也有相同的代码


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

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

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;
但是,这将导致主窗体在每次执行操作时短暂闪烁。如果你不想这样,你可以改变设计,让主窗体知道它什么时候得到了不需要的焦点,然后假装它没有聚焦

在单元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;
在单元2中:

uses
  unit1;

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

但是,如果在项目源代码中没有设置“MainFormOnTaskBar”,这是不够的,因为这样一来,主表单不仅会获得关注,而且会被放在前面。在这种情况下,两种形式都可以通过冻结其z顺序来响应不需要的焦点更改/激活。然后,第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;
    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;
第二单元:

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;

ribbon旨在成为主窗体中的UI元素,它实际上“修改”了添加到其中的窗体。如果你在程序的主窗体之外的其他地方放置了一个功能区,它将焦点发送回application.MainForm,我并不感到惊讶;它希望它是主窗体的一部分。VCL附带源代码,因此您可以打开该单元,看看是否可以找到有问题的代码。我正在设计的应用程序具有“Outlook”的感觉,在它的实现中,主程序使用一个功能区,而创建电子邮件、日历项、联系人、,等等。每当我在电子邮件中使用Outlook功能区操作时,它不会将我的焦点发送回Outlook主窗口。我已经对TRibbon组件的源代码做了一些检查,但是它确实有点厚。我将继续这样做,看看我是否能发现这是在哪里发生的,如果我能覆盖这个行为。不过,到目前为止,我运气不好。对我来说,这听起来很像一只虫子。这种行为不应该发生。如果你可以在一个简单的应用程序中重新编程,那么你应该向QCO报告,无论这是否是一个糟糕的设计理念,我也可以在Rad Studio XE上复制。在阅读文档时,没有明确说明这是否是预期行为,因此它看起来像一个bug。@Craig不,不是这样。Outlook就是一个例子。我不知道SDI是怎么回事。反应很好!我仍然不知道是否有人能告诉我为什么他们设计了这样一种强制方式的控件,而它本可以将控件返回给Ribbon的父控件-我猜这只是一个德尔菲式的谜。