How to Build Aggregate/Composite Components in Delphi

SuperComponents
How to Build Aggregate/Composite Components in Delphi™
 

Copyright © 1996, 1997 Mark Miller
Eagle Software
 

What are SuperComponents?
 

SuperComponents, also known as aggregate or compound components, are collections of existing sub-components and their relationships combined into a single component. The collections are typically arranged inside a container parent component that manages the visual layout of the sub-components.

 

Advantages
 

SuperComponents take all of the advantages of normal components and build on them. Rapid application development, object reuse, and user interface consistency are all benefits. Code shrinkage is another. If you have two or more forms containing similar component relationships (either inside an application or among several), that component relationship is tied to each form with - you guessed it - code. That binding Object Pascal code is duplicated with each form the component group lies on. By fusing the component collections into a single component, the code to manage the sub-component relationships disappears from the user interface code. The code for each form more truly represents its functionality; it's easier to read and maintain.

 

Another benefit achieved through the use of SuperComponents is multiple form inheritance. You can take parts of different forms (SuperComponents) and group them together into a new form. This leads to even faster prototyping and user interface design than normally experienced with Delphi.

 

SuperComponents can simplify the properties needed to control the collection. Instead of dealing with properties and events for each  component in the collection, the SuperComponent only exposes the properties and events it needs, thereby reducing the design-time complexity of the component.

 

As mentioned earlier, SuperComponents can embody the rules of the relationship among the subcomponents. This relationship might represent an algorithm or solution to a problem, or it might support state changes not present in any of the individual parts. Algorithm parameters and/or hooks to state changes can be easily exposed through new properties and events.

 

Finally, SuperComponents can be thought of as mini-apps. They are components themselves, and as such, lend themselves to a well-defined segregation from the rest of the project they are contained in. In team development environments, this means that developers most familiar with the problem and solution (whatever they be) can design the SuperComponents so that less-experienced developers can piece together the application with these building blocks.

 

 

Disadvantages
 

There are two disadvantages to making SuperComponents. First, visual SuperComponents require an additional Windows handle. Second, there is a small amount of overhead in the container parent component that holds all the sub-components.

 

Before We Start
 

It is useful to distinguish among the different roles developers and users take when dealing with components. There are three that we are concerned with:

 

•Application Users (a.k.a. "users") will use applications built with the components we make.

•Application Developers (a.k.a. "developers") will build applications using our components.

