Informant Spotlight

Delphi 3/ActiveX

 

By Dan Miser

 

Delphi 3 ActiveX

A Handy Introduction

 

Microsoft has defined a lightweight subset of their OLE technology that is well suited for the Internet. This technology is called ActiveX. Delphi 3 gives developers the ability to easily use existing ActiveX controls in their applications, as well as turn their native VCL components into ActiveX controls. Delphi 3 also provides developers with the ability to extend ActiveX controls, and even create custom ActiveX controls from scratch.

 

This article demonstrates five aspects of ActiveX programming with Delphi 3:

§          importing existing ActiveX controls

§          converting VCL controls into ActiveX controls

§          extending an ActiveX control by adding a property page

§          creating custom ActiveX controls

§          converting a Delphi form into an ActiveForm

 

That’s a lot of ground to cover, so let’s get to it. (Note: You’ll need Microsoft Internet Explorer version 3 or higher to access ActiveX controls on the Web. For Netscape users, a plug-in called ScriptActive will allow ActiveX access. It’s available from NCompass Labs at http://www.ncompasslabs.com.)

 

Importing an Existing ActiveX Control

Because they’re components, ActiveX controls fit nicely into Delphi’s component-based development strategy. In fact, Delphi 3 gives you the ability to integrate these controls right into the Delphi IDE. Select Component | Import ActiveX Control from the menu to display the list of ActiveX controls registered on your system. Any control found in this list can be imported as a Delphi component.

 

The following example will demonstrate how to install an ActiveX control, and how to use it in the Delphi 3 IDE. To follow this example, you must have Progressive Networks’ RealAudio ActiveX control installed on your system; you can find it at http://www.real.com/products/player/playerdl.html.

 

Select Component | Import ActiveX control, and modify the dialog box to look like that shown in Figure 1. If you pressed the Create Unit button, Delphi would generate a VCL wrapper by reading the type information of the ActiveX control, and translating it into Object Pascal syntax. The source code would then be saved in the directory you specify for Unit dir name.

 


Figure 1: The Import ActiveX Control dialog box.

 

However, because all Delphi 3 components need to be installed to a package before you can use them in the Delphi IDE, you should click the Install button. This will create the unit as previously described, then allow you to insert the component into a package by using Delphi’s standard component-installation dialog box. Next, you’ll be prompted to rebuild the modified package. After recompiling, you can treat the ActiveX control as you would any native VCL component.

 

You may have noticed the Add and Remove buttons on this dialog box. These give you a quick, easy way to install and remove ActiveX controls without resorting to Regsvr32, or some other registration utility. Figure 2 shows a RealAudio component on a form; its Source property is set to a RealAudio file, and Play is selected from the context menu. Notice that the file is being played at design time.

 


Figure 2: The RealAudio ActiveX control in action.

 

Creating ActiveX Controls from VCL Controls

You may be wondering how to create your own ActiveX control that others may use. Delphi comes to the rescue again by providing the ability to convert VCL controls to ActiveX controls. This capability is not restricted to Borland’s standard VCL controls; you can even convert your custom VCL controls.

 

Select File | New | ActiveX | ActiveX Control to run the ActiveX Control Wizard. This will generate a fully functional implementation of the ActiveX control, which you can use from within your Delphi applications. For now, let’s build a sample ActiveX control based on Delphi’s own Calendar control (which appears on the Samples page of the Component palette). Modify the ActiveX Control Wizard to look like the dialog box in Figure 3, and click OK.

 


Figure 3: The ActiveX Control Wizard.

 

Not all VCL controls can become ActiveX controls; to display a VCL control in the ActiveX Control Wizard, three requirements must be met:

§          The VCL control must descend from TWinControl. This may force you to alter the parentage of your VCL control, but there is usually a suitable alternative. For example, if you’re trying to port a non-visual control to an ActiveX control, you might consider using TCustomControl as the ancestor, and provide a simple Paint method.

§          The VCL control must descend from a control capable of supporting ActiveX conversion. Most Delphi components use the RegisterComponents procedure to install themselves to the Component palette. However, if you don’t want a control to be converted to ActiveX, you can use the RegisterNonActiveX procedure to install the component. A common reason to disallow ActiveX conversion is that the VCL control references other components. Having ActiveX controls talk to each other in this manner would be very difficult, so it’s easier to disallow the conversion altogether. Delphi’s data-aware controls are a prime example of this. Also, registering a control with RegisterNonActiveX will disqualify that control’s descendent components.

§          The VCL control must be installed in Delphi. If the component isn’t installed in the Component palette, it’s not registered with Delphi.

 

Once you click OK, Delphi will convert the VCL control to an ActiveX control. Because an ActiveX control is based on OLE, the VCL control can only translate properties that have a corresponding native-OLE data type. In addition, you can provide an adapter to convert the non-standard data type into something that OLE can understand. For example, Delphi comes bundled with adapters, so OLE can use properties of types TString, TPicture, and TFont. If Delphi can’t map a data type to a type that OLE can understand, Delphi will omit that property from the generated ActiveX control. You can add properties and methods to the ActiveX control manually by selecting Edit | Add To Interface, or editing the type library itself. Or, you can use the Type Library editor (by selecting View | Type Library) and have Delphi 3 do most of the work.

 

Once the code is generated for the ActiveX control, we can manipulate it as we would any other Delphi project. For now, we’ll save the control, then compile it by using the Project | Build All menu item.

 

Once you’ve compiled the ActiveX control and registered it with the system, any application that can use ActiveX controls for development can now access this control. Just for fun, you can import your resulting ActiveX control into Delphi, using the steps previously covered. This will allow you to view this control as other users might.

 

Anatomy of an ActiveX Control

Every project created in Delphi needs a project source file (a .dpr file). ActiveX controls are no exception. An ActiveX Library is the project source file for all ActiveX projects. This file exports the four signature methods of an ActiveX control, provides an extension compiler directive, and includes the appropriate type library.

 

The source file for an example calendar project, CalendarX, is shown in Figure 4.

 

library CalendarX;

 

uses

  ComServ,

  CalendarX_TLB in 'CalendarX_TLB.pas',

  CalImpl in 'CalImpl.pas' {CalendarX: CoClass},

  CalPage in 'CalPage.pas' {CalendarPage: TPropertyPage};

 

exports

  DllGetClassObject,

  DllCanUnloadNow,

  DllRegisterServer,

  DllUnregisterServer;

 

{$R *.TLB}

 

{$R *.RES}

 

{$E ocx}

 

begin

end.

Figure 4: The source file for CalendarX.

 

The compiler directive:

 

{$E ocx}

 

tells Delphi what extension to give to the compiled version of the project. To create a new ActiveX Library project from scratch, you would select File | New | ActiveX | ActiveX Library.

 

The CalendarX control is good, but it would be nice to offer an easier way to edit the properties. This is precisely what property pages were made for.

 

Property Pages

Property pages give users a friendly way to alter the properties of an ActiveX control. A property page has a visual control that represents a corresponding ActiveX property. These controls are displayed in a tabbed notebook.

 

To continue our example project, we’ll give CalendarX the ability to edit the date, in a property page. Create a property page by selecting File | New | ActiveX | Property Page. Press OK to generate skeleton code for a property page. After the form has been created, modify it to look like the one in Figure 5.

 


Figure 5: TCalendarPage at design time.

 

The OK, Cancel, and Apply buttons are all provided for you. In addition, the tabbed notebook reflects the number of property pages you’ve created. The only controls you need to place on a property-page form are those that implement the desired behavior of the property page.

 

The following steps are necessary to completely implement a property page:

Update the visual controls of the property page to match the state of the ActiveX control.

Modify the visual controls.

If necessary, update the ActiveX control’s properties to reflect the changes made in the property page. This is done only if the OK or Apply button is pressed.

 

Delphi supplies all the pieces to easily implement this logic. The UpdatePropertyPage method will be called whenever the property page is about to be displayed. This is where you set the visual controls to show the ActiveX control’s properties. For example, the following method will set the edit controls to the values of the ActiveX control:

 

procedure TCalendarPage.UpdatePropertyPage;

begin

  edMonth.Text := OleObject.Month;

  edDay.Text := OleObject.Day;

  edYear.Text := OleObject.Year;

end;

 

The TPropertyPage object contains a reference to the ActiveX control, and places it in a variable called OleObject. Access this variable whenever you need to access properties or methods of the ActiveX control.

 

The ActiveX control must be informed when its properties change. Whenever a user changes a property via a property page, you need to call the Modified method of the property page. This tells the ActiveX control that it needs to update itself. For example, the following method is assigned to each Edit component’s OnChange event.

 

procedure TCalendarPage.EditChange(Sender: TObject);

begin

  Modified;

end;

 

The UpdateObject method is called when the ActiveX control is about to set its contents from the property page. Basically, you assign the values you set in UpdatePropertyPages back to the OleObject of the ActiveX control.

 

Register the Page

The last step is to bind the property page to the ActiveX control by registering it. This is accomplished by calling the DefinePropertyPage procedure inside the ActiveX control’s DefinePropertyPages method. This method was created in the skeleton code when you created the ActiveX control. Simply pass the CLSID of the page that you want to register. You can find this value in the interface portion of the property-page unit.

 

procedure TCalendarX.DefinePropertyPages(

  DefinePropertyPage: TDefinePropertyPage);

begin

  DefinePropertyPage(Class_CalendarXPage);

  DefinePropertyPage(Class_DFontPropPage);

end;

 

Note also that you’ll need to add the name of the property-page unit to the Calendar ActiveX unit (that’s where you defined Class_CalendarXPage).

 

Every property page you define creates another tab on the property-page editor. This lets the user concentrate on one logically related group of data at a time. For example, you could create one property page for modifying the fonts used in a control, and another for modifying graphic elements. This would require calling the DefinePropertyPage method for each PropertyPage you want to register for this ActiveX control.

 

You can also easily give your control the ability to modify properties of type TFont, TColor, TPicture, and TStrings. These property pages will scan through your ActiveX control’s properties at run time, and let the user modify individual attributes of these properties. These property pages are implemented in Stdvcl32.dll; therefore, if your control accesses one of the standard property pages (see Figure 6), you’ll need to deploy this file as well.

 


Figure 6: A standard ActiveX property page at run time.

 

ActiveX from Scratch

We’ve seen the powerful ability of Delphi to generate ActiveX controls by using the ActiveX Control Wizard, but what do you do if your control isn’t listed there? Aside from the simple case where you have full control of the component’s parentage, you’ll need to create the ActiveX control from scratch. Here’s where studying Delphi’s generated code really pays off.

 

TreeView components are not listed in the ActiveX Control Wizard mainly because the Items property is of type TTreeNodes. This is not an OLE-compatible type, so there is no automatic way for Delphi to make this structure ActiveX-compliant. However, with a little reengineering, we can expose some of the functionality of the Items property, thus allowing the control to be manipulated as an ActiveX control.

 

To create this ActiveX control, first make sure all the files are closed inside Delphi. Then:

1)       Select File | New | ActiveX Library to get the project file used by ActiveX controls. This creates the project framework for an ActiveX control.

