1. Technology

Your suggestion is on its way!

An email with a link to:

http://delphi.about.com/library/bluc/text/uc083101e.htm

was emailed to:

Thanks for sharing About.com with others!

RTL reference|Glossary|Tips/Tricks|FREE App/VCL|Best'O'Net|Books|Link To
 
Creating Custom Delphi Components, Part II
Page 5: Sub-properties
 More of this Feature
• Page 1: Component references
• Page 2: Sets
• Page 3: Binary properties
• Page 4: Collections

• Download Demo Projects

Printer friendly versionPrinter friendly version
 Join the Discussion
"Post your questions, concerns, views and comments to this article..."
Discuss!
 Related Resources
• Custom VCL development
• VCL with source
• Third party VCL
• VCL using

   Sub-properties
Earlier on in this article we saw how it was possible to create an expandable property. The limitation of the earlier technique was that each sub-item appeared as a Boolean property. This next section will demonstrate how to create expandable properties that can contain any property type.

If a component requires a property that is a record type, this could quite easily be implemented by exposing each of the properties separately. If however our component needs to introduce two or more properties of the same complex type our Object Inspector view suddenly becomes very complicated.

The answer is to create a complex structure (alike to a record or object) and to publish this structure as a property whenever needed. The obvious problem is that Delphi does not know how to display this property unless we tell it. Creating a fully blown property editor (with dialogs etc) would be overkill, so luckily Delphi has provided a solution. As mentioned earlier, Delphi's internal streaming is based around the TPersistent class. The first step therefore is to derive our complex structure from this class.

type
  TExpandingRecord = class(TPersistent)
  private
    FIntegerProp : Integer;
    FStringProp : String;
    FCollectionProp : TOurCollection;
    procedure SetCollectionProp(const Value: 
      TOurCollection);
  public
    constructor Create(AOwner : TComponent);
    destructor Destroy; override;

    procedure Assign(Source : TPersistent); override;
  published
    property IntegerProp : Integer
      read FIntegerProp
      write FIntegerProp;
    property StringProp : String
      read FStringProp
      write FStringProp;
    property CollectionProp : TOurCollection
      read FCollectionProp
      write SetCollectionProp;
  end;

In the above structure we have created a descendent of TPersistent and given it three example properties, an integer, a string, and the collection that we created earlier in this article TOurCollection.

The constructor and destructor simply take care of creating and destroying the CollectionProp object, and the SetCollectionProp is implanted to stop this object reference from being lost (we Assign(value), rather than FCollectionProp := value). Whereas Assign is implemented in order to allow us to assign the properties of TExpandableRecord to another TExpandableRecord (again, this is necessary because we will need to Assign it when it is finally implemented as a property of a component).

procedure TExpandingRecord.Assign(Source: TPersistent);
begin
  if Source is TExpandingRecord then
    with TExpandingRecord(Source) do begin
      Self.IntegerProp := IntegerProp;
      Self.StringProp := StringProp;

      //This actually assigns
      Self.CollectionProp := CollectionProp;    
    end else
      inherited; //raises an exception
end;

constructor TExpandingRecord.Create(AOwner : TComponent);
begin
  inherited Create;
  FCollectionProp := TOurCollection.Create(AOwner);
end;

destructor TExpandingRecord.Destroy;
begin
  FCollectionProp.Free;
  inherited;
end;

procedure TExpandingRecord.SetCollectionProp(const Value: TOurCollection);
begin
  FCollectionProp.Assign(Value);
end;

Once this code has been implemented all of the hard work is done. To implement this class as an object is now quite straight forward.

(See ExpandingComponent.pas)

TExpandingComponent = class(TComponent)
  private
    FProperty1,
    FProperty2,
    FProperty3 : TExpandingRecord;
  protected
    procedure SetProperty1(const Value :      
      TExpandingRecord);
    procedure SetProperty2(const Value : 
      TExpandingRecord);
    procedure SetProperty3(const Value : 
      TExpandingRecord);
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
  published
    property Property1 : TExpandingRecord
      read FProperty1
      write SetProperty1;
    property Property2 : TExpandingRecord
      read FProperty2
      write SetProperty2;
    property Property3 : TExpandingRecord
      read FProperty3
      write SetProperty3;
  end;

The constructor will need to create the three objects used as properties, the destructor will obviously need to destroy them. The SetPropertyX procedures will Assign(Value) to the correct object.

Compile this component into a package and drop a TExpandingComponent onto your form. Looking in the object inspector you will notice that our TExpandingRecord propertys all have a [+] next to them, clicking this button will expand our property to reveal all of its sub-properties.

Expanding component

At first glance all looks well, our properties are shown with an expandable sub-set of properties, but everything is not as perfect as it may first seem. Clicking on the […] button of our "CollectionProp" does not invoke the standard TCollection editor, in fact, it does nothing.

You may ask yourself "What have we done wrong ?", the answer is "Nothing !". The error here is not on our part at all, the fault lies with the developers of Delphi, Borland. Although I like to think of the developers of Delphi as infallible, they are not, and they do sometimes make mistakes, and this is a perfect example of one.

When you register a property editor you can limit the component scope that it applies to. You can specify that it should only work on certain property names, or only on certain components, and this is what they have done. Although Delphi's architecture defines that the lowest object-form capable of streaming is TPersistent, someone at Borland registered the property editor to work only on objects that descend from TComponent. An oversight I am sure, but that doesn't really help us at all.

The answer to this problem is to descend our TExpandableRecord from TComponent instead of TPersistent, then the property editor will be invoked. The problem is that Delphi's default property editor for properties of type TComponent (and descendents) shows a combobox rather than showing an expandable view of sub-properties.

The whole solution to this dilemma lies in property editors, and will be covered in the final part of this series.

First page > Component references > Page 1, 2, 3, 4, 5

Creating Custom Delphi Components >>
>> Part III.

All graphics (if any) in this feature created by Peter Morris.

 More Delphi
· Learn another routine every day - RTL Quick Reference.
· Download free source code applications and components.
· Talk about Delphi Programming, real time.
· Link to the Delphi Programming site from your Web pages.
· Tutorials, articles, tech. tips by date: 2001|2000|1999|1998 or by TOPIC.
· NEXT ARTICLE: Lookup! - DB/15.
Chapter fifteen of the free Delphi Database Course for beginners. See how to use lookup fields in Delphi to achieve faster, better and safer data editing. Also, find how to create a new field for a dataset and discuss some of the key lookup properties. Plus, take a look at how to place a combo box inside a DBGrid.
 Stay informed with all new and interesting things about Delphi (for free).
Subscribe to the Newsletter
Name
Email

 Got some code to share? Got a question? Need some help?

©2014 About.com. All rights reserved.