Building IntraWeb Applications

Building IntraWeb Applications

When you build an IntraWeb application, a number of components are available. For example, if you look at the IW Standard page of Delphi's Component Palette, you'll see an impressive list of core components, from the obvious button, check box, radio button, edit box, list box, memo, and so on to the intriguing tree view, menu, timer, grid, and link components. I won't list each component and describe its use with an example—I'd rather use some of the components in a few demos and underline the architecture of IntraWeb rather than specific details.

I've built an example (called IWTree) showcasing the menu and tree view components of IntraWeb but also featuring the creation of a component at run time. This handy component makes available in a dynamic menu the content of a standard Delphi menu, by referring its AttachedMenu property to a TMenu component:

object MainMenu1: TMainMenu
  object Tree1: TMenuItem
    object ExpandAll1: TMenuItem
    object CollapseAll1: TMenuItem
    object N1: TMenuItem
    object EnlargeFont1: TMenuItem
    object ReduceFont1: TMenuItem
  object About1: TMenuItem
    object Application1: TMenuItem
    object TreeContents1: TMenuItem
object IWMenu1: TIWMenu
  AttachedMenu = MainMenu1
  Orientation = iwOHorizontal

If the menu items handle the OnClick event in the code, they become links at run time. You can see an example of a menu in a browser in Figure 21.3. The example's second component is a tree view with a set of predefined nodes. This component has a lot of JavaScript code to let you expand and collapse nodes directly in the browser (with no need to call back the server). At the same time, the menu items allow the program to operate on the menu by expanding or collapsing nodes and changing the font. Here is the code for a couple of event handlers:

procedure TformTree.ExpandAll1Click(Sender: TObject);
  i: Integer;
  for i := 0 to IWTreeView1.Items.Count - 1 do
    IWTreeView1.Items [i].Expanded := True;
procedure TformTree.EnlargeFont1Click(Sender: TObject);
  IWTreeView1.Font.Size := IWTreeView1.Font.Size + 2;
Click To expand Figure 21.3: The IWTree example features a menu, a tree view, and the dynamic creation of a memo component.

Thanks to the similarity of IntraWeb components to standard Delphi VCL components, the code is easy to read and understand.

The menu has two submenus, which are slightly more complex. The first displays the application ID, which is an application execution/session ID. This identifier is available in the AppID property of the WebApplication global object. The second submenu, Tree Contents, shows a list of the first tree nodes of the main level along with the number of direct subnodes. What's interesting, though, is that the information is displayed in a memo component created at run time (see again Figure 21.3), exactly as you'll do in a VCL application:

procedure TformTree.TreeContents1Click(Sender: TObject);
  i: Integer;
  with TIWMemo.Create(Self) do
    Parent := Self;
    Align := alBottom;
    for i := 0 to IWTreeView1.Items.Count - 1 do
      Lines.Add (IWTreeView1.Items [i].Caption + ' (' +
        IntToStr (IWTreeView1.Items [i].SubItems.Count) + ')');

Notice that alignment in IntraWeb works similarly to its VCL counterpart. For example, this program's menu with alTop alignment, the tree view has alClient alignment, and the dynamic memo is created with alBottom alignment. As an alternative, you can use anchors (again working as in the VCL): You can create bottom-right buttons, or components in the middle of the page, with all four anchors set. See the following demos for examples of this technique.

Writing Multipage Applications

All the programs you have built so far have had a single page. Now let's create an IntraWeb application with a second page. As you'll see, even in this case, IntraWeb development resembles standard Delphi (or Kylix) development, and is different than most other Internet development libraries. This example will also serve as an excuse to delve into some of the source code automatically generated by the IntraWeb application wizard.

Let's start from the beginning. The main form of the IWTwoForms example features an IntraWeb grid. This powerful component allows you to place within an HTML grid both text and other components. In the example, the grid content is determined at startup (in the OnCreate event handler of the main form):

