TActionMainMenuBar и TActionToolbar теряют настройки
недавно я наткнулся на очень странное поведение. Когда я использую TActionMainMenuBar (или TActionToolBar) в своей программе, компилирую и запускаю, а затем запускаю Photoshop CS5 или Internet Explorer 9, ActionMainMenuBar (и ActionToolBar) теряет все свои настройки. Цвета, определенные в назначенной цветовой карте, исчезают, а настройки шрифта также теряются. Кто-нибудь видел это раньше и знает обходной путь?
D2007 Pro( все обновления применены), D2010 Pro (все обновления применены), Vista Home Premium 32 бит, NVidia GForce 8600 GT, установлен последний драйвер.
воспроизведение:
- отбросьте TActionManager и TActionMainMenuBar в форме
- создать Категорию с некоторыми пунктами меню
- перетащите категорию на ActionMainMenuBar
- назначить TwilightColorMap для ActionMainMenuBar
- запустить программу
- запустите IE9 или Photoshop CS5
- смотреть все предопределенные настройки исчезают (вам нужно снова закрыть IE9, чтобы увидеть эффект)
Если вы сначала запустите Photoshop или IE, а затем программу Delphi, ничего не произойдет. Ошибка также присутствует в режиме разработки в среде IDE. Другой разработчик уже подтвердил описанное поведение для своей системы с Win7 Pro 32bit и Ati Radeon 9800 Pro.
Thx для любых комментариев / решений
Фил
PS: использование Photoshop CS3 не создает эту ошибку
2 ответов
вот еще один способ воспроизведения проблемы:
- выполните шаги с 1 по 4, как в вопросе (включая удаление TwilightColorMap).
- добавить кнопку в форму с кодом
Perform(WM_SETTINGCHANGE, 0, 0);
в обработчике щелчка. - запустите приложение и нажмите кнопку.
Итак, теперь мы знаем, что A WM_SETTINGCHANGE
broadcast может вызвать проблему, мы могли бы задаться вопросом, если запуск 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 должен сказать нашей форме (и всем другим окнам верхнего уровня) при каждом запуске, и я не знаю, делает ли он это на каждом окне окна на земле или только ваши и мои, но, очевидно,ActionMainMenuBar
не хорош в обращении он.
a win control (форма), получение WM_WININICHANGE
выполняет CM_WININICHANGE
и после получения он передает то же самое всем своим элементам управления. Ниже показано, как он обрабатывается в строке меню:
procedure TCustomActionMainMenuBar.CMWininichange(var Message: TWMWinIniChange);
begin
inherited;
RequestAlign;
Font.Assign(Screen.MenuFont);
end;
думая, что шрифт системного меню мог быть изменен (код должен был искать раздел "WindowsThemeElement" или "WindowMetrics" в сообщении, но в любом случае..), он переназначается из обновленного Screen.MenuFont
. Проблема в том, что мы не были использующий ее.
кроме того, цветовая карта отвечает на 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 88FF;
// 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 := 88FF;
end;
нашли его! Проблема заключалась в том, что свойство Colormap ActionMainMenuBar1 сбрасывается в ActionMainMenuBar1.DefaultColormap каждый раз, когда IE9 или Photoshop CS5 запускается / закрывается. Я приму решение Сертака как ответ, потому что он указал мне в правильном направлении, как решить эту проблему.
вот окончательный код. Он работает безупречно (насколько я могу судить) как с 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.
еще раз спасибо всем за вашу помощь.