TActionMainMenuBar и TActionToolbar теряют настройки - PullRequest
8 голосов
/ 06 марта 2012

Недавно я наткнулся на действительно странное поведение. Когда я использую TActionMainMenuBar (или TActionToolBar) в своей программе, компилирую и запускаю, а затем запускаю Photoshop CS5 или Internet Explorer 9, ActionMainMenuBar (и ActionToolBar) теряет все свои настройки. Цвета, определенные в назначенной цветовой карте, исчезают, а настройки шрифта также теряются. Кто-нибудь видел это раньше и знает обходной путь?

D2007 Pro (все обновления применены), D2010 Pro (все обновления применены), Vista Home Premium 32 bit, NVidia GForce 8600 GT, установлен последний драйвер.

Шаги для воспроизведения:

  1. Удалите TActionManager и TActionMainMenuBar на форму
  2. Создать категорию с некоторыми пунктами меню
  3. Перетащите категорию на ActionMainMenuBar
  4. Назначение TwilightColorMap для ActionMainMenuBar
  5. Запустить программу
  6. Запустите IE9 или Photoshop CS5
  7. Наблюдение за исчезновением всех предустановленных настроек (чтобы увидеть эффект, нужно снова закрыть IE9)

Если сначала запустить Photoshop или IE, а затем программу Delphi, ничего не произойдет. Ошибка также присутствует в режиме разработки в IDE. Один из разработчиков уже подтвердил описанное поведение своей системы с Win7 Pro 32bit и ATI Radeon 9800 Pro.

Спасибо за любые комментарии / решения

Phil

PS: использование Photoshop CS3 не приводит к этой ошибке

Ответы [ 2 ]

8 голосов
/ 10 мая 2012

Вот еще один способ воспроизвести проблему:

  • Выполните шаги с 1 по 4, как в вопросе (включая удаление TwilightColorMap).
  • Добавьте кнопку в форму с кодом Perform(WM_SETTINGCHANGE, 0, 0); в обработчике кликов.
  • Запустите приложение и нажмите кнопку.


Итак, теперь мы знаем, что проблема может быть вызвана вещанием WM_SETTINGCHANGE, и мы можем задаться вопросом, будет ли запуск IE аналогичным образом:

type
  TForm1 = class(TForm)
    ..
  protected
    procedure WMSettingChange(var Message: TWMSettingChange);
      message WM_SETTINGCHANGE;
    ..

procedure TForm1.WMSettingChange(var Message: TWMSettingChange);
begin
  Memo1.Lines.Add(IntToHex(Message.Flag, 4) + ', ' + Message.Section);
  inherited;
end;

После того, как мы запустили наше приложение и затем запустили IE, через несколько секунд в памятке появится следующее:

0000, Программное обеспечение \ Microsoft \ Internet Explorer \ SearchScopes

Я понятия не имею, что IE должен сказать нашей форме (и всем остальным окнам верхнего уровня) при каждом запуске, и я не знаю, делает ли это это на каждой Windows-коробке на Земле или только на вашей и моей. , но, очевидно, ActionMainMenuBar плохо справляется с этим.


Элемент управления win (форма), получив WM_WININICHANGE, выполняет CM_WININICHANGE и, получив его, транслирует то же самое на все свои элементы управления. Ниже показано, как это обрабатывается строкой меню:

procedure TCustomActionMainMenuBar.CMWininichange(var Message: TWMWinIniChange);
begin
  inherited;
  RequestAlign;
  Font.Assign(Screen.MenuFont);
end;

Считая, что шрифт системного меню мог быть изменен (код должен был искать в сообщении раздел «WindowsThemeElement» или «WindowMetrics», но в любом случае ...), он переназначается из обновленного Screen.MenuFont. Проблема в том, что мы не использовали его точно.

Кроме того, ColorMap реагирует на CM_WININICHANGE, сбрасывая свои цвета, вызывая метод UpdateColors. Это даже задокументировано :

UpdateColors вызывается автоматически, когда компонент ActionBand получает сообщение CM_WININICHANGE.

<ч /> Таким образом, решение будет включать в себя решение о том, что делать и переопределение поведения, и я постараюсь прокомментировать приведенное ниже решение, почему я считаю, что это будет правильное решение:

