Monday, January 12, 2015

Now Available: Delphi in Depth: ClientDataSets 2nd Edition

I published the original Delphi in Depth: ClientDataSets book back in March of 2011. At the time I believe it was my 24th book on computer software, though I must admit that I've lost count. Nonetheless, I greatly appreciated the many kind comments I've received from readers since its publication, even though it is a Delphi book "about one component."

I decided to update the code samples following the release of Delphi XE7. My original code samples made use of the Borland Database Engine and the sample Paradox tables that shipped with Delphi, which permitted those code samples to be compiled and run without any additional configuration, something that I felt was important.

Things changed with the release of Delphi XE7. The BDE is no longer installed by default. In addition, Delphi Professional (and higher) now ships with an alternative database solution that can be used without any additional configuration. This solution, which makes use of FireDAC and InterBase, did not exist when the first edition of this book was published.

Once I started updating the code samples to use FireDAC and InterBase, I realized that there were also a few additions that I could make to the original book. For example, the new FieldOptions property introduced to TDataSet in Delphi XE6 gave developers additional flexibility in their use of persistent fields, such as calculated and internal calc fields. A nod to LiveBindings would also be warranted.

It didn't take long before I concluded that it was time to give the book a good once over, tweaking the odd description here and there, adding material where appropriate, and correcting a few missed typos. And, of course, using the new FireDAC/InterBase examples in the book's code listings. I also updated all of the figures in the book to show the latest version of Delphi at the time of my writing (XE7). The original BDE/Paradox examples are still available for download, which is necessary in order to support older versions of Delphi, those prior to XE5. XE5 was the first version of FireDAC to use the new FD prefix in component names, as well as the new FireDAC unit names.

Permit me now to anticipate a question that is sure to come up. "What if I bought the original edition of Delphi in Depth: ClientDataSets? Should I buy the 2nd edition?" Well, you probably don't need to buy the 2nd edition unless you want to have the most current version on your bookshelf. In updating the 2nd edition I re-read, for the purpose of correcting and editing, every page of the original manuscript many times. And, for the most part, I was very happy with what I had written. As a result, there are large segments from the original text that are unchanged, or have inconsequential changes in the wording. Yes, I did add sections on FieldOptions, LiveBindings, and special filter operations. There are also a few additional examples and extended discussions.

I should also note that you can use the new FireDAC/InterBase samples with the original book. Visit the book's download site, listed in Appendix A of the original publication. There you will find the FireDAC/InterBase code sample download, along side the BDE/Paradox code download. Again, these FireDAC examples only work with Delphi XE5 and later, so the BDE-based examples are still available for use with earlier versions of Delphi.

I should also note that I removed three chapters that appeared at the end of the original book from the 2nd edition. Those chapters covered DataSnap, with a mix of COM-based DataSnap and the newer DataSnap introduced in Delphi 2009. While ClientDataSets played a central role in the COM-based DataSnap (the obsolete version), they are one of many options in the new DataSnap. In the end, I concluded that the DataSnap chapters were out of place.

I am releasing two versions of Delphi in Depth: ClientDataSets 2nd Edition. The printed version is available on CreateSpace and Amazon. And, in response to popular request, I am releasing a PDF version on FastSpring. You can find links to these releases from the book's web site at http://www.JensenDataSystems/cdsbook2.

I am very happy with how Delphi in Depth: ClientDataSets 2nd Edition turned out. I hope that you are too.

Publication Date: Jan 07 2015
ISBN/EAN13: 150584018X / 9781505840186
Page Count: 358 pages, 7.44" x 9.69"

Web Site: http://www.JensenDataSystems/cdsbook2

Tuesday, January 6, 2015

Delphi Developer Days 2015 Agenda Announced

Ray Konopka and I worked over the holidays to prepare the agenda for Delphi Developer Days 2015, and I am very happy with the content we have planned for this year's tour.

As usual, we have created a balanced program that offers something for every Delphi developer. We have sessions covering the VCL (Visual Component Library) as well as sessions on mobile/FireMonkey development. Some sessions cover some of Delphi's newest features, while others apply to nearly every version of Delphi. In the database realm, we have client/server sessions as well as sessions covering distributed (DataSnap and REST) topics. We even have sessions covering some of the third party tools that ships with Delphi, including CodeSite and Fast Report.

Ray Konopka is the world's leading expert on Delphi component creation, and is also an expert on user interface design, and we've ensured that these topics are represented in the agenda. Best of all, all of the topics that I've mentioned will be covered in the Delphi Developer Days book that all attendees receive. Ray and I are now turning our attention to writing that book, and it is a creative process to which I am looking forward.

Delphi Developer Days 2015 is visiting four cities in the United States, Europe, and Scandinavia between 13 April and 11 May. And, we are currently in our very early registration period, and you will save even more if you register and make payment by 23 January, 2015.

For complete information on Delphi Developer Days 2015 locations, the agenda, session descriptions, and discounts, please visit http://www.DelphiDeveloperDays.com

Space is limited. Register today to save your seat.

Thursday, December 18, 2014

Delphi Developer Days 2015 with Ray Konopka and Cary Jensen

In case you missed the official press release on Tuesday, let me be the first to share with you that Ray Konopka is going to join me in presenting this year's Delphi Developer Days 2015 tour. We will be visiting four cities in the United States, Europe, and Scandinavia this spring, and it is promising to be one of the best Delphi Developer Days tours so far.

Before I go any further, I want to assure you that my co-presenter for the past two years, Bob (Dr.Bob) Swart is still part of Delphi Developer Days. Bob is taking a sabbatical from traveling in the first part of 2015, and his humor and profound knowledge of Delphi will be sorely missed. But he will be back next year, and I am certain that he will have a wealth of new information to share at that time.

In the meantime, I have the supreme pleasure to have another of the world's great Delphi gurus joining me for this annual seminar tour. Ray Konopka is an Embarcadero MVP, winner of the coveted Spirit of Delphi award, and owner and founder of Raize Software, one of the first providers of third-party Delphi components. Another of his products, CodeSite Express, ships with Delphi.

I first met Ray when we served together on the annual Borland International Conference advisory board. That was more than 20 years ago. And I have to say that he is one of the hardest working and most focused people I've ever met. More importantly, he is an articulate speaker with a gift for communication. Whatever the topic, he understands the essential points, and presents them with clarity. You can't ask for more from a presenter.

But there's more, and it's particularly important when it comes to Delphi Developer Days. As everyone who has attended in the past knows, one of the hallmarks of Delphi Developer Days is the book that every attendee receives. Packed with hundreds of packages of material, it ensures that you take home more than an experience — you take home information.

This is why Delphi Developer Days presenters also have to be skilled writers. My first Delphi Developer Days co-presenter, Marco Cant├╣, is an excellent example of this. Marco has published more books on Delphi than anyone else, and I don't know anyone working with Delphi who doesn't own several of them. Bob, likewise, has published a number of books, countless tutorials, and many magazine articles. Who hasn't found the answer to some impenetrable problem from Bob's posts? I know I have.

Ray, likewise, is an author. He wrote the Blazing Pascal column for PC Techniques, and the column Delphi by Design for Visual Developer Magazine. Furthermore, his book, Developing Custom Delphi Components was groundbreaking (as was the follow-up, Developing Customer Delphi 3 Components), and it deserves a place on every Delphi developer's shelf.

Ray and I are still working on the agenda for this year's topics, but I can assure you that there will be lots of great information. I will blog and tweet again once we've posted the topics. In the meantime, you can read the press release at http://www.DelphiDeveloperDays.com/press_release.html.

For more information, visit http://www.DelphiDeveloperDays.com. And, I'd be remiss if I didn't mention that we're currently in the very early registration period, and you can save 25% if you sign up soon.

Let me finish by now returning to my main point. This year's Delphi Developer Days tour will be one to remember. Ray and I are looking forward to it, and I hope you are, too.

Thursday, June 19, 2014

Ode to Code

Ode to Code:
Reflections on Software Development in Iambic Heptameter
by Cary Jensen

While true, it's said, there's work to do, and now it's time to start
This is my task, so plain and clear, it's science, love, and art