•Component Writers (that's us!) will create components that will make is easier for developers to build applications and easier for users to use. It's important to note that as a component writer, you're designing for two customers.

 

Visual Containment
 

The steps to building a SuperComponent are roughly as follows:

 

1.       Design the layout of your components inside a form in Delphi, placing all the components inside a TPanel (or a descendant thereof).

2.       Select and copy the panel and paste it into a text file.

3.       Replace all instances of " = " with " := ", and add a semi-colon to the end of each line.

4.       Convert all DFM "object" declaration lines to appropriate object constructor code, setting the parent of all visual controls to the container panel.

5.       Clean up any remaining code. Bitmaps will need to be placed in resource files.

6.       Place this new pascal code inside a create constructor for your component. Within the constructor , group object sections under the appropriate sub-component creator.

 

 

Let's use an example to illustrate these steps. We'll make an OK/Cancel/Help button combination. Inside Delphi, the layout looks like this (Note: the TPanel's border is set to none):

 


 

Selecting, copying and pasting the above collection into a text file yields the following:

 

 

object Panel1: TPanel

  Left = 114

  Top = 10

  Width = 75

  Height = 95

  BevelOuter = bvNone

  TabOrder = 0

  object OKButton: TButton

    Left = 0

    Top = 0

    Width = 75

    Height = 25

    Caption = 'OK'

    Default = True

    ModalResult = 1

    TabOrder = 0

  end

  object CancelButton: TButton

    Left = 0

    Top = 35

    Width = 75

    Height = 25

    Cancel = True

    Caption = 'Cancel'

    ModalResult = 2

    TabOrder = 1

  end

  object HelpButton: TButton

    Left = 0

    Top = 70

    Width = 75

    Height = 25

    Caption = 'Help'

    TabOrder = 2

  end

end

 

 

This is the text representation of our SuperComponent group. Next, we need to convert this text to something that looks a little more like Object Pascal:

 

object Panel1: TPanel

  Left := 114;

  Top := 10;

  Width := 75;

  Height := 95;

  BevelOuter := bvNone;

  TabOrder := 0;

  object OKButton: TButton

    Left := 0;

    Top := 0;

    Width := 75;

    Height := 25;

    Caption := 'OK';

    Default := True;

    ModalResult := 1;

    TabOrder := 0;

  end

  object CancelButton: TButton

    Left := 0;

    Top := 35;

    Width := 75;

    Height := 25;

    Cancel := True;

    Caption := 'Cancel';

    ModalResult := 2;

    TabOrder := 1;

  end

  object HelpButton: TButton

    Left := 0;

    Top := 70;

    Width := 75;

    Height := 25;

    Caption := 'Help';

    TabOrder := 2;

  end

end

 

 

Now we're getting closer to what we want. The next step is to transfer the panel initialization to the component's constructor. We'll create the embedded controls here, too:

 

constructor TOkCancelHelp.Create(AOwner: TComponent);

{ Creates an object of type TOkCancelHelp, and initializes properties. }

begin

  inherited Create(AOwner);

  Width := 75;

  Height := 95;

  BevelOuter := bvNone;

  TabOrder := 0;

 

  OKButton := TButton.Create(Self);

  OKButton.Parent := Self;

 

  CancelButton := TButton.Create(Self);

  CancelButton.Parent := Self;

 

  HelpButton := TButton.Create(Self);

  HelpButton.Parent := Self;

end;  { Create }

 

 

The three buttons, OKButton, CancelButton, and HelpButton need to be declared as fields of our new component. Our component's declaration looks like this:

 

type

  TOkCancelHelp = class(TPanel)

    OKButton: TButton;

    CancelButton: TButton;

    HelpButton: TButton;

  private

    { Private declarations }

  protected

    { Protected declarations }

  public

    { Public declarations }

    constructor Create(AOwner: TComponent); override;

  published

    { Published properties and events }

  end;  { TOkCancelHelp }

Now let's take that converted DFM text and initialize the three buttons. Although you can do this inside our component's constructor, some VCL sub-component initialization code depends on a windows handle existing in its parent. At the time when our new SuperComponent is created (inside the Create constructor), this handle does not yet exist. So we need to find a method we can override in the TPanel that is called before the component is displayed and before its loaded method is called, but after a windows handle is assigned to the TPanel. The CreateWindowHandle method is the best place to do this. An override of this method, with the DFM initialization code inserted, looks like this:

 

 

procedure TOkCancelHelp.CreateWindowHandle(const Params: TCreateParams);

{ Calls inherited CreateWindowHandle and initializes subcomponents. }

begin

  inherited CreateWindowHandle(Params);

 

  with OKButton do

  begin

    Left := 0;

    Top := 0;

    Width := 75;

    Height := 25;

    Caption := 'OK';

    Default := True;

    ModalResult := 1;

    TabOrder := 0;

  end;  { OKButton }

 

  with CancelButton do

  begin

    Left := 0;

    Top := 35;

    Width := 75;

    Height := 25;

    Cancel := True;

    Caption := 'Cancel';

    ModalResult := 2;

    TabOrder := 1;

  end;  { CancelButton }

 

  with HelpButton do

  begin

    Left := 0;

    Top := 70;

    Width := 75;

    Height := 25;

    Caption := 'Help';

    TabOrder := 2;

  end;  { HelpButton }

end;  { CreateWindowHandle }

 

 

And the component declaration now looks like this:

 

type

  TOkCancelHelp = class(TPanel)

    OKButton: TButton;

    CancelButton: TButton;

    HelpButton: TButton;

  private

    { Private declarations }

  protected

    { Protected declarations }

    procedure CreateWindowHandle(const Params: TCreateParams); override;

  public

    { Public declarations }

    constructor Create(AOwner: TComponent); override;

  published

    { Published properties and events }

  end;  { TOkCancelHelp }

 

 

Finally, we need to add a register method so we can place our new component onto Delphi's component palette:

 

 

procedure Register;

begin

  RegisterComponents('CDK', [TOkCancelHelp]);

end;  { Register }

 

Exposing Sub-Component Properties
 

Grouping components together to create new SuperComponents is a pretty neat trick. One advantage is that it allows you to isolate many of the grouped components' properties from application developers. In fact unless you explicitly state otherwise, all of the grouped components properties will be hidden from developers!

 

So how do you expose a sub-component property? You need to create two transfer methods that transfer the sub-component's properties to the outside world.

 

For example, in our TOkCancelHelp component, it might be useful to expose the caption property so application writers can change it (e.g., for application development in a language other than English). Our transfer methods look a lot like the standard property Get and Set methods we're already familiar with. Here's the declaration for the OK button's caption property:

 

 

type

  TOkCancelHelp = class(TPanel)

    .

    .

    .

  private

    { Private declarations }

    procedure SetCaption_OKButton(newValue: TCaption);

    function GetCaption_OKButton: TCaption;

    .

    .

    .

  published

    { Published properties and events }

    property Caption_OKButton: TCaption read GetCaption_OKButton write SetCaption_OKButton;

  end;

 

 

These transfer methods pass the property values to and from the subcomponents. Their implementation looks like this:

 

 

function TOkCancelHelp.GetCaption_OKButton: TCaption;

{ Returns the Caption property from the OKButton subcomponent. }

begin

  result := OKButton.Caption;

end;  { GetCaption_OKButton }

 

procedure TOkCancelHelp.SetCaption_OKButton(newValue: boolean);

{ Sets the OKButton subcomponent's Caption property to newValue. }

begin

  OKButton.Caption := newValue;

end;  { SetCaption_OKButton }

 

You may notice that there is no field variable for this property. All sub-component properties rely on the sub-components themselves for storage. Also notice that unlike most Set methods, this one doesn't check to see if the internal value is different from the passed-in newValue. We let the sub-component handle this check if necessary.

 

Exposing Sub-Component Events
 

Just as you would need to expose a sub-component's properties, you might also need to expose its events. The theory behind exposing sub-component events is similar to that used to expose properties. The difference is that with events, we need to store the event handler in a field variable (just like we would for a normal event). Additionally we have to hook in to any sub-componenent event that we want to make available to our component users, which means creating our own event handler for it. Event handlers must be assigned dynamically when the component is first created. The declaration for an exposed event and the event handler that surfaces it looks like this:

 

 

type

  TOkCancelHelp = class(TPanel)

    .

    .

    .

  private

    { Private declarations }

    FOnClick_OKButton: TNotifyEvent;

    .

    .

    .

    procedure Click_OKButtonTransfer(Sender: TObject);  { TNotifyEvent }

  published

    { Published properties and events }

    .

    .

    .

    property OnClick_OKButton: TNotifyEvent read FOnClick_OKButton write FOnClick_OKButton;

  end;

 

 

Here, Click_OKButtonTransfer acts as the event handler. Notice that its type, TNotifyEvent, matches the expected type for the OnClick event (TNotifyEvent). The implementation for the transfer method looks like this:

 

 

procedure TOkCancelHelp.Click_OKButtonTransfer(Sender: TObject);

{ Transfers the OKButton OnClick event to the outside world. }

begin

  if assigned(FOnClick_OKButton) then

    FOnClick_OKButton(Self);  { Substitute Self for subcomponent's Sender. }

end;  { Click_OKButtonTransfer }

 

 

 

 

 

If you've triggered events before, you probably recognize this code. The if-clause checks to see if the event is assigned (typically performed via the Object Inspector at design-time), and if so calls it, passing a reference to itself (the SuperComponent) to the component user's event handler. So, the sub-component's event is handled by our transfer method, which in turn passes the event to a component user's event handler (that's right -- two event handlers for each event!). To hook up this chain of events, all we do is dynamically assign the event transfer method to the sub-component's event. We do this in the overridden CreateWindowHandle method:

 

 

procedure TOkCancelHelp.CreateWindowHandle(const Params: TCreateParams);

{ Calls inherited CreateWindowHandle and initializes subcomponents. }

begin

  inherited CreateWindowHandle(Params);

 

  with OKButton do

  begin

    .

    .

    .

    OnClick := Click_OKButtonTransfer;

  end;  { OKButton }

  .

  .

  .

end;  { CreateWindowHandle }

 

Hooking in to Sub-Component Events
 

Sometimes you want to respond to a sub-component event without exposing it. The steps involved here are similar to those you'd follow to expose a sub-component event, except you don't declare the event (and you don't need the corresponding event field variable).

 

type

  TOkCancelHelp = class(TPanel)

    .

    .

    .

  private

    { Private declarations }

    .

    .

    .

    procedure Click_CancelButtonHandler(Sender: TObject);  { TNotifyEvent }

  published

    .

    .

    .

  end;

 

 

The handler looks like this:

 

 

procedure TOkCancelHelp.Click_CancelButtonHandler(Sender: TObject);

{ Handles the CancelButton OnClick event. }

begin

   { Place your event-handling code here. }

end;  { Click_CancelButtonHandler }

 

 

We glue it all together by dynamically assigning our handler to the sub-component event, just as we do when we want to expose a sub-component event:

 

procedure TOkCancelHelp.CreateWindowHandle(const Params: TCreateParams);

{ Calls inherited CreateWindowHandle and initializes subcomponents. }

begin

  inherited CreateWindowHandle(Params);

  .

  .

  .

  with CancelButton do

  begin

    .

    .

    .

    OnClick := Click_CancelButtonHandler;

  end;  { CancelButton }

  .

  .

  .

end;  { CreateWindowHandle }

 

Summary
 

SuperComponents promote consistency and reuse. They can embody commonly-used configurations of controls, dramatically cutting development time and slashing code size. And the techniques involved here are not difficult to master.

 

 

Recommended Reading
 

The following books were current at the time of this writing. Make sure you check for the most recent edition.

 

Developing Custom Delphi Components

by Ray Konopka; edited by Jeff Duntemann

Coriolis Group Books

(800) 410-0192 or (602) 483-0192

http://www.coriolis.com

585 pages

ISBN 1-883577-47-0

 

This book is highly recommended if you need additional information on component building or building business components.  It is filled with useful information and excellent examples.  The explanations are clear and easy to understand.  In addition, Ray Konopka’s TRzBusinessComponent, included with the CDK, is presented and detailed in his book.

 

 

 

Secrets of Delphi 2 -- Exposing Undocumented Features of Delphi

by Ray Lischner

Waite Group Press

831 pages

ISBN 1-57169-026-3

 

This is an amazing book, packed with valuable information you can’t get anywhere else. Also contains excellent coverage of property editors, component editors, and other advanced component-building topics.

 

 

 

The Delphi Magazine

Edited by Chris J G Frizelle

iTec Publishing

To subscribe in the UK:

Tel/Fax:  +44 (0) 181 460 0650

Email: 70630.717@compuserve.com

 

To subscribe in the US:

Phone: (802) 244-7820

Email: 70602.1215@compuserve.com

 

This magazine consistently contains excellent technical articles on Delphi-related subjects. Also contains a monthly column by Bob Swart (a.k.a. Dr. Bob) on component building in Delphi.

 

 

 

 

Design Patterns--Elements of Reusable Object-Oriented Software

by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides

Foreword by Grady Booch

Addison-Wesley Publishing Company

395 pages

ISBN 0-201-63361-2

 

This book organizes and presents a catalog of proven object-oriented solutions for architecting complex systems that you can apply to your own specific applications.  These design constructs are labeled, allowing your development team to share a common vocabulary.  The CDK help file references two of the design patterns in this book:  the Proxy pattern and the Template Method pattern.  The examples are in C++, but the patterns apply to all programming languages.

 

 

 

Component Writer’s Guide

by Borland International, Inc.

(408) 431-1000

156 pages

 

The Component Writers Guide is a lean but important resource for learning about how to create working components and to ensure that components you write are well-behaved parts of the Delphi environment.  The book guides you to writing components that fit in well with any Delphi application.

 

 

About the Author
 

Mark Miller is the lead software engineer at Eagle Software. Mark holds the primary vision for the Component Developer Kit for Delphi (the CDK) reAct, a component testing tool for Delphi, as well as several other Delphi add-ons currently in development. He has been programming in Pascal, Object Pascal, and Delphi for nearly 16 years.

 

He can be reached at markm@eagle-software.com, or via CompuServe at 76043,2422.

转载于:https://www.cnblogs.com/MaxWoods/archive/2006/07/20/455755.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值