Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
How can to create a simple property editor Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 2.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Lou Adler 

How can I create a simple property editor?


This is an introductory level article about creating a simple property editor that 
I hope will get you started. I'll provide enough information about the DSGNINTF.PAS 
(Design interface that holds the TPropertyEditor class) file so you can finish the 
article with a sense of that you are able to create a property editor.


The following is a cut and paste of the TPropertyEditor class declaration found in 
2     TPropertyEditor = class
3   private
4     FDesigner: TFormDesigner;
5     FPropList: PInstPropList;
6     FPropCount: Integer;
7     constructor Create(ADesigner: TFormDesigner; APropCount: Integer);
8     function GetPrivateDirectory: string;
9     procedure SetPropEntry(Index: Integer; AInstance: TComponent;
10      APropInfo: PPropInfo);
11  protected
12    function GetPropInfo: PPropInfo;
13    function GetFloatValue: Extended;
14    function GetFloatValueAt(Index: Integer): Extended;
15    function GetMethodValue: TMethod;
16    function GetMethodValueAt(Index: Integer): TMethod;
17    function GetOrdValue: Longint;
18    function GetOrdValueAt(Index: Integer): Longint;
19    function GetStrValue: string;
20    function GetStrValueAt(Index: Integer): string;
21    function GetVarValue: Variant;
22    function GetVarValueAt(Index: Integer): Variant;
23    procedure Modified;
24    procedure SetFloatValue(Value: Extended);
25    procedure SetMethodValue(const Value: TMethod);
26    procedure SetOrdValue(Value: Longint);
27    procedure SetStrValue(const Value: string);
28    procedure SetVarValue(const Value: Variant);
29  public
30    destructor Destroy; override;
31    procedure Activate; virtual;
32    function AllEqual: Boolean; virtual;
33    procedure Edit; virtual;
34    function GetAttributes: TPropertyAttributes; virtual;
35    function GetComponent(Index: Integer): TComponent;
36    function GetEditLimit: Integer; virtual;
37    function GetName: string; virtual;
38    procedure GetProperties(Proc: TGetPropEditProc); virtual;
39    function GetPropType: PTypeInfo;
40    function GetValue: string; virtual;
41    procedure GetValues(Proc: TGetStrProc); virtual;
42    procedure Initialize; virtual;
43    procedure Revert;
44    procedure SetValue(const Value: string); virtual;
45    function ValueAvailable: Boolean;
46    property Designer: TFormDesigner read FDesigner;
47    property PrivateDirectory: string read GetPrivateDirectory;
48    property PropCount: Integer read FPropCount;
49    property Value: string read GetValue write SetValue;
50  end;

Whew! that's a lot of stuff, isn't it? Add to the fact that the Tools API is poorly 
documented, and you've got a lot confusion to deal with. There's a little relief in 
the Delphi 2.0 help file, but the way it's organized can leave you with the sinking 
feeling that you're in way over your head. After you've done a few property 
editors, it's really not that hard.

The Trick to Writing Property Editors

One of the biggest problems with technical documentation is that it's technical. 
Not much conceptual material is ever covered in tech specs or tech manuals. This 
leaves it up to the programmer to extrapolate the underlying concepts. I'm of the 
opinion that if something has the possibility of being a widely used feature, you 
should cover not only the technical specifications, but the conceptual points as 
well. Gaining conceptual understanding is the real trick to creating property.

The trick to writing property editors is understanding the virtual methods and what 
they do. Writing your own custom property editors is all about overriding the 
proper methods of the TPropertyEditor class to get the functionality out of 
property editor that you require. Granted, there are a lot of very complex property 
editors out there. But whether simple or complex, they're all built in a similar 
fashion: they override default functionality of TPropertyEditor.

Once you let this concept sink in, and as you gain more experience in building 
components, writing a property editor merely becomes the task of overriding the 
appropriate methods to get your job done.

Furthermore, the DSGNINFT.PAS is thoroughly commented. When constructing components 
that will have property editors, make sure that this file is open in the editor so 
you can refer to the documentation covering the virtual methods you will be 
overriding. As an aside, if you do BDE programming, having the BDE.INT (Delphi 2.0) 
or DBIPROCS.INT, DBITYPES.INT, DBIERRS.INT (Delphi 1.0) is essential to successful 
BDE programming

The Value List: the Simplest Type of Property Editor

Properties that display value lists are common to components. In fact, you see them 
all the time. For instance, a value list property that everyone has used is the 
Align property.

Value lists are simple enumerated types, which are merely a collection of 
sequentially ordered elements in a list. The first item has an ordinal value of 0, 
the second 1, and so forth. Enumerated types are useful in communicating with the 
user using a set of named choices rather than ordinal or numeric choices. For 
instance, the Align element alBottom is much easier to understand than '0,' which 
is its ordinal value in the list. In this case, the ordinal value has no clear 
conceptual context.

To create a property editor that presents a value list to a user in the object 
inspector is very simple and requires only a few steps. Here is a brief synopsis of 
what you have to do before we go into detail:

First, define and declare your enumerated type under a new type section.
Under the enumerated type declaration, declare your class, including the functions 
you will be overriding in your code.
Write your code in the implementation section of the unit.

Sounds pretty simple, right? It is. So let's go and create one now, then we'll 
discuss it in detail below.

...other code

51  interface
53  type
54    TEnumMonth = (emJan, emFeb, emMar,
55      emApr, emMay, emJun,
56      emJul, emAug, emSep,
57      emOct, emNov, emDec);
59    TEnumMonths = class(TEnumProperty)
60    public
61      function GetAttributes: TPropertyAttributes; override;
62      function AllEqual: Boolean; override;
63    end;
65  implementation
67  ...other code
69  function TEnumMonths.AllEqual: Boolean;
70  begin
71    Result := True;
72  end;
74  function TEnumMonths.GetAttributes: TPropertyAttributes;
75  begin
76    Result := [paMultiSelect, paValueList];
77  end;
79  procedure register;
80  begin
81    RegisterPropertyEditor(TypeInfo(TEnumMonth), TPSIBaseExt, 'RangeBegin',
82      TEnumMonths);
83    RegisterPropertyEditor(TypeInfo(TEnumMonth), TPSIBaseExt, 'RangeEnd', 
84  TEnumMonths);
85  end;

The property editor listed above was created to serve a singular purpose: Allow the 
user to select a specific month from a list of months, rather than typing in a 
month value code himself (which is more work than the user needs and is also prone 
to spelling errors).

This component modernizes a cumbersome style of interaction in existing 
applications. In these applications users were required to enter month ranges as a 
six-digit string beginning with current two-digit year plus the four digit 
month/day combination (eg., YYMMDD). Past experience said that runtime errors or 
empty result sets from queries that used the range values were usually the result 
of mistyping. So the property editor was created to let the user pick a month for 
both the starting month and ending month of the range of values they wanted to 
extract. This explains why in the code above I registered the property editor for 
both the RangeBegin and RangeEnd properties.

Elsewhere in the component, I have created an array type of type String and created 
two arrays representing the starting month and ending month code values, 

Here's the array type declaration:

86  type
87    TMonthRng = array[0..11] of string;
88    ....
90  Here are the declarations and initializations of the arrays themselves:
92  var
93    stmonArr,
94      enmonArr: TMonthRng;
95  begin
96    stmonArr[0] := '0101';
97    stmonArr[1] := '0201';
98    stmonArr[2] := '0301';
99    stmonArr[3] := '0401';
100   stmonArr[4] := '0501';
101   stmonArr[5] := '0601';
102   stmonArr[6] := '0701';
103   stmonArr[7] := '0801';
104   stmonArr[8] := '0901';
105   stmonArr[9] := '1001';
106   stmonArr[10] := '1101';
107   stmonArr[11] := '1201';
108   enmonArr[0] := '0131';
109   enmonArr[1] := '0229';
110   enmonArr[2] := '0331';
111   enmonArr[3] := '0430';
112   enmonArr[4] := '0531';
113   enmonArr[5] := '0630';
114   enmonArr[6] := '0731';
115   enmonArr[7] := '0831';
116   enmonArr[8] := '0930';
117   enmonArr[9] := '1031';
118   enmonArr[10] := '1130';
119   enmonArr[11] := '1231';
121   ...

By doing things in this manner, one can easily get the appropriate value needed by 
passing the ordinal value of the appropriate enumerated type as an index of an 
element in the array. For example, let's say the user chose emApr as his/her 
starting month. The ordinal value of emApr is 3. Referencing that value in the 
stmonArr array would produce the string '0401.' What I've essentially done here is 
eliminate the need for the user to do anything more than choose an appropriate 
month to start with. The proper code is handled by the program. Here's some sample 
code that demonstrates how it's done:
123 procedure ReturnMonthCode(Index: Integer; StartMonth: Boolean): string;
124 var
125   stmonArr,
126     enmonArr: TMonthRng;
127 begin
128   stmonArr[0] := '0101';
129   stmonArr[1] := '0201';
130   stmonArr[2] := '0301';
131   stmonArr[3] := '0401';
132   stmonArr[4] := '0501';
133   stmonArr[5] := '0601';
134   stmonArr[6] := '0701';
135   stmonArr[7] := '0801';
136   stmonArr[8] := '0901';
137   stmonArr[9] := '1001';
138   stmonArr[10] := '1101';
139   stmonArr[11] := '1201';
141   enmonArr[0] := '0131';
142   enmonArr[1] := '0229';
143   enmonArr[2] := '0331';
144   enmonArr[3] := '0430';
145   enmonArr[4] := '0531';
146   enmonArr[5] := '0630';
147   enmonArr[6] := '0731';
148   enmonArr[7] := '0831';
149   enmonArr[8] := '0930';
150   enmonArr[9] := '1031';
151   enmonArr[10] := '1130';
152   enmonArr[11] := '1231';
154   if StartMonth then
155     Result := stMonArr[Index]
156   else
157     Result := enMonArr[Index];
158 end;
160 //To actually use ReturnMonthCode all we do is the following:
162 var
163   S: string;
164 begin
166   S := ReturnMonthCode(Ord(RangeBegin), True);

Remember, RangeBegin is a property of type TEnumArray. Therefore, to access its 
ordinal value, all we need do is apply the Ord function to it.

Based on the information above, you should be able to create at the very least a simple property editor like the example above. For more complex property editors, you will have to override more of the methods; but remember, don't be daunted by the code. The trick is overriding the default methods with your own.

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC