Delphi XE: las acciones de TRibbon siempre envían el foco a MainForm
Pregunta
Cuando coloco un control TRibbon en un formulario que no es el MainForm de la aplicación, las acciones de TRibbon (es decir, Cortar, Pegar) siempre devolverán el foco al MainForm después de que se ejecute la acción.
Esto ocurre incluso si el TForm que contiene el TRibbon no es un hijo del MainForm.
Estoy usando Windows 7 de 64 bits, Embarcadero RAD Studio XE versión 15.0.3953.35171.
¿Estoy usando el control TRibbon de forma incorrecta o se trata de un problema con TRibbon?
Solución
Esto es evidentemente por diseño. Fragmento de código de muestra de 'ribbonactnctrls.pas':
procedure TRibbonBaseButtonControl.Click;
begin
inherited;
SetFocus(Application.MainForm.Handle);
end;
Como puede ver, no hay ninguna condición marcada que nos ayude a evitar la llamada. También existe el mismo código en la selección de elementos del menú y en los controladores de pulsaciones de teclas.
Probablemente modificaría la fuente comentando las llamadas de enfoque y trataría de ver si hay efectos secundarios.
Como alternativa, puede restaurar el foco de nuevo a su formulario después de cambiarlo al formulario principal. Supongamos que 'ActionList1' es TActionList que contiene acciones estándar en el no formulario 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;
Sin embargo, esto hará que el formulario principal parpadee brevemente cada vez que se ejecute una acción. Si no quiere eso, puede cambiar el diseño para que el formulario principal sepa cuándo está recibiendo un enfoque no deseado y fingir que no está enfocado.
En la unidad 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;
en la unidad 2:
uses
unit1;
procedure TForm2.ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
begin
Form1.IgnoreFocus := True;
PostMessage(Form1.Handle, UM_CANCELIGNOREFOCUS, NativeInt(Self), 0);
end;
Sin embargo, esto no es suficiente si no tiene 'MainFormOnTaskBar' configurado en el código fuente del proyecto, ya que entonces el formulario principal no solo ganará el foco sino que se traerá al frente. En este caso, ambas formas podrían responder al cambio / activación de enfoque no deseado congelando sus órdenes z. El código sería entonces 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;
y para unit2:
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;