Delphi Programming Guide
Delphi Programmer 

Menu  Table of contents
Bookmark and Share

Part I - Foundations
  Chapter 1 Delphi 7 and Its IDE
  Chapter 2 The Delphi Programming Language
  Chapter 3 The Run-Time Library
  Chapter 4 Core Library classes
  Chapter 5 Visual Controls
  Chapter 6 Building the User Interface
  Chapter 7 Working with Forms
Part II - Delphi Object-Oriented Architectures
  Chapter 8 The Architecture of Delphi Applications
  Chapter 9 Writing Delphi Components
  Chapter 10 Libraries and Packages
  Chapter 11 Modeling and OOP Programming (with ModelMaker)
  Chapter 12 From COM to COM+
Part III - Delphi Database-Oriented Architectures
  Chapter 13 Delphi's Database Architecture
  Chapter 14 Client/Server with dbExpress
  Chapter 15 Working with ADO
  Chapter 16 Multitier DataSnap Applications
  Chapter 17 Writing Database Components
  Chapter 18 Reporting with Rave
Part IV - Delphi, the Internet, and a .NET Preview
  Chapter 19 Internet Programming: Sockets and Indy
  Chapter 20 Web Programming with WebBroker and WebSnap
  Chapter 21 Web Programming with IntraWeb
  Chapter 22 Using XML Technologies
  Chapter 23 Web Services and SOAP
  Chapter 24 The Microsoft .NET Architecture from the Delphi Perspective
  Chapter 25 Delphi for .NET Preview: The Language and the RTL
       
  Appendix A Extra Delphi Tools by the Author
  Appendix B Extra Delphi Tools from Other Sources
  Appendix C Free Companion Books on Delphi
       
  Index    
  List of Figures    
  List of tables    
  List of Listings    
  List of Sidebars  

 
Previous Section Next Section

Writing Property Editors

Writing components is an effective way to customize Delphi, helping developers to build applications more quickly without requiring a detailed knowledge of low-level techniques. The Delphi environment is also open to extensions. In particular, you can extend the Object Inspector by writing custom property editors and the Form Designer by adding component editors.

Along with these techniques, Delphi offers internal interfaces to add-on tool developers. Using these interfaces, known as the OpenTools API, requires an advanced understanding of how the Delphi environment works and a fairly good knowledge of many advanced techniques that are not discussed in this book. For references to technical information and some examples of these techniques, see Appendix A, "Extra Delphi Tools by the Author."

Note 

The OpenTools API in Delphi has changed considerably over time. For example, the DsgnIntf unit from Delphi 5 has been split into DesignIntf, DesignEditors, and other specific units. Borland has also introduced interfaces to define the sets of methods for each kind of editor. However, most of the simpler examples, such as those presented in this book, compile almost unchanged from earlier versions of Delphi. For more information, you can study the extensive source code in Delphi's \Source\ToolsApi directory. Notice also that with Delphi 6 Update Pack 2 Borland has for the first time shipped a Help file with the documentation of the OpenTools API.

Every property editor must inherit from the abstract TPropertyEditor class, which is defined in the DesignEditors unit and provides a standard implementation for the IProperty interface. Delphi already defines some specific property editors for strings (the TStringProperty class), integers (the TIntegerProperty class), characters (the TCharProperty class), enumerations (the TEnumProperty class), and sets (the TSetProperty class), so you can inherit your property editor from the one for the property type you are working with.

In any custom property editor, you must redefine the GetAttributes function so it returns a set of values indicating the capabilities of the editor. The most important attributes are paValueList and paDialog. The paValueList attribute indicates that the Object Inspector will show a combo box with a list of values (eventually sorted if the paSortList attribute is set) provided by overriding the GetValues method. The paDialog attribute style activates an ellipsis button in the Object Inspector, which executes the editor's Edit method.

An Editor for the Sound Properties

The sound button you built earlier has two sound-related properties: SoundUp and SoundDown. These are strings, so you can display them in the Object Inspector using a default property editor. However, requiring the user to type the name of a system sound or an external file is not friendly, and it's a bit error-prone.

