Property Editor Writing
When you select a component in the designer, its properties are displayed in object inspector. You can create your own editor for any property. “Font” property standard editor can exemplify that: if this property is selected, the ...
button appears in right part of line; call standard "font properties" dialogue box by clicking this button. One more example is “Color” property editor. It shows standard colors and color specimens names in drop-down list.
Base class for all property editors is described in “frxDsgnIntf” unit:
TfrxPropertyEditor = class(TObject)
protected
procedure GetStrProc(const s: String);
function GetFloatValue: Extended;
function GetOrdValue: Integer;
function GetStrValue: String;
function GetVarValue: Variant;
procedure SetFloatValue(Value: Extended);
procedure SetOrdValue(Value: Integer);
procedure SetStrValue(const Value: String);
procedure SetVarValue(Value: Variant);
public
constructor Create(Designer: TfrxCustomDesigner); virtual;
destructor Destroy; override;
function Edit: Boolean; virtual;
function GetAttributes: TfrxPropertyAttributes; virtual;
function GetExtraLBSize: Integer; virtual;
function GetValue: String; virtual;
procedure GetValues; virtual;
procedure SetValue(const Value: String); virtual;
procedure OnDrawLBItem(Control: TWinControl; Index: Integer; ARect: TRect; State: TOwnerDrawState); virtual;
procedure OnDrawItem(Canvas: TCanvas; ARect: TRect); virtual;
property Component: TPersistent readonly;
property frComponent: TfrxComponent readonly;
property Designer: TfrxCustomDesigner readonly;
property ItemHeight: Integer;
property PropInfo: PPropInfo readonly;
property Value: String;
property Values: TStrings readonly;
end;
You also can inherit from any of the following classes which themselves realize some basic functionality for working with properties of corresponding types:
TfrxIntegerProperty = class(TfrxPropertyEditor)
TfrxFloatProperty = class(TfrxPropertyEditor)
TfrxCharProperty = class(TfrxPropertyEditor)
TfrxStringProperty = class(TfrxPropertyEditor)
TfrxEnumProperty = class(TfrxPropertyEditor)
TfrxClassProperty = class(TfrxPropertyEditor)
TfrxComponentProperty = class(TfrxPropertyEditor)
Several properties are defined in this class:
Component
- link to parent component (not to property itself!), to which the given property belongs;frComponent
- the same, but casted toTfrxComponent
type (for convenience in some cases);Designer
– link to report designer;ItemHeight
- item height, in which property is displayed. It can be useful in OnDrawXXX;PropInfo
- link to PPropInfo structure, which contains information about edited property;Value
- property value displayed as string;Values
- list of values. This property is to be filled inGetValue
method, if “paValueList” attribute is defined (see below).
The following methods are service ones. They can be used to get or set edited property value.
function GetFloatValue: Extended;
function GetOrdValue: Integer;
function GetStrValue: String;
function GetVarValue: Variant;
procedure SetFloatValue(Value: Extended);
procedure SetOrdValue(Value: Integer);
procedure SetStrValue(const Value: String);
procedure SetVarValue(Value: Variant);
You should use methods, which correspond to property type. Thus, use GetOrdValue
and SetOrdValue
methods, if property is of “Integer” type. These methods are also used for working with property of TObject
type, since such property contains 32-bit object address. In this case, it is sufficient to do cast of the following type, for example:
MyFont := TFont(GetOrdValue)
To create your own editor, it is necessary to inherit from basic class and override one or several methods defined in public section (this depends on property type and functionality you wish to realize). One of methods you surely have to override is GetAttributes
method. This method is to return set of property attributes. Attributes are defined in the following way:
TfrxPropertyAttribute = (paValueList, paSortList, paDialog, paMultiSelect, paSubProperties, paReadOnly, paOwnerDraw);
TfrxPropertyAttributes = set of TfrxPropertyAttribute;
Attribute assignment is achieved as follows:
paValueList - property represents dropping down list of values. (This function is exemplified in “Color” property). If this attribute is present,
GetValues
method should be overridden;paSortList - sorts list elements. It is used together with paValueList;
paDialog - property has editor. If this attribute is present, the
...
button is displayed in the right part of editing line. Edit method is called on by clicking on it;paMultiSelect – allows given property editing in some objects selected at the same time. Some properties (such as “Name”, etc) do not have this attribute;
paSubProperties - property is object of
TPersistent
type and has its own properties, which are also should be displayed. (This function is exemplified in “Font” property);paReadOnly - it is impossible to modify value in editor line. Some properties, being “Class” or “Set” types, possess this attribute;
paOwnerDraw - property value drawing is performed via
OnDrawItem
method. If “paValueList” attribute is defined, then drop-down list drawing is performed viaOnDrawLBItem
method.
Edit method is called in two cases: either by selecting property, by double-clicking its value, or (if property has paDialog attribute) by clicking the ...
button. This method should return “True,” if property value was modified.
GetValue
method should return property value as string (it will be displayed in object inspector). If you inherit from TfrxPropertyEditor
basic class, it is necessary to override this method.
SetValue
method is to set property value transferred as string. If you inherit from TfrxPropertyEditor
basic class, it is necessary to override this method.
GetValues
method should be overridden in case you defined “paValueList” attribute. This method should fill Values
property with values.
The following three methods allow performing manual property value drawing (Color property editor works in the same way). These methods are called, if you define “paOwnerDraw” attribute.
OnDrawItem
method is called when drawing property value in object inspector (when property is not selected; otherwise its value is simply displayed in editing line). For example, Color property editor draws rectangle, filled with color according to value, to the left of property value.
GetExtraLBSize
method is called in case you defined “paValueList” attribute. This method returns number of pixels, by which “Drop-Down List” width should be adjusted in order to find room for displayed picture. By default, this method returns value corresponding to cell height for property enveloping. If you need to deduce picture, with width larger than its height, the given method should be overridden.
OnDrawLBItem
method is called when drawing string in drop-down list, if you defined paValueList attribute. In fact, this method is TListBox.OnDrawItem
event handler and has the same set of parameters.
Property editor registration is performed via procedure described in frxDsgnIntf file:
procedure frxPropertyEditors.Register(PropertyType: PTypeInfo; ComponentClass: TClass; const PropertyName: String; EditorClass: TfrxPropertyEditorClass);
PropertyType - information about property type, transferred via “TypeInfo” system function, for example TypeInfo(String);
ComponentClass – component name, with property you want to edit (may be nil);
PropertyName - name of property you want to edit (may be blank string);
EditorClass - property editor name
It is necessary to specify “PropertyType” parameter only. “ComponentClass” and/or “PropertyName” parameters may be blank. This allows to register editor either to any property of PropertyType type, to any property of concrete ComponentClass components and its successors, or to PropertyName concrete property of concrete component (or any component, if ComponentClass parameter is blank).
Let us examine three property editors examples. Editor code, according to FastReport requirements, can be placed in a file having the same name as file with code of the component, and adding the Editor suffix.
{ TFont property editor displays editor button(...) }
{ inherit from ClassProperty }
type
TfrxFontProperty = class(TfrxClassProperty)
public
function Edit: Boolean; override;
function GetAttributes: TfrxPropertyAttributes; override;
end;
function TfrxFontProperty.GetAttributes: TfrxPropertyAttributes;
begin
{ property has nested properties and editor. It cannot be edited manually }
Result := [paMultiSelect, paDialog, paSubProperties, paReadOnly];
end;
function TfrxFontProperty.Edit: Boolean;
var
FontDialog: TFontDialog;
begin
{ create standard dialogue }
FontDialog := TFontDialog.Create(Application);
try
{ take property value }
FontDialog.Font := TFont(GetOrdValue);
FontDialog.Options := FontDialog.Options + [fdForceFontExist];
{ display dialogue }
Result := FontDialog.Execute;
{ bind new value }
if Result then
SetOrdValue(Integer(FontDialog.Font));
finally
FontDialog.Free;
end;
end;
{ registration }
frxPropertyEditors.Register(TypeInfo(TFont), nil, '', TfrxFontProperty);
{ TFont.Name property editor displays drop-down list of available fonts }
{ inherit from StringProperty, as property is of string type }
type
TfrxFontNameProperty = class(TfrxStringProperty)
public
function GetAttributes: TfrxPropertyAttributes; override;
procedure GetValues; override;
end;
function TfrxFontNameProperty.GetAttributes: TfrxPropertyAttributes;
begin
Result := [paMultiSelect, paValueList];
end;
procedure TfrxFontNameProperty.GetValues;
begin
Values.Assign(Screen.Fonts);
end;
{ registration }
frxPropertyEditors.Register(TypeInfo(String), TFont, 'Name', TfrxFontNameProperty);
{ TPen.Style property editor displays picture, which is pattern of selected style }
type
TfrxPenStyleProperty = class(TfrxEnumProperty)
public
function GetAttributes: TfrxPropertyAttributes; override;
function GetExtraLBSize: Integer; override;
procedure OnDrawLBItem(Control: TWinControl; Index: Integer;
ARect: TRect; State: TOwnerDrawState); override;
procedure OnDrawItem(Canvas: TCanvas; ARect: TRect); override;
end;
function TfrxPenStyleProperty.GetAttributes: TfrxPropertyAttributes;
begin
Result := [paMultiSelect, paValueList, paOwnerDraw];
end;
{ method draws thick horizontal line with selected style }
procedure HLine(Canvas: TCanvas; X, Y, DX: Integer);
var
i: Integer;
begin
with Canvas do
begin
Pen.Color := clBlack;
for i := 0 to 1 do
begin
MoveTo(X, Y - 1 + i);
LineTo(X + DX, Y - 1 + i);
end;
end;
end;
{ drawing drop-down list }
procedure TfrxPenStyleProperty.OnDrawLBItem(Control: TWinControl; Index: Integer; ARect: TRect; State: TOwnerDrawState);
begin
with TListBox(Control), TListBox(Control).Canvas do
begin
FillRect(ARect);
TextOut(ARect.Left + 40, ARect.Top + 1, TListBox(Control).Items[Index]);
Pen.Color := clGray;
Brush.Color := clWhite;
Rectangle(ARect.Left + 2, ARect.Top + 2, ARect.Left + 36, ARect.Bottom - 2);
Pen.Style := TPenStyle(Index);
HLine(TListBox(Control).Canvas, ARect.Left + 3, ARect.Top + (ARect.Bottom - ARect.Top) div 2, 32);
Pen.Style := psSolid;
end;
end;
{ drawing property value }
procedure TfrxPenStyleProperty.OnDrawItem(Canvas: TCanvas; ARect: TRect);
begin
with Canvas do
begin
TextOut(ARect.Left + 38, ARect.Top, Value);
Pen.Color := clGray;
Brush.Color := clWhite;
Rectangle(ARect.Left, ARect.Top + 1, ARect.Left + 34, ARect.Bottom - 4);
Pen.Color := clBlack;
Pen.Style := TPenStyle(GetOrdValue);
HLine(Canvas, ARect.Left + 1, ARect.Top + (ARect.Bottom - ARect.Top) div 2 - 1, 32);
Pen.Style := psSolid;
end;
end;
{ return picture width }
function TfrxPenStyleProperty.GetExtraLBSize: Integer;
begin
Result := 36;
end;
{ registration }
frxPropertyEditors.Register(TypeInfo(TPenStyle), TPen, 'Style', TfrxPenStyleProperty);