【问题标题】:Delphi XE - TRibbon actions always send focus to MainFormDelphi XE - TRibbon 动作总是将焦点发送到 MainForm
【发布时间】:2012-01-14 06:09:15
【问题描述】:

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

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

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

是我错误地使用了 TRibbon 控件,还是 TRibbon 的问题?

【问题讨论】:

  • 功能区旨在成为主表单中的 UI 元素,实际上它确实“修改”了它添加到的表单。如果你在程序主窗体以外的地方放置了一个功能区,它会将焦点发送回 application.MainForm,我并不感到惊讶;它期望它是主要形式的一部分。 VCL 带有源代码,因此您可以打开该单元并查看是否可以找到有问题的代码。
  • 我正在设计的应用程序是为了一种“Outlook”的感觉,在它的实现中,它使用一个功能区作为主程序,另一个功能区用于创建电子邮件、日历项目、联系人等。每当我在电子邮件中使用 Outlook 功能区操作时,它都不会将我的焦点发送回 Outlook 主窗口。我已经浏览了 TRibbon 组件的源代码,但它确实有点厚。我将继续这样做,看看是否可以发现发生这种情况的位置,以及是否可以覆盖此行为。不过,到目前为止,我还没有运气。
  • 对我来说听起来非常像一个错误。这种行为不应该发生。如果您可以在一个简单的应用程序中重现,那么您应该将其报告给 QC
  • 不管它是否是一个糟糕的设计理念,我也可以在 Rad Studio XE 上复制它。阅读文档并没有明确说明这是否是预期的行为,所以它看起来像一个错误。
  • @Craig 不,事实并非如此。 Outlook 就是一个例子。我看不出 SDI 的作用。

标签: delphi focus delphi-xe ribbon taction


【解决方案1】:

这显然是设计使然。 'ribbonactnctrls.pas' 中的示例代码 sn-p:

procedure TRibbonBaseButtonControl.Click;
begin
  inherited;
  SetFocus(Application.MainForm.Handle);
end;

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


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

作为替代方案,您可以在切换到主窗体后将焦点恢复到窗体。假设 'ActionList1' 是包含 not 主窗体上的标准操作的 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;

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

在单元 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 顺序来响应不需要的焦点更改/激活。然后代码将变为 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;

对于单元 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;

【讨论】:

  • 很好的回应!我仍然不知道是否有人能告诉我为什么他们设计控件以如此强制的方式运行,而它本可以将控制权返回给 Ribbon 的父级 - 我想这只是一个 Delphic 之谜。
猜你喜欢
  • 1970-01-01
  • 2016-05-07
  • 1970-01-01
  • 1970-01-01
  • 2011-08-31
  • 2011-07-15
  • 2016-01-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多