My slate starts clean, a screen pure white, no logic bears my will
Upon this page, through thought and might, it's my intent to fill

I make my plan, each step designed, my spec is well thought out
It matters much, oh yes it does, there can't be any doubt

My fingers fly, the logic pours, a waterfall sublime
I see my goal, I type and click, I lose all sense of time

My words are key, my syntax right, and subtly reserved
I own this world, I'm in control, though humble, numb, and nerved

It's getting close, the tension firms, anticipation's high
I hit F9, compile and run, I feel success is nigh

But wait! It fails! How can this be, catastrophe I feel
I've lost my touch, my senses fail, my mortal soul revealed

Oh, damn the Gods! This logic broke, a bug I cannot find
I check the source. I check each line. I think I'll lose my mind

But what is this, a misplaced test, can this thing truly be
Compile again, and it just works, I'm now in ecstasy

I document the change I made, my comment explains why
I always try to take this step, to prevent a future cry

Relaxed, I am, my goal achieved, a conquest satisfied
It's what I do, a job compelled, a source of joy and pride

You beauty mine, my cherished child, the product of my soul
From white blank slate to final code, a void has morphed to whole

No eyes will see this gold I've spun, no heart with feel its beat
It lies beneath the interface, resplendent and complete

I scratch one item off my list, but I cannot pretend
With this task done, there will be more, my work will never end;

Copyright © 2014 Cary Jensen. All Rights Reserved

Tuesday, May 13, 2014

Simplifying LiveBindings Configuration in Mobile Apps Using Frames

Unless you have been sleeping under a rock, you already know that Delphi can create native executables for both iOS and Android mobile devices. These applications must be created using FireMonkey, since FireMonkey is Delphi's cross-platform component library. When using FireMonkey, most developers make use of LiveBindings and the LiveBindings Designer to enable data awareness in the visual controls.

Unfortunately, the evolving best practices for mobile application design in Delphi has relied on applications based on a single form, where a TabControl containing two or more TabItems defines the various screens (pages) of the application.

This design avoids some of the issues associated with having multiple forms in a mobile application. For example, Android applications do not support the concept of non-modal forms, and although iOS does, the active form always occupies the entire screen.

Another issue related to having more than one form in a modal application is related to mobile device orientation. Specifically, many applications will re-orient themselves when a user rotates their device 90 degrees, switching from a portrait view to a landscape view and back. The problem here is that the OnResize event is received only by the form with focus. No other forms in memory receive this event, and therefore may not be aware that orientation has changed.

If a mobile application needs two or more pages (or views or forms or whatever you want to call them), this can be achieved by including a single form on which a TabControl appears. You then create one TabItem for each page of the application, and provide user interface elements such as menus, tabs, or buttons to navigate between the available TabItems. In fact, this is the model used by all of Delphi's multi-page wizards in the Object Repository, which you can select from when you select File | New | FireMonkey Mobile Application from Delphi's main menu.

While the TabControl/TabItem configuration works, it introduces an unwanted side effect. Specifically, if you use the LiveBindings Designer to visually bind your controls to BindSourceDB components, every control appears on the default layer, and the designer quickly becomes cluttered, making it difficult to create and manage your LiveBindings. And even though the LiveBindings Designer permits you to define layers on which to display select controls, the clutter remains a challenging issue. And, the clutter increases in direct proportion with the overall number of controls on your one form. This can be seen in the following view of the LiveBindings Designer for a somewhat simple two TabItem TabControl.

 
Unfortunately, a two-page mobile application is uncommon. Most mobile applications will have many more pages (TabItems), and the problem of clutter in the LiveBindings Designer can get completely out of hand.

There is another solution, however, and it can be found in FireMonkey frames, which were added to FireMonkey in Delphi XE3 (the VCL introduced frames in Delphi 5, and I have found them to be invaluable in my Windows applications).

Here's the deal. A frame is a design surface, and each design surface in Delphi has its own LiveBindings Designer. As a result, only those controls that appear on the frame automatically appear when you view the frame's LiveBindings Designer.