We could write a generic editor to handle filenames, but you want to be able to choose the name of a system sound as well. (System sounds are predefined names of sounds connected with user operations, associated with actual sound files in the Windows Control Panel's Sounds applet.) For this reason, I built a more complex property editor. My editor for sound strings allows a user to either choose a value from a drop-down list or display a dialog box from which to load and test a sound (from a sound file or a system sound). The property editor provides both Edit and GetValues methods:

type
  TSoundProperty = class (TStringProperty)
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure Edit; override;
  end;
Tip 

The default Delphi convention is to name a property editor class with a name ending with Property and all component editors with a name ending with Editor.

The GetAttributes function combines the paValueList (for the drop-down list) and paDialog (for the custom edit box) attributes, and also sorts the lists and allows the selection of the property for multiple components:

function TSoundProperty.GetAttributes: TPropertyAttributes;
begin
  // editor, sorted list, multiple selection
  Result := [paDialog, paMultiSelect, paValueList, paSortList];
end;

The GetValues method calls the procedure it receives as a parameter many times, once for each string it wants to add to the drop-down list (as you can see in Figure 9.12):

procedure TSoundProperty.GetValues(Proc: TGetStrProc);
begin
  // provide a list of system sounds
  Proc ('Maximize');
  Proc ('Minimize');
  Proc ('MenuCommand');
  Proc ('MenuPopup');
  Proc ('RestoreDown');
  ...
end;

Figure 9.12: The list of sounds provides a hint for the user, who can also type in the property value or double-click to activate the editor (shown later, in Figure 9.13).

A better approach would be to extract these values from the Windows Registry, where all these names are listed. The Edit method is straightforward: It creates and displays a dialog box. I could have displayed the Open dialog box directly, but I decided to add an intermediate step to allow the user to test the sound. This is similar to what Delphi does with graphic properties: You open the preview first, and load the file only after you've confirmed that it's correct. The most important step is to load the file and test it before you apply it to the property. Here is the code for the Edit method:

procedure TSoundProperty.Edit;
begin
  SoundForm := TSoundForm.Create (Application);
  try
    SoundForm.ComboBox1.Text := GetValue;
    // show the dialog box
    if SoundForm.ShowModal = mrOK then
      SetValue (SoundForm.ComboBox1.Text);
  finally
    SoundForm.Free;
  end;
end;

The GetValue and SetValue methods are defined by the base class, the string property editor. They read and write the value of the current component's property that you are editing.

As an alternative, you can access the component you're editing by using the GetComponent method (which requires a parameter indicating which of the selected components you are working on—0 indicates the first component). When you access the component directly, you also need to call the Designer object's Modified method (a property of the base class property editor). You don't need this Modified call in the example, because the base class SetValue method does this automatically for you.

The previous Edit method displays a dialog box—a standard Delphi form that is built visually, as always, and added to the package hosting the design-time components. The form is quite simple; a ComboBox displays the values returned by the GetValues method, and four buttons allow you to open a file, test the sound, and terminate the dialog box by accepting the values or canceling. You can see an example of the dialog box in Figure 9.13. Providing a drop-down list of values and a dialog box for editing a property causes the Object Inspector to display only the arrow button that indicates a drop-down list and to omit the ellipsis button to indicate that a dialog box editor is available. In this case, as it happened for the default Color property editor, the dialog box is obtained by double-clicking the current value or pressing Ctrl+Enter.

Click To expand
Figure 9.13:  The Sound Property Editor's form displays a list of available sounds and lets you load a file and hear the selected sound.

The form's first two buttons have a method assigned to their OnClick event:

procedure TSoundForm.btnLoadClick(Sender: TObject);
begin
  if OpenDialog1.Execute then
    ComboBox1.Text := OpenDialog1.FileName;
end;
   
procedure TSoundForm.btnPlayClick(Sender: TObject);
begin
  PlaySound (PChar (ComboBox1.Text), 0, snd_Async);
end;

Notice that it is far from simple to determine whether a sound is properly defined and is available. (You can check the file, but the system sounds create a few issues.) The PlaySound function returns an error code when played synchronously, but only if it can't find the default system sound it attempts to play if it can't find the sound you ask for. If the requested sound is not available, it plays the default system sound and doesn't return the error code. PlaySound looks for the sound in the Registry first and, if it doesn't find the sound there, checks to see whether the specified sound file exists.

Tip 

If you want to further extend this example, you might add graphics to the drop-down list displayed in the Object Inspector—if you can decide which graphics to attach to particular sounds.

Installing the Property Editor

After you've written this code, you can install the component and its property editor in Delphi. To accomplish this, you have to add the following statement to the unit's Register procedure:

procedure Register;
begin
  RegisterPropertyEditor (TypeInfo(string), TMdSoundButton, 'SoundUp',
    TSoundProperty);
  RegisterPropertyEditor (TypeInfo(string), TMdSoundButton, 'SoundDown',
    TSoundProperty);
end;

This call registers the editor specified in the last parameter for use with properties of type string (the first parameter), but only for a specific component and for a property with a specific name. These last two values can be omitted to provide more general editors. Registering this editor allows the Object Inspector to show a list of values and the dialog box called by the Edit method.

To install this component, you can add its source code file to an existing or new package. Instead of adding this unit and the others in this chapter to the MdPack package, I created a second package containing all the add-ins built in this chapter. The package is named MdDesPk (Mastering Delphi design package). What's new about this package is that I compiled it using the {$DESIGNONLY} compiler directive. This directive is used to mark packages that interact with the Delphi environment, installing components and editors, but are not required at run time by applications you've built.

Note 

The source code for all the add-on tools is in the MdDesPk subdirectory, along with the code for the package used to install them. There are no examples demonstrating how to use these design-time tools, because all you have to do is select the corresponding components in the Delphi environment and see how they behave.

The property editor's unit uses the SoundB unit, which defines the TMdSoundButton component. For this reason, the new package should refer to the existing package. Here is its initial code (I'll add other units to it later in this chapter):

package MdDesPk;
   
{$R *.RES}
{$ALIGN ON}
...
{$DESCRIPTION 'Mastering Delphi DesignTime Package'}
{$DESIGNONLY}
   
requires
  vcl,
  MdPack,
  designide;
   
contains
  PeSound in 'PeSound.pas',
  PeFSound in 'PeFSound.pas' {SoundForm};

 
Previous Section Next Section


 


 


Copyright © 2004-2016 "Delphi Sources". Delphi Programming Guide
     Twitter     Facebook