Pergunta

Quando coloco um controle TRibbon em um formulário que não é o MainForm do aplicativo, as ações desse TRibbon (ou seja, cortar, colar) sempre retornarão o foco para o MainForm após a ação ser executada.

Isso ocorre mesmo se o TForm que contém o TRibbon não for filho do MainForm.

Estou usando o Windows 7 de 64 bits, Embarcadero RAD Studio XE versão 15.0.3953.35171.

Estou usando o controle do TRibbon incorretamente ou isso é um problema do TRibbon?

Foi útil?

Solução

Isso é evidentemente intencional. Amostra de snippet de código de 'ribbonactnctrls.pas':

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

Como você vê, não foram verificadas condições que nos ajudem a evitar a ligação. Há o mesmo código também na seleção de itens de menu e manipuladores de teclas pressionadas.


Eu provavelmente modificaria a fonte comentando as chamadas de foco e tentaria ver se há algum efeito colateral.

Como alternativa, você pode restaurar o foco de volta ao seu formulário depois que ele for alterado para o formulário principal. Suponha que 'ActionList1' seja a TActionList que contém ações padrão no não formulário principal:

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;

No entanto, isso fará com que o formulário principal pisque brevemente toda vez que uma ação for executada. Se você não quiser isso, pode alterar o design para que o formulário principal saiba quando está recebendo um foco indesejado e finja que não está focado.

Na unidade1:

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;

na unidade 2:

uses
  unit1;

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


No entanto, isso não é suficiente se você não tem 'MainFormOnTaskBar' definido no código-fonte do projeto, já que o formulário principal não só ganhará destaque, mas será levado para a frente. Nesse caso, os dois formulários podem responder à alteração / ativação indesejada do foco congelando suas ordens z. O código se tornaria então para 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;

e para a unidade 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;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top