Here is what you can do. In the simplest case, you create one frame for each TabItem that you will include in your single form application. You then place each frame into its corresponding TabItem, and then align the frame to alClient. If you want to get really clever, you might even place two or more frames into a single TabItem, but that is a design issue.

For each frame, you include in its uses clause only those data modules whose TDataSets you want to bind using LiveBindings. The result is that each LiveBindings Designer includes only a subset of controls that appear in the single form. This can be seen in the following view, which shows a frame that replaces the controls that appear on the first TabItem of the form whose LiveBinding Designer was shown earlier in this post.

 
The problem is not entirely solved. Specifically, the one form of your application on which the TabControl appears will display all of the objects from all of the frames placed on the individual TabItems within the LiveBindings Designer when the form itself is selected in the designer, so the clutter still exists. But you will not really care. As long as you can configure LiveBindings at the Frame level, you will use the LiveBindings Designer only with individual frames, in which case only the components on the selected frame will be visible. Furthermore, the benefits of this frame-by-frame configuration will increase in direct proportion to the number of TabItems on your form.

This is just one of the tips, along with many other fun and useful Delphi techniques, that I will be covering along with Bob Swart (Dr.Bob) at this years Delphi Developer Days 2014 tour. We have completed our first two cities in the United States, and the response was excellent. We now continue on to Frankfurt, Germany on May 26th and 27, Amsterdam, The Netherlands on June 12th and 13th, and London, Great Britain on June 18th and 19th. For more information and to register, please visit http://wwwDelphiDeveloperDays.com.

Copyright (2014) Cary Jensen. All Rights Reserved

Tuesday, February 11, 2014

Delphi Developer Days 2014

It is again my pleasure to announce the dates and cities for our annual Delphi Developer Days tour by me and Delphi expert Bob (Dr.Bob) Swart. We will be visiting two cities in the United States and three in Europe/United Kingdom. We will be in the Washington DC/Baltimore area on May 5th and 6th and Chicago on May 8th and 9th.

We continue in Frankfurt, Germany on May 26th and 27th, continuing to Amsterdam, The Netherlands on June 12th and 13th. We have once again added London to our spring tour, and we will be there June 18th and 19th.

If you are not familiar with Delphi Developer Days, it is an intense, two-day Delphi-focused event, where we cover some of Delphi's latest features, as well as topics that apply to many versions of Delphi. Bob and I present some of the more general topics together in joint sessions, where we share our knowledge and personal perspectives. We also include a number of breakout sessions, where Bob and I go to separate rooms to present different topics.

I am very pleased with this year's agenda. Our joint sessions include an overview of mobile development with Delphi, where we will talk about both iOS and Android development, device setup, app deployment, and Delphi's next generation compiler. In another joint session we review some of the more advanced language features that have been added to Delphi since Delphi 2009.

We also have a joint session on data awareness in cross-platform applications. In this session we discuss LiveBindings, as well as alternative techniques for presenting data to your users. And Delphi Developer Days would not be complete without our Tips, Tricks, and Techniques session. In this joint session Bob and I share a collection of powerful, and sometimes odd features of Delphi that you can use to extend your applications and improve your development experience.

We also have a wide variety of separate sessions. In these sessions we cover FireDAC, Delphi's new data access framework, REST servers and clients, debugging and development support techniques, including unit testing, and an extensive and in-depth survey of object-oriented programming in Delphi. Other separate sessions cover advanced mobile development, a comparison of Delphi's target platforms and how these operating systems differ, as well as a session on when and where to place your data entry validation and business rules in database applications.

For a complete list of sessions, view our agenda  at http://www.delphideveloperdays.com/descriptions.html. On this page you will also find a link to view a more detailed description of each session.

It's a lot of material in two days, and due to the separate sessions, you won't be able to physically attend every session. That's why Bob and I document every session we cover (with the obvious exception of the question and answer sessions that we hold at the end of each day). These limited edition course books, available only to Delphi Developer Days attendees, are extensive and detailed, typically weighing in at 300-400 pages. This year is no different. I have set aside a significant amount of time over the next three months for my writing, and I know Bob is doing the same. Regardless of which sessions you attend, you'll get all of the material we cover, include the many code samples we use in our demonstrations.

