user1175743
user1175743

Reputation:

How to know if I am referencing to the correct component?

Overview

I am overriding some procedures that inherit from TXPStyleMenuItem, this is so I can change the appearance of how the menus appear when using a TActionMainMenuBar.

Example:

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

I have created my own component which is derived from the TActionMainMenuBar, and in the GetControlClass method I have set the var ControlClass: TCustomActionControlClass to TMyXPStyleMenuItem.

What I have so far is working well, though obviously is more complete then the above example - I kept it as short and simple as possible for the purpose of the question.

Styler Component

I have created a non-visual styler component which is basically nothing more than a bunch of published properties, this styler component can be assigned to some of my controls, in this case my descended TActionMainMenuBar.

When my controls has the styler component assigned, it reads the values of the properties and then paints based on the values. If the styler component is not assigned then I simply use defaults to decide how the controls are painted.

A quick example is from one of my custom TEdits, I can change the color depending on the assigned styler value:

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;

Problem

The issue I am facing though is with the overrided procedures in TMyXPStyleMenuItem = class(TXPStyleMenuItem).

I need to read the values of my styler component, but because TMyXPStyleMenuItem is not actually part of my menu component I have no way of knowing where FStyler came from, eg which instance of my TActionMainMenuBar it was assigned to?

Typically FStyler is set like so from my menu class:

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;

but those procedures from TMyXPStyleMenuItem do not know about the above.

I cannot include a new parameter to the overidden procedures because it wont work as the declaration differs from the base class, example:

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

Almost had it

The closest I did get was in the unit of my menu component I created a global variable above the implementation part:

var
  FStyler: TMyStyler;

implementation

then in the overidded DrawBackground procedure I did this:

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;

Just as I thought I had it working, I soon discovered it is not. As soon as I have a secondary menu, or a menu on another form it gets confused as to which menu should be painted using which stylers values. This is because in theory any instance of my menu component is going to be using the global FStyler variable.

Another thing I thought I could do was mark the procedures with the overload directive and that would let me add my extra parameter, but then I still don't know if I am referencing the correct menu component.

As I am basically still getting familiar with component writing and such, forgive me if I am overlooking something completely obvious. If it is simple, then I have completely missed it - this is got me quite a bit confused now if I am honest!

Upvotes: 3

Views: 174

Answers (2)

user1175743
user1175743

Reputation:

I ran into problems using jachguate's answer where the following line in the GetStyler method was never triggered:

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

This obviously caused problems with the rest of the code as my custom derived menu was never found.

I did a little testing and debugging with the Menu property as pointed out in jachguate's answer, to determine the classname to see if it was TMyActionMainMenuBar, but it was not - it was TXPStylePopupMenu instead.

So I needed to find out how I could get the class to be TMyActionMainMenuBar.

I found the solution by trying a few different things, here is what I found:

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

The key to it was Menu.RootMenu, which tells us the custom menu bar been used.

Upvotes: 3

jachguate
jachguate

Reputation: 17203

use the Menu property (declared in TCustomMenuItem, which is the grand-parent of your class) to get access to the menu styler, something like this:

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;

You can refactor your code to add a GetMenuStyler method:

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;

Upvotes: 5

Related Questions