Developing components in Delphi

Last Updated: 9/21/2008



Sections above here:
Home  »  Development  »  Custom Components

Sections below here:

    Topics in this section:
    Custom Component Development
    Developing components in Delphi
    Miscellaneous issues
    Third Party Program

    Search Documentation:

    Delphi component development is simple, but there is one thing you should bear in mind here. It was mentioned before that IW "wraps up" all its code into HTML tags. Hence the set of visual components you can develop in IntraWeb is restricted by

    • HTML specification
    • WEB browsers

    It could make you think in terms of HTML tags rather than pure Delphi code.

    Inheritance

    TIWCustomControl is base class to inherit your own component from - if you want to design one from the scratch. There are also "custom skeletons" available. E.g. TIWCustomLabel, these base classes already define quite a few base behavior and make your life easier. Being the base classes, you should never create instances of them directly. Descendant classes should be used instead to override or introduce properties, methods etc.

    You can also descend from "final" component classes, such as TIWLabel. It basically depends on what you want to achieve. The more different your new component  is supposed to behave, the earlier you have to descend.

    [TIWCustomControl] -> [TIWCustomLabel] -> [TIWLabel]

    "Moving Label" demo

    To illustrate, let's develop a moving label. It will be inherited from TIWCustomLabel. The purpose of this component is to show a moving advertisement text running through the browser window. The HTML-tag which the new component is based upon is "marquee". This simple demo will show the core ideas of IntraWeb components.

    To begin with, launch Delphi's New Component wizard and fill the gaps as follows:





    The new class name is TIWMovingLabel, inherited from TIWCustomLabel. After clicking on "Finish" you should have the following unit:

    unit IWMovingLabel;

    interface

    uses
      SysUtils, Classes, Controls, IWVCLBaseControl, IWBaseControl,
      IWBaseHTMLControl, IWControl, IWCompLabel;

    type
      TIWMovingLabel = class(TIWCustomLabel)
      private
        { Private declarations }
      protected
        { Protected declarations }
      public
        { Public declarations }
      published
        { Published declarations }
      end;

    procedure Register;

    implementation

    procedure Register;
    begin
      RegisterComponents('Samples', [TIWMovingLabel]);
    end;

    end.

    Further, we have to let IntraWeb HTML-render machine know about HTML tags the new component should be wrapped up into. To do this let's declare override procedure RenderHTML as

    type

    TIWLabelM = class (TIWCustomLabel)

    private

    { Private declarations }

    protected

    { Protected declarations }

    public

    function RenderHTML: TIWHTMLTag; override ;

    published

    property Caption;

    end ;

    with following constructor

    constructor TIWLabelM.Create(AOwner: TComponent);

    begin

    inherited Create(AOwner);

    Caption:=Name;

    end ;

    And finally, write its new implementation

    function TIWLabelM.RenderHTML: TIWHTMLTag;

    begin

    Result:=TIWHTMLTag.CreateTag(' marquee ');

    try

    Result.Contents.AddText(Caption);

    except

    FreeAndNil(Result); raise ;

    end ;

    end ;

    The passage above is the gist of the whole topic. The only thing remains unrevealed is the TIWHTMLTag class . You can get to know more on TIWHTMLTag class from Help or refer to Tags implementation

    Installing components on Delphi's Component palette

    Installing a new IW component on Delphi Component palette is done in the usual manner, but there is one thing here to point out. When you place the IWLabelM on a form in design time no caption shows. It looks like

    Use the resource icon we made in Delphi's Image Editor tool and without any captions. Some extra code must be added to paint a text:

    • procedure IWRegisterPaintHandler, add its declaration into the initialization section:

    initialization

    IWRegisterPaintHandler('TIWLabelM',TIWLabelMPaintHandler);

    • adjust the uses clause:

    uses

    SysUtils, Classes, IWBaseControl, IWControl, IWCompLabel,

    {$IFDEF Linux}QGraphics, {$ELSE}Graphics, {$ENDIF}

    {$IFDEF Linux}QControls, {$ELSE}Controls, {$ENDIF}

    IWHTMLTag,

    IWTypes,IWDsnPaint,Types;

    • declare the paint handler as:

    TIWLabelMPaintHandler = class (TIWPaintHandlerDsn)

    public

    procedure Paint; override ;

    end ;

    • implement the page handler:

    procedure TIWLabelMPaintHandler.Paint;

    var

    TheRect : TRect;

    begin

    with FControl as TIWCustomLabel do

    begin

    TheRect:=Rect(2,2,Width-2,Height-2);

    Canvas.TextRect(TheRect,2,2,Caption)

    end ;

    end ;

    All this stuff above will result in true "Delphi-style" component design-time appearance.

    Testing the installed component

    When testing the TIWLabelM we could see the advertising text travel ing from right to left on the screen:

    T he corresponding HTML source looks like:

    <marquee id="TIWLABELM1" class="TIWLABELM1CSS">Text to move</marquee>

    TIWHTMLTag

    It allows the developer to work with HTML-tags and holds tag information for most of the IntraWeb controls. TIWHTMLTag is extensively used by IntraWeb to build HTML tags for the final pages, hold parameter information, JavaScript events, etc. We'll just concentrate on few of its members.

    Contents . AddText

    Adds a text to the HTML element. Example:

    function TIWLabelM.RenderHTML: TIWHTMLTag;

    begin

    Result:=TIWHTMLTag.CreateTag(' marquee ');

    try

    Result.Contents.AddText(Caption);

    Result.Contents.AddText('<Img Src="/files/home.jpg">');

    except

    FreeAndNil(Result); raise ;

    end ;

    end ;

    This Delphi code could result in following HTML reperesentation:

    <marquee id="TIWLABELM1" class="TIWLABELM1CSS">Text to move<Img Src="/files/home.jpg"></marquee>

    Contents . AddTag

    Adds a new closed tag into Contents collection. The method returns the new TIWHTMLTag object that has been created and added to the collection. The code above could be rewritten as

    Result.Contents.AddTag('Img Src="/files/home.jpg"');

    Contents . AddTagAsObject

    Adds a tag object into current HTML element. The code above could be rewritten as

    function TIWLabelM.RenderHTML: TIWHTMLTag;

    var

    LTag : TIWHTMLTag;

    begin

    Result:=TIWHTMLTag.CreateTag('marquee');

    try

    Result.Contents.AddText(Caption);

    LTag:=TIWHTMLTag.CreateTag('Img');

    with LTag do

    Try

    LTag.Add('Src=/files/home.jpg');

    Result.Contents.AddTagAsObject(LTag);

    except

    FreeAndNil(LTag); raise ;

    end ;

    except

    FreeAndNil(Result); raise ;

    end ;

    end ;

    AddStringParam

    ( AddBoolParam , AddIntegerParam )

    Adds a parameter to the Params list. Example:

    function TIWLabelM.RenderHTML: TIWHTMLTag;

    begin

    Result:=TIWHTMLTag.CreateTag(' marquee ');

    try

    Result.Contents.AddText(Caption);

    Result.AddStringParam('loop','1');

    except

    FreeAndNil(Result); raise ;

    end ;

    end ;

    This Delphi code makes LabelM to move through the window once only. Corresponding HTML reperesentation looks like

    <marquee loop="1" id="TIWLABELM1" class="TIWLABELM1CSS">Text to move</marquee>

    Contents . Add

    Call the Add method to add a new value to the params list of a current tag. The code above could be rewritten as

    function TIWLabelM.RenderHTML: TIWHTMLTag;

    begin

    Result:=TIWHTMLTag.CreateTag(' marquee ');

    try

    Result.Contents.AddText(Caption);

    Result.Add('loop=1');

    except

    FreeAndNil(Result); raise ;

    end ;

    end ;

    Javascript

    Embedding into components

    You may put JavaScript code directly into component to be rendered. It makes your application more flexible then you assign values to ScriptEvents or to TIWForm.JavaScript.

    Let's extend "Moving Label" example in the following way: we need the IWLabelM to show various advertising text in series.

    TIWControl.AddToJavaScriptOnce

    You could re-implement TIWLabelM as follows:

    function TIWLabelM.RenderHTML: TIWHTMLTag;

    var

    LScript : string ;

    LTag : TIWHTMLTag;

    begin

    Result:=TIWHTMLTag.CreateTag('marquee');

    try

    Result.Contents.AddText(Caption); Result.AddStringParam('loop','1');

    Result.Add('onfinish=ff()');

    LScript:= ' function ff() { '+

    ' var o = event.srcElement; '+

    ' o.stop; ' +

    ' o.innerHTML = "Other adverti s ing text"; '+

    ' o.start; }';

    AddToJavaScriptOnce(LScript);

    except

    FreeAndNil(Result); raise ;

    end ;

    end ;

    AddToJavaScriptOnce method (it comes from TIWLabelM parent ) adds JS-code of ff() function into result page. When "onfinish" event occurs, another text starts to move through the browser window.

    Developing custom JS libs

    Embedding JS code into a component has a major shortcoming. When you instantiate TIWLabelM twice or several times it leads to several ff() declarations in result page. Each instance will have its own ff() function with almost the same code.

    To avoid duplications and make your project faster you can use custom JavaScript libraries. IntraWeb itself based upon a number of them:

    · IWCL.js. This is core library that supports align/anchors functionality.

    · IWCSData.js. This is used for client side datasets. Supports client side data objects.

    · IWExplorer.js. Supports client side browser for HTML elements searching, etc.

    · IWCommon.js. This is the common JS code for all supported browsers.


    Developing custom JS library

    To develope custom JS library for our example do the follows:

    1. Create text file as

    function ff() {

    var o = event.srcElement;

    o.stop;

    o.innerHTML = " Other adverti s ing text ";

    o.start;

    }

    2. Save it into the project's directory as IWLabelM.js

    3 . Create resource file containing the following tab-separated data

    IW_JS_IWLABELM RCDATA "IWLabelM.js"

    and save it with the name IWLabelM.rc.

    Note : RCDATA means binary resource type. IW_JS prefix means it contains JS code only.

    Note : Be sure you write it in upper case.

    4. Compile resource file:

    >brcc32.exe IWLabelM.rc

    It makes the resource file IWLabelM.RES ready to be included into the component unit.


    Including the resource into the component's source

    Make sure you include the resource as follows:

    unit IWLabelM;

    ....

    implementation

    {$R IWLabelM.RES}

    Then add it to the internal files list using initialization section of IWLabelM unit. After doing this the section should read as:

    initialization

    IWRegisterPaintHandler('TIWLabelM',TIWLabelMPaintHandler);

    TIWServer.AddInternalFile('IW_JS_IWLABELM', '/js/IWLabelM.js');

    Adding script file into the page produced

    To add the script file into the current context rewrite the RenderHTML implementation as below:

    function TIWLabelM.RenderHTML: TIWHTMLTag;

    begin

    Result:=TIWHTMLTag.CreateTag('marquee');

    try

    Result.Contents.AddText(Caption);

    Result.Add('onfinish=ff()');

    Result.Add('loop=1');

    AddScriptFile('/js/IWLabelM.js');

    except

    FreeAndNil(Result); raise ;

    end ;

    end ;

    The TIWLabelM.AddScriptFile comes from custom parent component and performs the JavaScript non-duplicated code including we are after. The core server will spit out the result HTML-code as follows:

    ...

    <meta name="GENERATOR" content="IW5.1.22 Serial 12334567">

    <script language=Javascript src="/js/IWCommon.js_5.1.22"></script>

    <script language=Javascript src="/js/IWCL.js_5.1.22"></script>

    <script language=Javascript src="/js/IWCSData.js_5.1.22"></script>

    <script language=Javascript src="/js/IWExplorer.js_5.1.22"></script>

    <script language=Javascript src="/js/IWLabelM.js_5.1.22"> </script>

    </head>...

    JavaScript event handling

    To handle client-side event you may use ScriptEvents property. But it is a good practice for component developers to use TIWControl.HookEvents instead.

    Let's override HookEvents and redeclare TIWLabelM as follows:

    type

    TIWLabelM = class (TIWCustomLabel)

    private

    { Private declarations }

    protected

    { Protected declarations }

    public

    { Public declarations }

    constructor Create(AOwner: TComponent); override ;

    function RenderHTML: TIWHTMLTag; override ;

    procedure HookEvents(AScriptEvents: TIWScriptEvents); override ;

    published

    property Caption;

    property OnClick;

    { Published declarations }

    end ;

    HookEvents comes from TIWControl and allow the programmer to assign any code for AScriptEvents handling .

    If we need, for example, the Moving label to stop when click a mouse button on it, we may fill our event handler with:

    procedure TIWLabelM.HookEvents(AScriptEvents: TIWScriptEvents);

    begin

    AScriptEvents.HookEvent

    ('OnClick', //<-the JS event name

    'document.all["'+HTMLName+'"].stop();' //<-JavaScript code itself

    )

    end ;

    HTMLName is the name of TIWLabelM instance which OnClick event is to be handled.

    Note. IntraWeb adds the function result into the outcome page as

    return true; by default. You may define your own return value if it is necessary:

    AScriptEvents.HookEvent

    ('OnClick',

    'document.all["'+HTMLName+'"].stop();'+EOL+

    'return false;'

    );



    (C) 2002-2009 - Atozed Software Ltd.