|
Building IntraWeb ApplicationsWhen 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 end object About1: TMenuItem object Application1: TMenuItem object TreeContents1: TMenuItem end end object IWMenu1: TIWMenu AttachedMenu = MainMenu1 Orientation = iwOHorizontal end 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); var i: Integer; begin for i := 0 to IWTreeView1.Items.Count - 1 do IWTreeView1.Items [i].Expanded := True; end; procedure TformTree.EnlargeFont1Click(Sender: TObject); begin IWTreeView1.Font.Size := IWTreeView1.Font.Size + 2; end; 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); var i: Integer; begin with TIWMemo.Create(Self) do begin 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) + ')'); end; end;
Writing Multipage ApplicationsAll 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); var i: Integer; link: TIWURL; begin // 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 begin 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 := 'http://www.marcocantu.com'; IWGrid1.Cell [i,2].Control := link; end; end; 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. 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: <tr> <td valign="middle" align="left" NOWRAP> <font style="font-size:10pt;">Row 2</font> </td> <td valign="middle" align="left" NOWRAP> <font style="font-size:10pt;">IWTwoForms by Marco Cantù</font> </td> <td valign="middle" align="left" NOWRAP> <font style="font-size:10pt;"></font> <a href="#" onclick="parent.LoadURL('http://www.marcocantu.com')" id="TIWURL1" name="TIWURL1" style="z-index:100;font-style:normal;font-size:10pt;text-decoration:none;"> Click here</a> </td> </tr> 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); begin anotherform := TAnotherForm.Create(WebApplication); anotherform.Show; end; 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).
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: begin 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: 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); var aCanvas: TCanvas; begin aCanvas := IWImage1.Picture.Bitmap.Canvas; aCanvas.Pen.Width := 8; aCanvas.Pen.Color := clGreen; aCanvas.Ellipse(Ax - 10, Ay - 10, Ax + 10, Ay + 10); end;
Sessions ManagementIf 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: type TUserSession = class public UserCount: Integer; end; 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); begin ASession.Data := TUserSession.Create; end; 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.
Again, the default ServerController unit provides a helper function you can use: function UserSession: TUserSession; begin Result := TUserSession(RWebApplication.Data); end; 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); begin InterlockedIncrement (GlobalCount); Inc (FormCount); Inc (UserSession.UserCount); IWLabel1.Text := 'Global: ' + IntToStr (GlobalCount); IWLabel2.Text := 'Form: ' + IntToStr (FormCount); IWLabel3.Text := 'User: ' + IntToStr (UserSession.UserCount); end; 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: 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); begin VForm := TformMain.Create(AWebApplication); end; 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.
Here is a summary of the DFM of the example program's web module: object WebModule1: TWebModule1 Actions = < item Default = True Name = 'WebActionItem1' PathInfo = '/show' OnAction = WebModule1WebActionItem1Action end item Name = 'WebActionItem2' PathInfo = '/iwdemo' Producer = IWPageProducer1 end> object IWModuleController1: TIWModuleController object IWPageProducer1: TIWPageProducer OnGetForm = IWPageProducer1GetForm end end 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 LayoutThe 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.) 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>
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. |
|
Copyright © 2004-2025 "Delphi Sources" by BrokenByte Software. Delphi Programming Guide |
|