Delphi XE - действия TRibbon всегда отправляют фокус на MainForm - PullRequest
6 голосов
/ 06 декабря 2011

Когда я помещаю элемент управления TRibbon в форму, которая не является основной формой приложения, действия этого TRibbon (т. Е. Вырезать, вставить) всегда возвращают фокус на основную форму после выполнения действия.

Этопроисходит, даже если TForm, который содержит TRibbon, не является дочерним элементом MainForm.

Я использую 64-разрядную версию Windows 7, Embarcadero RAD Studio XE версии 15.0.3953.35171.

Использую ли янеправильный контроль TRibbon, или это проблема с TRibbon?

1 Ответ

2 голосов
/ 13 января 2012

Это очевидно по замыслу. Пример фрагмента кода из 'ribbonactnctrls.pas':

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

Как видите, нет проверенных условий, которые помогли бы нам избежать вызова. Тот же код есть и в выборе пунктов меню и в обработчиках нажатия клавиш.


Я, вероятно, изменил бы источник, комментирующий вызовы фокуса, и попытался бы увидеть, есть ли какие-либо побочные эффекты.

В качестве альтернативы вы можете восстановить фокус обратно на форму после ее переключения на основную форму. Предположим, 'ActionList1' - это 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;

и для 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;
...