I also want to mention that we are particular about where we hold our events. All of our venues are close to airports and/or train stations. For example, both of the hotels in the US have free shuttles from the nearby airports. These hotels also have free parking if you arrive by car. Our Frankfurt hotel is less than 2 kilometers from Frankfurt's main train station, from where you can hire a taxi or even catch a bus that stops outside the hotel. Alternatively, the hotel is a 30 euro taxi ride from Frankfurt Airport, one of the largest airports in Europe. There is also free parking for attendees on the days of the event.

In Amsterdam, we are at the historic Victoria Park Plaza, which is directly across the street from Amsterdam's Central Station, which is a short train ride from Amsterdam's Schiphol Airport, also one of the largest in Europe. Finally, in London we are at the Park Inn London Heathrow. Fly into Heathrow and you can catch a free bus that drops you off a short walk from the hotel. There is also free parking for attendees on the days of the event.

Attendance in each city is limit to 42 developers, which permits sufficient networking opportunities while keeping the experience intimate. As a result, however, we have sold out most cities over the past years, so you should consider registering early to ensure that you reserve a seat in the city of your choice. We are also offering discounts for early registration, and if you've attended Delphi Developer Days since 2009, there are additional discounts. There are also discounts when your company sends three or more of their developers.

Delphi Developer Days History

Delphi Developer Days in a long-time Delphi touring event focusing exclusively on Delphi development. Cary Jensen, who authored and was the featured speaker on the original Delphi World Tours (1995 through 1999), started Delphi Developer Days in the fall of 2001. From 2009 through 2012, Marco Cant├╣ of Wintech Italia, Srl and Cary joined forces to co-offer Delphi Developer Days in Europe and in the United States. When Marco was hired to be the Delphi product manager, noted Delphi expert Bob Swart of Bob Swart Training & Consultancy (eBob42) teamed up with Cary to keep the Delphi Developer Days tradition alive.
For more information, and to register, visit www.DelphiDeveloperDays.com.

Thursday, January 9, 2014

Two Approaches to Sub-classing Components Compared

Delphi is an object oriented programming language. Unless you write nothing but console applications, this fact is obvious. For example, when your application includes either a form or a data module, the class that defines the form or data module is a descendent of an existing class. In the case of a form, it is a TForm descendant, and in the case of a data module it is a TDataModule descendant.

This process of extending an existing class, especially one that is not TObject, is used extensively by the visual component library (VCL), and to a lesser extent in the runtime library (RTL). Importantly, it is a technique that you can use to create your own custom classes, ones that inherit the power of an existing class, and which extend that class to add additional features. These features might include new properties, additional methods, or alternative behaviors for methods inherited from the ancestor class.

Overall, the VCL is a remarkable and rich component library. Nonetheless, it is not uncommon, especially with seasoned developers, to want to extend existing classes of the VCL or RTL to add custom capabilities.

In this post I am going to discuss two different, though not entirely dissimilar, techniques for creating a new class based on an existing VCL component. In most cases, these techniques can also be used to extend any component, whether created by you or your development team or a third party. Towards the end of this post I will compare these two techniques by discussing the particular strengths of each approach.

The Traditional Method

Most developers who sub-class components of the VCL do so by declaring that class as a descendant of the existing component, followed by compiling that class into a runtime package which they install in Delphi. I am going to demonstrate this technique by creating a component that extends the TDBGrid class, adding a handy feature for reading data from it's underlying TDataSet.

This sub-classed grid exposes a GetField method, which returns a TField associated with the current record of the data set being displayed by the grid based on the name of the underlying dataset field. This new class also includes an InitializeDictionary method which must be called at least once after the grid has been associated with a dataset, but before the first call to GetField.

Here is the declaration of this new class, named TEasyReaderDBGrid:

type
  TEasyReaderDBGrid = class(Vcl.DBGrids.TDBGrid)
  private
    { Private declarations }
    FDict: TDictionary< string, TField>;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure InitializeDictionary(DataSet: TDataSet);
    /// 
    /// You must call InitializeDictionary each time you assign the
    /// DataSet property of the grid before you can use GetField
    /// 
    function GetField(const Name: string): TField;
  end;