type
  // Derive your own ColorMap that would reset its own colors.
  // This is an interposer for simplicity..
  TTwilightColorMap = class(actncolormaps.TTwilightColorMap)
  public
    procedure UpdateColors; override;
  published
    property Color default clGreen;
    property FontColor default clYellow;
    property MenuColor default $4488FF;
    // reintroduce as many property as necessary, probably all is necessary..
  end;

  TForm1 = class(TForm)
    ..
  private
    FSaveMenuFont: TFont; // will hold initial main menu bar's font settings
  protected
    procedure WMSettingChange(var Message: TWMSettingChange);
      message WM_SETTINGCHANGE;
  end;

..

procedure TForm1.FormCreate(Sender: TObject);
begin
  FSaveMenuFont := TFont.Create;
  FSaveMenuFont.Assign(ActionMainMenuBar1.Font);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FSaveMenuFont.Destroy;
end;

procedure TForm1.WMSettingChange(var Message: TWMSettingChange);
begin
  inherited;
  // The below are the *section*s that really changing system settings
  // would notify that I'm aware of, there may be more...
  if (Message.Section <> 'WindowsThemeElement')
      or (Message.Section <> 'WindowMetrics') then
    ActionMainMenuBar1.Font.Assign(FSaveMenuFont)
  else
    // Develop your logic here. The system menu font might really have been
    // changed. You can get it from Screen.MenuFont. But then if we had been
    // using the system font, the control already applies the change by default.

end;

procedure TTwilightColorMap.UpdateColors;
begin
  inherited;
  // Reset your colors, note that system colors might have been
  // changed or not. If changed, they should be reflected in 'cl..' constants.
  Color := clGreen;
  FontColor := clYellow;
  MenuColor := $4488FF;
end;
4 голосов
/ 30 мая 2012

Нашли это! Проблема заключалась в том, что свойство Colormap для ActionMainMenuBar1 сбрасывается в ActionMainMenuBar1.DefaultColormap каждый раз, когда IE9 или Photoshop CS5 запускаются / закрываются. Я приму решение Sertac в качестве ответа, потому что оно указало мне верное направление, как решить эту проблему.

Вот окончательный код. Теперь он работает безупречно (насколько я могу судить) с IE9 и Photoshop CS5.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ActnMan, ActnColorMaps, ActnList, ToolWin, ActnCtrls, ActnMenus,
  StdCtrls;


type
  TForm1 = class(TForm)
    ActionManager1: TActionManager;
    ActionMainMenuBar1: TActionMainMenuBar;
    Action1: TAction;
    Action2: TAction;
    Action3: TAction;
    Action4: TAction;
    TwilightColorMap1: TTwilightColorMap;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ActionMainMenuBar1GetControlClass(Sender: TCustomActionBar;
      AnItem: TActionClient; var ControlClass: TCustomActionControlClass);
  protected
    procedure WMSettingChange(var Message: TWMSettingChange); message WM_SETTINGCHANGE;
  private
    { Private declarations }
    FSaveMenuFont: TFont; // will hold initial main menu bar's font settings
    FSaveColormap: TTwilightColormap;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


// Fixing paint issue when IE9 was run and closed again

procedure TForm1.ActionMainMenuBar1GetControlClass(Sender: TCustomActionBar;
  AnItem: TActionClient; var ControlClass: TCustomActionControlClass);
begin
  ActionMainMenuBar1.ColorMap.Assign(FSaveColormap);
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  FSaveMenuFont := TFont.Create;
  FSaveMenuFont.Assign(ActionMainMenuBar1.Font);
  FSaveColormap := TTwilightColormap.Create(Self);
  FSaveColormap.Assign(ActionMainMenuBar1.Colormap);
end;


procedure TForm1.FormDestroy(Sender: TObject);
begin
  FSaveMenuFont.Destroy;
  FSaveColormap.Destroy;
end;


procedure TForm1.WMSettingChange(var Message: TWMSettingChange);
begin
  inherited;
  // Memo1.Lines.Add(IntToHex(Message.Flag, 4) + ', ' + Message.Section);
  ActionMainMenuBar1.Font.Assign(FSaveMenuFont);
  // In case Photoshop CS5 was run and closed before
  ActionMainMenuBar1.ColorMap.Assign(FSaveColormap);
end;

end.

Еще раз спасибо всем за помощь.

...