1. Tech

Your suggestion is on its way!

An email with a link to:

http://delphi.about.com/od/oopindelphi/l/aa082603a.htm

was emailed to:

Thanks for sharing About.com with others!

Accessing protected members of a component
Many Delphi components have useful properties and methods that are marked invisible ("protected") to a Delphi developer. In this article you will find the workaround to this problem - thus enabling you to access a DBGrid's RowHeights property, for example.
 Win prizes by sharing code!
Do you have some Delphi code you want to share? Are you interested in winning a prize for your work?
Delphi Programming Quickies Contest
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• Enhanced DBNavigator
• DBGrid + OnClick event

• Writing Delphi components
• OOP in Delphi

Have you ever caught yourself in talking to yourself? A discussion like the one below perhaps?
"Hmm, that DBNavigator is really cool, but if I could only lay my hands on the buttons and those pictures it displays?", or "I could really use the OnClick event for the DBGrid, but why is it not accessible in the Object Inspector?!", or "If I could just change the height of that title row in the DBGrid".

A little OOP story...
As you probably know (and those who do not will learn now), in Delphi OOP the protected data of a class is accessible to any method that appears in the same unit as the class.
In Delphi class declaration, every member of a class has an attribute called visibility, which is indicated by one of the reserved words private, protected, public, published, or automated. If you are not familiar with the meaning of those keywords, please read at least some of the pages of the "Creating Custom Delphi Components - Inside and Out" article.

Recall the self-dialog above, what if you really need to get your hands on the Height property of the first row (where titles are displayed) of a DBGrid component.

In this article, you'll find out about a technique sometimes called the "Protected Hack" - deriving (subclassing) a component from an existing Delphi component with the only purpose or exposing the protected properties and methods.

Note: Another workaround to this "problem" is to create a new component derived from the component you want to hack and make the properties either public or published. This would end up in installing the new component on the Palette so you can access it from everywhere. However, many times you'll only need to expose only one property or method (presumably limited to a single unit in your code) - why bother with creating a whole new component?

"Protected Hack"
The general technique explained here can be used to surface hidden (that is, "protected" in Delphi OOP jargon) properties and methods for Delphi classes (components). If you know that an ancestor could do something (handle an event, access a property, execute a method) this is how to make the descendant do it:
  • Let's say we need to hack a DBGrid. What we want to access is the RowHeights property.
  • Make sure you are editing the unit where the DBGrid is declared (for example: DBGrid1 dropped on Form1, so we are editing the Unit1.pas)
  • Create a noticeably worthless derived class:
    THackDBGrid = class(TDBGrid);
  • Apparently, the class THackDBGrid derived like this does not bring any new functionality to DBGrid.
  • Though, it does provide the *free* access to all the protected members of the DBGrid component.
  • To use once protected members, you must typecast the component you refer too to the new (descendant) class.

Now, let's see some Delphi code. Assume you have a Delphi form (Form1) with a DBGrid (DBGrid1) displaying some data. To access the protected RowHeights property of that DBGrid you first hact the DBGrid comonent. Here's the portion of the Unit1.pas unit - where Form1 is declared:

uses
  Windows, Messages, ..., Grids, DBGrids, ... ;

type THackDBGrid = class(TDBGrid);

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
....

Note the THackDBGrid declaration! Now, to change the height of the title row of the DBGrid1 you simply typecast DBGrid1 to THackDBGrid like:

  //makes the Title's row 20 px in height (if displayed)
  THackDBGrid(DBGrid1).RowHeights[0] := 20;

Ok, that's it. RowHeights?! Yes, the RowHeights property of a TCustomGrid (the TDBGrid derives from) gives the height (in pixels) of all rows in the grid. In TDBGrid this property is protected - in THackDBGrid it is editable!

Note that the next assignemet gives the compile time error: "Undeclared identifier: RowHeights".

DBGrid1.RowHeights[0]:=20;

It's working! ... Howcome?
Yep, it is. This is why: the THackDBGrid automatically inherits all the protected members of the TDBGrid (and all public, private, etc. of course), due to the fact that THackDBGrid is in the same unit as the code that accesses the protected property, the protected property is available.

Note: do not think that you can define the THackDBGrid in another unit - the code will no longer compile.

Another approach - no type-casting.
If you need to access the protected members of a component but do not want to use type casting, you can declare the new (hacked) class with the same name as the old class:

type TDBGrid = class(DBGrids.TDBGrid);

Now, when accessing any protected member, you do not need to use the THackDBGrid(DBGrid1).Something syntax; Delphi will use the modified component as if it were the old one. Therefore a line like DBGrid1.RowHeights[0]:=20; will compile.

Warning!
This is a hack - a very bad OOP technique. There are reasons that components do not expose certain members of their class declaration. Use the technique explained in this article at your own risk.
Note: "a hack" - means that it breaks standard OOP practices. This "hack" works (and will work in future Delphi version) because it relies on a specific Delphi Pascal OOP feature.

©2014 About.com. All rights reserved.