Delphi XE - Le azioni TRibbon inviano sempre il focus a MainForm
Domanda
Quando metto un controllo TRibbon su un form che non è il MainForm dell'applicazione, le azioni di TRibbon (cioè Taglia, Incolla) restituiranno sempre il focus al MainForm dopo che l'azione è stata eseguita.
Ciò si verifica anche se il TForm che contiene il TRibbon non è un figlio del MainForm.
Utilizzo Windows 7 a 64 bit, Embarcadero RAD Studio XE versione 15.0.3953.35171.
Sto usando il controllo TRibbon in modo errato o questo è un problema con TRibbon?
Soluzione
Questo è evidentemente di progettazione. Esempio di snippet di codice da "ribbonactnctrls.pas":
procedure TRibbonBaseButtonControl.Click;
begin
inherited;
SetFocus(Application.MainForm.Handle);
end;
Come vedi non ci sono condizioni controllate che ci aiutino a evitare la chiamata. C'è lo stesso codice anche nella selezione delle voci di menu e nei gestori di pressione dei tasti.
Probabilmente modificherei la fonte commentando le chiamate al focus e proverei a vedere se ci sono effetti collaterali.
In alternativa puoi ripristinare il focus sul tuo form dopo che è passato al form principale. Supponiamo che "ActionList1" sia la TActionList che contiene azioni standard nel modulo principale non :
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;
Questo farà sì che il form principale lampeggi brevemente ogni volta che viene eseguita un'azione. Se non lo desideri, puoi modificare il design in modo che il modulo principale sappia quando sta ottenendo un focus indesiderato e fingere di non essere focalizzato.
Nell'unità 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;
nell'unità 2:
uses
unit1;
procedure TForm2.ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
begin
Form1.IgnoreFocus := True;
PostMessage(Form1.Handle, UM_CANCELIGNOREFOCUS, NativeInt(Self), 0);
end;
Tuttavia, questo non è sufficiente se non hai "MainFormOnTaskBar" impostato nel sorgente del progetto, da allora il form principale non solo guadagnerà il focus ma sarà portato in primo piano. In questo caso entrambe le forme potrebbero rispondere al cambiamento / attivazione indesiderata del focus congelando i loro ordini z. Il codice diventerebbe quindi per unità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;
e per l'unità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;