Вот еще один способ воспроизвести проблему:
- Выполните шаги с 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;