2)       Select File | New | Automation Object, and give the object a class name of TTreeViewX. This will create a COM-object declaration, as well as a type library. Both these files will be modified throughout the rest of this process.

3)       Make the following changes to the source-code statements in the Automation Object unit: The TTreeViewX class must descend from TActiveXControl instead of TAutoObj. The factory-creation class in the initialization section must read:

 

TActiveXControlFactory.Create(ComServer, TTreeViewX,

                              TTreeView,

Class_TreeView, 1, '', 0)

 

4)       Add the extension directive to the project source file. This step ensures that when you compile the project, the resulting binary file will have the standard ActiveX extension — namely, OCX:

 

{$E ocx}

 

5)       Borrowing heavily from a Delphi-generated ActiveX control, make the following modifications to the TTreeViewX class:

 

TTreeViewX = class(TActiveXControl, ITreeViewX)

  private

    FDelphiControl: TTreeView;

    FEvents: ITreeViewXEvents;

  protected

    procedure InitializeControl; override;

    procedure EventSinkChanged(const EventSink: IUnknown); override;

    procedure DefinePropertyPages(DefinePropertyPage:

TDefinePropertyPage); override;

 

These methods will provide the link between the VCL control and the ActiveX control. Whenever you make a change to the ActiveX control, you’ll actually pass that change on to the VCL control that it represents. See the source code for this month’s article for the complete implementation of this control (see end of article for download details).

 

