3

概述

我重写了一些从 TXPStyleMenuItem 继承的过程,这样我可以在使用 TActionMainMenuBar 时更改菜单的外观。

例子:

  TMyXPStyleMenuItem = class(TXPStyleMenuItem)
  protected
    procedure DrawBackground(var PaintRect: TRect); override;
  end;

我创建了自己的组件,该组件派生自 TActionMainMenuBar,并在 GetControlClass 方法中将其设置var ControlClass: TCustomActionControlClass为 TMyXPStyleMenuItem。

到目前为止,我所拥有的一切运行良好,尽管显然比上面的例子更完整——为了问题的目的,我尽可能地简短和简单。

样式器组件

我创建了一个非可视样式器组件,它基本上只是一堆已发布的属性,这个样式器组件可以分配给我的一些控件,在本例中是我的下降 TActionMainMenuBar。

当我的控件分配了样式器组件时,它会读取属性的值,然后根据这些值进行绘制。如果未分配样式器组件,那么我只需使用默认值来决定控件的绘制方式。

一个简单的示例来自我的一个自定义 TEdit,我可以根据分配的样式器值更改颜色:

TMyEdit = class(TCustomEdit)
private
  FStyler: TMyStyler;
public
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;
end;

if Assigned(FStyler) then
begin
  Color := FStyler.Color;
end;

问题

我面临的问题是TMyXPStyleMenuItem = class(TXPStyleMenuItem).

我需要读取我的样式器组件的值,但是因为 TMyXPStyleMenuItem 实际上不是我的菜单组件的一部分,所以我无法知道从哪里来FStyler,例如它被分配给我的 TActionMainMenuBar 的哪个实例?

通常 FStyler 从我的菜单类中设置如下:

private
  FStyler: TMyStyler;
  procedure SetStyler(const Value: TMyStyler);
published
  property Styler: TMyStyler read FStyler write SetStyler;

..

procedure TMyMenu.SetStyler(const Value: TMyStyler);
begin
  if Value <> FStyler  then
  begin
    FStyler := Value;
  end;
end;

但是那些程序TMyXPStyleMenuItem从不知道上述内容。

我不能在覆盖的过程中包含一个新参数,因为它不会工作,因为声明与基类不同,例如:

procedure DrawBackground(var PaintRect: TRect; AStyler: TMyStyler); override;

几乎拥有它

我得到的最接近的是在我的菜单组件的单元中,我在实现部分上方创建了一个全局变量:

var
  FStyler: TMyStyler;

implementation

然后在 overidded DrawBackground 过程中我这样做了:

procedure TMyXPStyleMenuItem.DrawBackground(var PaintRect: TRect);

  procedure _DrawBackground(AStyler: TMyStyler);
  begin
    // handle all the painting from here
    // using the values from AStyler.
  end;

begin
  if Assigned(FStyler) then
    _DrawBackground(FStyler);
end;

就像我以为我可以正常工作一样,我很快发现它不是。一旦我有一个二级菜单,或者另一个表单上的菜单,它就会混淆应该使用哪些样式器值绘制哪个菜单。这是因为理论上我的菜单组件的任何实例都将使用全局 FStyler 变量。

我认为我可以做的另一件事是使用重载指令标记过程,这可以让我添加额外的参数,但是我仍然不知道我是否引用了正确的菜单组件。

因为我基本上仍然熟悉组件编写等,如果我忽略了一些完全明显的东西,请原谅我。如果它很简单,那么我完全错过了它 - 老实说,这让我现在有点困惑!

4

2 回答 2

5

使用该Menu属性(在 中声明TCustomMenuItem,它是您班级的祖父母)来访问菜单样式器,如下所示:

procedure TMyXPStyleMenuItem.DrawBackground(var PaintRect: TRect);
var
  Styler: TMyStyler;
begin
  if Menu is TMyActionMainMenuBar then
    Styler := TMyActionMainMenuBar(Menu).Styler
  else
    Styler := nil;
  if Assigned(Styler) then
    DrawStyledBackground
  else
    DrawDefaultBackground;
end;

您可以重构代码以添加 GetMenuStyler 方法:

function TMyXPStyleMenuItem.GetMenuStyler: TMyStyler;
begin
  if Menu is TMyActionMainMenuBar then
    Result := TMyActionMainMenuBar(Menu).Styler
  else
    Result := nil;
end;

procedure TMyXPStyleMenuItem.DrawBackground(var PaintRect: TRect);
var
  Styler: TMyStyler;
begin
  Styler := GetMenuStyler;
  if Assigned(Styler) then
    DrawStyledBackground
  else
    DrawDefaultBackground;
end;

procedure TMyXPStyleMenuItem.OtherDrawMethod(var PaintRect: TRect);
var
  Styler: TMyStyler;
begin
  Styler := GetMenuStyler;
  if Assigned(Styler) then
   ...;
end;
于 2013-02-21T22:01:30.227 回答
3

我在使用 jachguate 的答案时遇到了问题,其中GetStyler方法中的以下行从未被触发:

function TMyXPStyleMenuItem.GetMenuStyler: TMyStyler;
begin
  if Menu is TMyActionMainMenuBar then //< never hit
    Result := TMyActionMainMenuBar(Menu).Styler
  else
    Result := nil;
end;

这显然会导致其余代码出现问题,因为从未找到我的自定义派生菜单。

Menu正如 jachguate 的回答中所指出的,我对属性进行了一些测试和调试,以确定类名以查看它是否是TMyActionMainMenuBar,但它不是 - 它是TXPStylePopupMenu

所以我需要找出我怎样才能让这门课成为TMyActionMainMenuBar.

我通过尝试一些不同的事情找到了解决方案,这就是我发现的:

function TMyXPStyleMenuItem.GetStyler: TMyStyler;
begin
  if Menu.RootMenu is TMyActionMainMenuBar then
    Result := TMyActionMainMenuBar(Menu.RootMenu).Styler
  else
    Result := nil;
end;

它的关键是Menu.RootMenu,它告诉我们使用了自定义菜单栏。

于 2013-02-22T10:39:16.500 回答