This class is not very complicated. It uses a generic TDictionary (declared in System.Generics.Collections) to implement the GetField method. This can be seen in the implementation of this class, shown here:
{ TDBGrid }
constructor TEasyReaderDBGrid.Create(AOwner: TComponent);
begin
  inherited;
  FDict := TDictionary< string, TField>.Create;
end;
destructor TEasyReaderDBGrid.Destroy;
begin
  FDict.Free;
  inherited;
end;
function TEasyReaderDBGrid.GetField(const Name: string): TField;
begin
  Result := FDict.Items[Name];
end;
procedure TEasyReaderDBGrid.InitializeDictionary(DataSet: TDataSet);
var
  Field: TField;
begin
  for Field in DataSet.Fields do
    FDict.Add(Field.FieldName, Field);
end;
All we need to do to make this component available on the component palette is to create a design time package, add this component's unit to the package, make a call to RegisterComponents from a Register procedure, and then install this new package.

I've added the forward declaration and implementation of the Register method to the same unit in which the TEasyReaderDBGrid class is declared, adding this new component to the Samples page of the Tool Palette. Finally, here is the source of the design time package:

package EasyReaderDBGrid;

{$R *.res}
{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION OFF}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES ON}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DEFINE DEBUG}
{$ENDIF IMPLICITBUILDING}
{$DESCRIPTION 'Runtime package for TEasyReaderDBGrid'}
{$DESIGNONLY}
{$IMPLICITBUILD ON}

requires
  rtl,
  vcl,
  dbrtl,
  vcldb;

contains
  Traditionalu in 'Traditionalu.pas';

end.

Once we compile and install this new design time package, the new component, TEasyReaderDBGrid appears on the Samples page of the Tool Palette, as shown in Figure 1.

Figure 1. A traditionally sub-classed component appears on the Tool Palette

Figure 2 shows a VCL Forms application on which an instance of the TEasyReaderDBGrid class has been placed at design time. In this figure a call to GetField has been made from the OnClick event handler of a button, shown here:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(EasyReaderDBGrid1.GetField('CustNo').AsString);
end;


Figure 2. The GetField method of the TEasyReaderDBGrid component returns a TField associated the grid's TDataSet.

The Interceptor Method

Unlike the traditional method, which involves adding a component to the component palette, the interceptor method "intercepts" the name of the existing VCL component, mapping it to a different class. Consider the form shown in Figure 3.


Figure 3. A VCL Forms application using a class that intercepts TDBGrid

The form in Figure 3 looks and behaves like that shown in Figure 2. The difference is that the grid that appears in Figure 3 is not a TEasyReaderDBGrid. Instead, the grid is actually an instance of the TDBGrid class. This class, however, is intercepted and extended, giving the TDBGrid class the methods necessary to support the features of the TEasyReaderDBGrid class.
There are two general approaches to class interception. One is to intercept the class within the module where the extended class is used. This approach is shown in the following type declaration:

type
  TDBGrid = class(Vcl.DBGrids.TDBGrid)
  private
    { Private declarations }
    FDict: TDictionary< string, TField>;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure InitializeDictionary(DataSet: TDataSet);
    /// 
    ///   You must call InitializeDictionary each time you assign the
    ///   DataSet property of the grid before you can use GetField
    /// 
    function GetField(const Name: string): TField;
  end;
  TForm1 = class(TForm)
    ClientDataSet1: TClientDataSet;
    DataSource1: TDataSource;
    Button1: TButton;
    DBGrid1: TDBGrid;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  end;

Alternatively, the interceptor class can be declared in its own unit. In that case, all you need to do is ensure that the unit in which the interceptor class is declared appears later in the uses clause than the unit of the class that is being extended. Here is an example of how the interface section of an interceptor unit might look (the implementation is identical to that shown earlier for the TEasyReaderDBGrid class):

unit Interceptoru;

interface

uses System.Classes, Vcl.Grids, Vcl.DBGrids,
     System.Generics.Collections, Data.DB;

