• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

HowtoBuildAggregate/CompositeComponentsinDelphi

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

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: [email protected]

 

To subscribe in the US:

Phone: (802) 244-7820

Email: [email protected]

 

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 [email protected], or via CompuServe at 76043,2422.


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Delphi10.3.2试用报告发布时间:2022-07-18
下一篇:
Delphi全面控制Windows任务栏发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap