TActionMainMenuBar和TActionToolbar丢失设置

8
最近我发现了一个非常奇怪的问题。当我在程序中使用TActionMainMenuBar(或TActionToolBar),编译和运行后,启动Photoshop CS5或Internet Explorer 9时,ActionMainMenuBar(和ActionToolBar)将失去所有设置。分配的颜色映射中定义的颜色消失,字体设置也丢失了。有人见过这种情况并知道解决方法吗?
D2007 Pro(应用了所有更新),D2010 Pro(应用了所有更新),Vista Home Premium 32位,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 32位系统和ATI Radeon 9800 Pro的情况。
谢谢任何评论/解决方案
Phil
PS:使用Photoshop CS3不会出现此错误。

1
疼,很可能是注册表设置的问题...尝试在源代码中搜索Registry,看看“保存/加载”实现在哪里,然后制定一个解决方案... - user497849
当你说你的ActionMainMenuBar“失去了所有设置”时,你是指属性设置、图像、事件处理程序还是其他什么? - user496736
它正在失去其属性设置 - 在这种情况下,是在颜色映射中定义的颜色和字体设置。 - Phil
Dorin,注册表中没有任何内容被写入。 - Phil
当更改Windows视觉样式时(已在Windows 7上测试),这种行为似乎也会发生。例如,在Delphi IDE中,如果您将Colormap分配给TActionMainMenuBar,然后更改Windows Visual Style,Colormap将恢复为.DefaultColorMap。 - user1175743
2个回答

8
这里有另一种重现问题的方法:
  • 按照问题中的步骤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, Software\Microsoft\Internet Explorer\SearchScopes

我不知道IE每次启动时向我们的表单(以及所有其他顶级窗口)说了什么,也不知道它是在地球上的每台Windows计算机上都这样做还是只有你和我,但显然ActionMainMenuBar无法处理它。
一个窗口控件(表单)接收到WM_WININICHANGE后,执行CM_WININICHANGE,并在接收到广播后将其广播给其所有控件。下面是菜单栏如何处理它的方式:
procedure TCustomActionMainMenuBar.CMWininichange(var Message: TWMWinIniChange);
begin
  inherited;
  RequestAlign;
  Font.Assign(Screen.MenuFont);
end;

考虑到系统菜单字体可能已更改(代码应该查找消息中的“WindowsThemeElement”或“WindowMetrics”部分,但无论如何..),它被重新分配为刷新的Screen.MenuFont。问题是,我们并没有真正使用它。

此外,ColorMap通过调用其UpdateColors方法响应CM_WININICHANGE来重置其颜色。这甚至有文档记录

当ActionBand组件接收到CM_WININICHANGE消息时,会自动调用UpdateColors。


因此,解决方案涉及决定要做什么并覆盖两种行为。我尝试对以下解决方案进行评论,以说明我认为这将是正确的解决方案的原因:

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;

1
谢谢@Ken。据我所知,这不会被广泛使用,因为ColorMap不是一个广泛使用的控件。但这很有趣 :)。 - Sertac Akyuz
抱歉,我又来了(我错过了编辑上面评论的5分钟时间窗口)。颜色设置并没有从颜色映射中消失,但是TActionMainMenuBar的颜色没有恢复。 - Phil
重新分配颜色属性似乎不是解决方案,我刚刚尝试过了。 - Phil
1
@Phil - 不要理会之前的评论,看一下 'ActnColorMaps.pas' 的源代码,每当一个颜色映射接收到 'CM_WININICHANGE' 时,都会调用 UpdateColors。它应该被称为 'ResetColors' 才对。无论如何,我认为正确的解决方案是派生你的颜色映射并覆盖 UpdateColors - Sertac Akyuz
@Phil - 我真的怀疑更新会解决这个问题... 是的,如果我没记错的话,动作绑带遵守样式。我已经更新了答案。看看是否有帮助。 - Sertac Akyuz
显示剩余7条评论

4

找到了!问题在于每次启动/关闭IE9或Photoshop CS5时,ActionMainMenuBar1的Colormap属性都会重置为ActionMainMenuBar1.DefaultColormap。我将接受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.

再次感谢大家的帮助。

很高兴你解决了问题 :) 谢谢你发布解决方案。 - Sertac Akyuz
我最终成功解决了IE9和Photoshop CS5的绘图问题。请查看上面更新后的代码。谢谢。 - Phil
现在GetControlClass不必要了吗? - Sertac Akyuz
GetControlClass是解决IE9绘制问题的必要方法。将代码添加到WMSettingChange过程中,可以修复Photoshop CS5的问题以及字体的问题。 - Phil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接