The skeleton for TTreeViewX is now complete. To allow access to the properties, methods and events of the TTreeView control, we need to add these elements to the ActiveX wrapper through the type library.

 

For example, to add the Indent property to the control, select View | Type Library to invoke Delphi’s visual Type Library editor (see Figure 7). Select the ITreeViewX interface, and press the Property button. Name this property Indent. Next, press the Refresh button. This will add two methods to the TTreeViewX class: Get_Indent and Set_Indent. This is where the communication between the VCL control and the ActiveX wrapper occurs.

 


Figure 7: The Type Library editor.

 

For the Indent property, it’s a simple matter of reading and writing the value of FDelphiControl.Indent. Enumerated types are only slightly more complex to deal with, because you need to typecast the result to the appropriate type. You can learn a lot by looking at one or two Delphi-generated ActiveX control wrappers.

 

A TreeView control without items is like a vintage car without a steering wheel: It might be interesting to look at, but you’ll never use it. We need to provide a way to add items to this control without exposing the TTreeNodes structure to the outside world. Creating the AddChild and AddRoot methods to the type library will give TTreeViewX users a way to add items programmatically. The implementation of these methods is shown in Figure 8.

 

procedure TTreeViewX.AddChild(const RootIndex,

  Index: Integer; const S: WideString);

var

  ARoot, AChild : TTreeNode;

  i : Integer;

begin

  // Find the right root position.

  ARoot := FDelphiControl.Items.GetFirstNode;

  for i := 0 to RootIndex-1 do

    if ARoot <> nil then

      ARoot := ARoot.GetNextSibling;

 

  // Now find the right child position.

  if (ARoot <> nil) and

     (ARoot.HasChildren) then

    begin

      AChild := ARoot.GetFirstChild;

      for i := 0 to Index-1 do

        if (AChild <> nil) and

           (AChild.GetNextChild(ARoot) <> nil) then

          AChild := AChild.GetNextChild(ARoot);

      FDelphiControl.Items.Add(AChild, S);

    end

  else

    // If sorted, inserted in sort order; else, inserted

    // as last child node.

    FDelphiControl.Items.AddChild(ARoot,S);

end;

 

procedure TTreeViewX.AddRoot(const Index: Integer;

                             const S: WideString);

begin

  if FDelphiControl.Items.Count = 0 then

    FDelphiControl.Items.Add(nil, S)

  else if (Index>=0) and

          (Index<FDelphiControl.Items.Count) then

    FDelphiControl.Items.Add(FDelphiControl.Items[Index],S);

end;

Figure 8: The AddRoot and AddChild methods.

 

Registering the ActiveX Control

After building an ActiveX control, you can take advantage of Delphi’s ability to register the control for you. Select Run | Register ActiveX Server to create the required registry settings for the ActiveX control. Once registered with your system, you can use the ActiveX control in any capable environment — including Visual Basic and ActiveX Control Pad.

 

