Reputation:
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
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
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