type
  TDBGrid = class(Vcl.DBGrids.TDBGrid)
  private
    { Private declarations }
    FDict: TDictionary< string, TField>;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure InitializeDictionary(DataSet: TDataSet);
    /// 
    ///   You must call InitializeDictionary each time you assign the
    ///   DataSet property of the grid before you can use GetField
    /// 
    function GetField(const Name: string): TField;
  end;

When using an interceptor unit, I generally find it necessary to document the placement of the interceptor unit in the uses clause, as shown here:

unit MainformIu;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils,
  System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids,
  Vcl.DBGrids, system.Generics.Collections, Data.DB,
  Vcl.StdCtrls,Datasnap.DBClient,
  Interceptoru;  //This unit must appear later in this
                 //uses clause than the Vcl.DBGrids unit

type
  TForm1 = class(TForm)
    ClientDataSet1: TClientDataSet;
    DataSource1: TDataSource;
    Button1: TButton;
    DBGrid1: TDBGrid;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  end;

The real difference in implementation between the traditional method and the interceptor method is that your code looks as though you are using the class that you sub-classed, as opposed to the sub-class itself. This can be seen in the following event handler, which is similar to the OnClick event handler for the button shown earlier in this article.

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(DBGrid1.GetField('CustNo').AsString);
end;

Comparing These Two Methods

While the end result of these two approaches is identical, there are significant differences. These differences make each of these mechanisms better suited for some uses over the other. I'll begin by considering the advantages of the traditional approach.

Advantage of the Traditional Approach

The primary advantage of the traditional approach is that your sub-classed component can appear in the Tool Palette. Having the component in the Tool Palette provides two benefits. First, any published properties that you add to your sub-classed component will appear in the Object Inspector at design time. Second, once you've placed the component onto your module from the Tool Palette, the unit in which your component is defined will be added to your interface section automatically the next time you save or compile your project.

Both of these benefits come down to convenience. Traditionally sub-classed components are easier to use.

Advantages of the Interceptor Approach

While the interceptor approach has the drawback that it is somewhat more complicated to use, it also introduces benefits that make it a powerful alternative. To begin with, creating a interceptor class takes less time. There is no need to create a design time package and a design time package does not need to be installed.

Not needing a design time package actually makes interceptor classes easier to share with a development team. Using the traditional approach, you need to provide each of the developers who will be working on the project with access to the package, which they will need to install into their copy of Delphi. When using an interceptor class, all you need to do is add the interceptor class unit into a directory on your library search path, and it will just work. Alternatively, you can make the interceptor unit a unit of your project, again making any classes defined in it immediately available to any unit that uses the interceptor unit.

Another advantage of an interceptor class is that it makes it remarkably easy to extend an existing class when you want to add only one or two new features. Furthermore, when you declare your interceptor class directly in the unit from which it will be used, you can customize each instance of the ancestor class for that module. For example, you might want to add an additional method to a TListBox interceptor on a given form. If you have a second form that also needs a modified TListBox, but with a different custom method, no problem. Create a different interceptor class for each form and you are done.

Finally, and what I think is absolutely the best advantage of interceptor classes is that it lets you make customizations to a component's sub-classes. For example, imagine that you want to add one or more custom properties to the individual menu items of a TMainMenu. This can be done very easily by creating an interceptor class for TMenuItem. After that, all of the menu items you add to your main menu will have those properties. Granted, you can only access those properties at runtime, but that is just a detail.

Here is a simple example of a TMenuItem interceptor type declaration that, if added to a unit prior to the TForm declaration, adds a MyInteger runtime property to any menu item appearing on that form.

type
  TMenuItem = class(Vcl.Menus.TMenuItem)
  strict private
    FMyInteger: Integer;
  public
    property MyInteger: Integer read FMyInteger write FMyInteger;
  end;

By comparison, if you use the traditional approach, you would have to actually sub-class TMainMenu and implement an extended version of TMenuItem from within the owner class.

Summary

Sub-classing existing classes of the VCL (or other libraries) is a powerful tool that every Delphi developer should take advantage of. In this article I have looked at the two primary mechanisms for sub-classing existing components. Each of these approaches have their strengths, making each best suited for some applications over the other.

Copyright (c) 2013 Cary Jensen. All Rights Reserved