zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    VFP正则表达式判断是否是手机号码/电子邮件
    VFP自定义函数StringFormat (仿.NET String.Format 方法)
    解决SOAPCLIENT访问WebSerivce外网发布端口
    VFP调用SOAPTOOLKIT 低级API
    经典的Hello World VFP前端调后端C# Webservice
    VFP不同句柄 同一事务处理 统一提交或回滚
    C++文件(夹)选择对话框操作
    Linxu之rz和sz命令
    测试标题
    自定义Silverlight TextBox 具有下拉框提示控件
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/455755.html
Copyright © 2011-2022 走看看