procedure TformMain.IWAppFormCreate(Sender: TObject);
  i: Integer;
  link: TIWURL;
  // set grid titles
  IWGrid1.Cell[0, 0].Text := 'Row';
  IWGrid1.Cell[0, 1].Text := 'Owner';
  IWGrid1.Cell[0, 2].Text := 'Web Site';
  // set grid contents
  for i := 1 to IWGrid1.RowCount - 1 do
    IWGrid1.Cell [i,0].Text := 'Row ' + IntToStr (i+1);
    IWGrid1.Cell [i,1].Text := 'IWTwoForms by Marco Cantù';
    link := TIWURL.Create(Self);
    link.Text := 'Click here';
    link.URL := '';
    IWGrid1.Cell [i,2].Control := link;

The effect of this code is shown in Figure 21.4. In addition to the output, there are a few interesting things to notice. First, the grid component uses Delphi anchors (all set to False) to generate code that keeps it centered in the page, even if a user resizes the browser window. Second, I've added an IWURL component to the third column, but you could add any other component (including buttons and edit boxes) to the grid.

Click To expand
Figure 21.4:  The IWTwoForms example uses an IWGrid component, embedded text, and IWURL components.

The third and most important consideration is that an IWGrid is translated into an HTML gird, with or without a frame around it. Here is a snippet of the HTML generated for one of the grid rows:

  <td valign="middle" align="left" NOWRAP>
    <font style="font-size:10pt;">Row 2</font>
  <td valign="middle" align="left" NOWRAP>
    <font style="font-size:10pt;">IWTwoForms by Marco Cantù</font>
  <td valign="middle" align="left" NOWRAP>
    <font style="font-size:10pt;"></font>
    <a href="#" onclick="parent.LoadURL('')"
      id="TIWURL1" name="TIWURL1"
      Click here</a>

In the previous listing, notice that the linked URL is activated via JavaScript, not with a direct link. This happens because all actions in IntraWeb allow for extra client-side operations, such as validations, checks, and submits. For example, if you set the Required property for a component, if the field is empty the data won't be submitted, and you'll see a JavaScript error message (customizable by setting the descriptive FriendlyName property).

The core feature of the program is its ability to show a second page. To accomplish this, you first need to add a new IntraWeb page to the application, using the Application Form option on the IntraWeb page of Delphi's New Items dialog box (File ® New ® Other). Add to this page a few IntraWeb components, as usual, and then add to the main form a button or other control you'll use to show the secondary form (with the reference anotherform stored in a field of the main form):

procedure TformMain.btnShowGraphicClick(Sender: TObject);
  anotherform := TAnotherForm.Create(WebApplication);

Even if the program calls the Show method, it can be considered like a ShowModal call, because IntraWeb considers visible pages as a stack. The last page displayed is on the top of the stack and is displayed in the browser. By closing this page (hiding or destroying it), you re-display the previous page. In the program, the secondary pages closes itself by calling the Release method, which as in the VCL is the proper way to dispose of a currently executing form. You can also hide the secondary form and then display it again, to avoid re-creating it each time (particularly if doing so implies losing the user's editing operations).


In the program I added a Close button to the main form. It should not call Release, but rather should invoke the WebApplication object's Terminate method, passing the output message, as in WebApplication .Terminate('Goodbye!'). The demo uses an alternative call: TerminateAndRedirect.

Now that you have seen how to create an IntraWeb application with two forms, let's briefly examine how IntraWeb creates the main form. The relevant code, generated by the IntraWeb wizard as you create a new program, is in the project file:

  IWRun(TFormMain, TIWServerController);

This is different from Delphi's standard project file, because it calls a global function instead of applying a method to a global object representing the application. The effect, though, is quite similar. The two parameters are the classes of the main form and of the IntraWeb controller, which handle sessions and other features as you'll see in a while.

The secondary form of the IWTwoForms example shows another interesting feature of IntraWeb: its extensive graphics support. The form has a graphical component with the classic Delphi Athena image. This is accomplished by loading a bitmap into the an IWImage component: IntraWeb converts the bitmap into a JPEG, stores it in a cache folder created under the application folder, and returns a reference to it, with the following HTML:

<img src="/cache/JPG1.tmp" name="IWIMAGE1" border="0" width="153" height="139">

The extra feature provided by IntraWeb and exploited by the program is that a user can click on the image with the mouse to modify the image by launching server-side code. In this program, the effect is to draw small green circles.

This effect is obtained with the following code:

procedure Tanotherform.IWImage1MouseDown(ASender: TObject;
  const AX, AY: Integer);
  aCanvas: TCanvas;
  aCanvas := IWImage1.Picture.Bitmap.Canvas;
  aCanvas.Pen.Width := 8;
  aCanvas.Pen.Color := clGreen;
  aCanvas.Ellipse(Ax - 10, Ay - 10, Ax + 10, Ay + 10);

The painting operation takes place on the bitmap canvas. Do not try to use the Image canvas (as you can do with the VCL's TImage component), and do not try to use a JPEG in the first place, or you'll see either no effect or a run-time error.

Sessions Management

If you've done any web programming, you know that session management is a complex issue. IntraWeb provides predefined session management and simplifies the way you work with sessions. If you need session data for a specific form, all you have to do is add a field to that form. The IntraWeb forms and their components have an instance for each user session. For example, in the IWSession example, I've added to the form a field called FormCount. As a contrast, I've also declared a global unit variable called GlobalCount, which is shared by all the instances (or sessions) of the application.

To increase your control over session data and let multiple forms share it, you can customize the TUserSession class that the IntraWeb Application Wizard places in the ServerController unit. In the IWSession example, I've customized the class as follows:

  TUserSession = class
    UserCount: Integer;

IntraWeb creates an instance of this object for each new session, as you can see in the IWServerControllerBaseNewSession method of the TIWServerController class in the default ServerController unit:

procedure TIWServerController.IWServerControllerBaseNewSession(
  ASession: TIWApplication; var VMainForm: TIWAppForm);
  ASession.Data := TUserSession.Create;

In an application's code, the session object can be referenced by accessing the Data field of the RWebApplication global variable, used to access the current user's session.


RWebApplication is a threadvar variable, defined in the IWInit unit. It gives you access to the session data in a thread-safe way: you need to take special care to access it even in a multi-threading environment. This variable can be used outside of a form or control (which are natively session-based), which is why it is primarily used inside data modules, global routines, and non-IntraWeb classes.

Again, the default ServerController unit provides a helper function you can use:

function UserSession: TUserSession;
  Result := TUserSession(RWebApplication.Data);

Because most of this code is generated for you, after adding data to the TUserSession class you simply use it through the UserSession function, as in the following code extracted from the IWSession example. When you click a button, the program increases several counters (one global and two session-specific) and shows their values in labels:

procedure TformMain.IWButton1Click(Sender: TObject);
  InterlockedIncrement (GlobalCount);
  Inc (FormCount);
  Inc (UserSession.UserCount);
  IWLabel1.Text := 'Global: ' + IntToStr (GlobalCount);
  IWLabel2.Text := 'Form: ' + IntToStr (FormCount);
  IWLabel3.Text := 'User: ' + IntToStr (UserSession.UserCount);

Notice that the program uses Windows' InterlockedIncrement call to avoid concurrent access to the global shared variable by multiple threads. Alternative approaches include using a critical section or Indy's TidThreadSafeInteger (found in the IdThreadsafe unit).

Figure 21.5 shows the output of the program (with two sessions running in two different browsers). The program has also a check box that activates a timer. Odd as it sounds, in an IntraWeb application, timers work almost the same as in Windows. When the timer interval expires, code is executed. Over the Web, this means refreshing the page by triggering a refresh in the JavaScript code:

Click To expand
Figure 21.5: The IWSession application has both session-specific and global counters, as you can see by running two sessions in two different browsers (or even in the same browser).
IWTIMER1=setTimeout('SubmitClick("IWTIMER1","", false)',5000);

Integrating with WebBroker (and WebSnap)

Up to now, you have built stand-alone IntraWeb applications. When you create an IntraWeb application in a library to be deployed on IIS or Apache, you are basically in the same situation. Things change considerably, however, if you want to use IntraWeb Page mode, which is integrated in an IntraWeb page in a WebBroker (or WebSnap) Delphi application.

The bridge between the two worlds is the IWPageProducer component. This component hooks to a WebBroker action like any other page producer component and has a special event you can use to create and return an IntraWeb form:

procedure TWebModule1.IWPageProducer1GetForm(ASender: TIWPageProducer;
  AWebApplication: TIWApplication; var VForm: TIWPageForm);
  VForm := TformMain.Create(AWebApplication);

With this single line of code (plus the addition of an IWModuleController component in the web module), the WebBroker application can embed an IntraWeb page, as the CgiIntra program does. The IWModuleController component provides core services for IntraWeb support. A component of this type must exist in every project for IntraWeb to work properly.


The release that comes with Delphi 7 has a problem with Delphi's Web App Debugger and the IWModuleController component. This issue has been fixed and is a free update.

Here is a summary of the DFM of the example program's web module:

object WebModule1: TWebModule1
  Actions = <
      Default = True
      Name = 'WebActionItem1'
      PathInfo = '/show'
      OnAction = WebModule1WebActionItem1Action
      Name = 'WebActionItem2'
      PathInfo = '/iwdemo'
      Producer = IWPageProducer1
  object IWModuleController1: TIWModuleController
  object IWPageProducer1: TIWPageProducer
    OnGetForm = IWPageProducer1GetForm

Because this is a Page mode CGI application, it has no session management. Moreover, the status of the components in a page is not automatically updated by writing event handlers, as in a standard IntraWeb program. To accomplish the same effect you need to write specific code to handle further parameters of the HTTP request. It should be clear even from this simple example that Page mode does less for you than Application mode, but it's more flexible. In particular, IntraWeb Page mode allows you to add visual RAD design capabilities to your WebBroker and WebSnap applications.

Controlling the Layout

The CgiIntra program features another interesting technology available in IntraWeb: the definition of a custom layout based on HTML. (That topic isn't really related, because HTML layouts work also in Application mode—but I've happened to use these two techniques in a single example.) In the programs built so far, the resulting page is the mapping of a series of components placed at design time on a form, in which you can use properties to modify the resulting HTML. But what if you want to embed a data-entry form within a complex HTML page? Building the entire page contents with IntraWeb components is awkward, even if you can use the IWText component to embed a custom piece of HTML within an IntraWeb page.

The alternative approach is represented by the use of IntraWeb's layout managers. In IntraWeb you invariably use a layout manager; the default is the IWLayoutMgrForm component. The other two alternatives are IWTemplateProcessorHTML for working with an external HTML template file and IWLayoutMgrHTML for working with internal HTML.

This second component includes a powerful HTML editor you can use to prepare the generic HTML as well as embed the required IntraWeb components (something you'll have to do manually with an external HTML editor). Moreover, as you select an IntraWeb component from this editor (which is activated by double-clicking on an IWLayoutMgrHTML component), you'll be able to use Delphi's Object Inspector to customize the component's properties. As you can see in Figure 21.6, the HTML Layout Editor available in IntraWeb is a powerful visual HTML editor; the HTML text it generates is available in a separate page. (The HTML editor will be improved in a coming upgrade, and a few quirks will be fixed.)

Click To expand
Figure 21.6:  IntraWeb's HTML Layout Editor is a full-blown visual HTML editor.

In the generated HTML, the HTML defines the structure of the page. The components are marked only with a special tag based on curly braces, as in the following of the example:

<P> {%IWLabel1%} {%IWButton1%}</P>

Notice that when you're using HTML the components don't use absolute positioning but flow along with the HTML. Thus the form becomes only a component holder, because the size and position of the form's components are ignored.

Needless to say, the HTML you see in the visual designer of the HTML Layout Editor corresponds almost perfectly to the HTML you'll see when running the program in a browser.

Part I: Foundations