Of course, selecting Run | Unregister ActiveX Server will remove the ActiveX control from the registry. We’ll cover this topic in more depth next month, when we explore the issues concerned with deploying ActiveX controls.

 

ActiveForms

Delphi 3 has supplied you with the ability to easily turn an existing form into an ActiveX control known as an ActiveForm. The only real difference between an ActiveForm and an ActiveX control is that the ActiveForm will likely contain several visual controls. You can think of it as a super-component if you want. ActiveForms give you the ability to deploy almost any Delphi form via the Web. However, there are some limitations to what an ActiveForm can do. For example, an ActiveForm cannot be a robust, multi-form application. If you need to access multiple screens in your ActiveForm, consider using a Tabbed Notebook interface instead.

 

Creating an ActiveForm

Let’s create a simple ActiveForm to view and edit information from the Employee table found in \Dbdemos. To create an ActiveForm, select File | New | ActiveX | ActiveForm. An ActiveX Library will be built for you, if necessary.

 

(Note: This demo requires BDE 4.0 to be installed on every client machine that will access this ActiveForm. You can also deploy a thin-client solution by using MIDAS. For more information on how to use MIDAS in a Delphi application, see http://www.borland.com/midas.)

 

An ActiveForm is really not much different than a standard Delphi form. For this example, we’ll place some standard data-aware controls on the ActiveForm, as shown in Figure 9. Notice that we can still use the standard Table and DataSource components to drive our data-aware controls.

 


Figure 9: The ActiveForm at design time.

 

Once all the controls are configured, we can save, compile, and register the ActiveForm as we would any other ActiveX control. At this point, you could use the ActiveX control in Visual Basic, with all its functionality intact. However, it would be nice to give the ActiveX user an interface to control some individual elements of this super-control.

 

Because an ActiveX control is a DLL, you can’t directly access public variables inside the control. Instead, access to the underlying variables of a form must occur through a procedural interface. Use Edit | Add To Interface to make a property accessible to the outside world. For even more control over this interface, use the Type Library editor to manipulate all the attributes of the associated type library.

 

In our example, we would like to give the user a programmatic way to control the contents of the Search edit control’s text property. Select Edit | Add To Interface to display the dialog box shown in Figure 10. This will create two stub methods to your ActiveForm: Get_Search and Set_Search. Fill in the two methods to get and set the edSearch.Text property of the ActiveForm, respectively. Now you can access edSearch.Text from any ActiveX development environment by assigning a value to the Search property of the ActiveForm:

 

function TAFEmployee.Get_Search: WideString;

begin

  Result := edSearch.Text;

end;

 

procedure TAFEmployee.Set_Search(const Value: WideString);

begin

  edSearch.Text := Value;

end;

 


Figure 10: The Add To Interface dialog box.

 

We can further extend this control by adding functionality to programmatically search a database, and even control the visibility of all the search-related controls. In addition, because this really is an ActiveX control, we could even add property pages to this ActiveForm, and import the control for use within Delphi.

 

Conclusion

So there you have it — a whirlwind tour. From simply importing them, to creating your own, in this article, we’ve quickly covered many aspects of Delphi 3 ActiveX programming. In my next article, we’ll discuss techniques for deploying ActiveX controls and ActiveForms, including how, when, and why to use run-time packages with your ActiveX controls, and how to deploy them to the Web with INF and CAB files.

 

The projects referenced in this article are available for download.

 

Dan Miser is a software developer residing in Southern California with his wife and daughter. He has been a Borland Certified Client/Server Developer since 1996, and is a frequent contributor to Delphi Informant. You can contact him at http://www.iinet.com/users/dmiser.

 

Just in case you don’t know, Delphi 3 is a major player in ActiveX development. Mr Miser demonstrates many of Delphi 3’s capabilities in this area, including importing ActiveX controls, converting VCL controls into ActiveX controls, creating custom ActiveX controls, and more.

 

Mr Miser demonstrates Delphi 3’s ActiveX capabilities.