Implementing Custom Property Editor
- 4 minutes to read
The Delphi IDE provides an Object Inspector to edit the values of published properties at design time for the components placed on a form.
The purpose of the cxRTTIInspector control is the same as for the IDE Object Inspector but for runtime.
To bind the cxRTTIInspector runtime Object Inspector to the desired component at runtime use the TcxRTTIInspector.InspectedObject property as shown in the code snippet below:
//...
RTTIInspector.InspectedObject := DBVerticalGrid;
When applied the user can browse and edit the values of the published properties of the bound component at runtime:
The runtime Object Inspector enables the editing of properties values as defined by the TcxPropertyEditor descendants for the appropriate types of properties. You can provide your own alternate editor for specific properties by implementing and registering the TcxPropertyEditor descendant.
The example below shows how to implement a combo box editor for selecting the image index in the row header. This example is based on the ExpressVerticalGrid RTTIInspector Demo shipped with this product.
First define the image index editor represented by the TcxImageIndexProperty class, which is derived from the TcxPropertyEditor family. Override the following inherited methods to provide the proper behavior:
AdjustInnerEditProperties – this is a protected method of the TcxPropertyEditor class and is responsible for customizing the editing properties;
SetValue specifies the edited property value.
Define a new virtual method GetImages to provide the image list of the bound component, this method can be overridden by TcxImageIndexProperty descendants.
Register the TcxImageIndexProperty editor with the cxRegisterPropertyEditor and cxRegisterEditPropertiesClass procedures. The former procedure creates association between the property editor, the property type and the latter creates association between the property editor and the editor properties.
unit ImageIndexPropertyEditor;
interface
uses cxOI, cxEdit, ImgList, cxImageComboBox, Controls;
type
TcxImageIndexProperty = class(TcxIntegerProperty)
public
procedure AdjustInnerEditProperties(AProperties: TcxCustomEditProperties); override;
// override GetImages function in descendant to provide another image list
function GetImages: TCustomImageList; virtual;
procedure SetValue(const Value: string); override;
end;
implementation
uses SysUtils, cxVGrid;
resourcestring
sNoImage = 'no image';
{ TcxImageIndexProperty }
procedure TcxImageIndexProperty.AdjustInnerEditProperties(
AProperties: TcxCustomEditProperties);
var
i: Integer;
AComboBoxItem: TcxImageComboBoxItem;
AImageComboBoxProperties: TcxImageComboBoxProperties;
AImages: TCustomImageList;
procedure AssignImages;
begin
if AImages.Height = 16 then
AImageComboBoxProperties.Images := AImages
else
AImageComboBoxProperties.LargeImages := AImages
end;
function GetImageList: TCustomImageList;
begin
if AImageComboBoxProperties.Images <> nil then
Result := AImageComboBoxProperties.Images
else
Result := AImageComboBoxProperties.LargeImages;
end;
begin
AImageComboBoxProperties := AProperties as TcxImageComboBoxProperties;
AImages := GetImages;
if AImages <> nil then
begin
AssignImages;
AImageComboBoxProperties.Items.Clear;
for i:= 0 to GetImageList.Count - 1 do
begin
AComboBoxItem := AImageComboBoxProperties.Items.Add as TcxImageComboBoxItem;
AComboBoxItem.ImageIndex := i;
AComboBoxItem.Value := i;
AComboBoxItem.Description := IntToStr(i);
end;
end;
AComboBoxItem := AImageComboBoxProperties.Items.Add as TcxImageComboBoxItem;
AComboBoxItem.ImageIndex := -1;
AComboBoxItem.Value := Integer(-1);
AComboBoxItem.Description := sNoImage;
end;
procedure TcxImageIndexProperty.SetValue(const Value: string);
begin
if Value = sNoImage then
SetOrdValue(-1)
else inherited SetValue(Value);
end;
function TcxImageIndexProperty.GetImages: TCustomImageList;
begin
Result := nil;
if GetComponent(0) is TcxCaptionRowProperties then
// it assumes the image list is bound at design time
Result := TcxCaptionRowProperties (GetComponent(0)).Row.VerticalGrid.Images;
end;
initialization
cxRegisterPropertyEditor(TypeInfo(TImageIndex), TcxCaptionRowProperties, 'ImageIndex', TcxImageIndexProperty); cxRegisterEditPropertiesClass(TcxImageIndexProperty, TcxImageComboBoxProperties);
end.
Now there is the main unit where the TcxDBVerticalGrid and TcxRTTIInspector objects are defined. Two rows – the PerformanceAttributes category row and the HP editor row – will be the inspected components in this application.
Just one method, the DBVerticalGridClick, is defined. It binds a row that gets focus to the runtime Object Inspector.
unit RTTIInspectorDemoMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, cxStyles, cxGraphics, cxEdit, cxVGrid, cxOI, cxControls, cxInplaceContainer, cxDBVGrid, DB, ADODB, ImgList, cxClasses, StdCtrls, cxDropDownEdit, cxContainer, cxTextEdit, cxMaskEdit, cxImageComboBox;
type
TRTTIInspectorForm = class(TForm)
DBVerticalGrid: TcxDBVerticalGrid;
RTTIInspector: TcxRTTIInspector;
DataSource: TDataSource;
Cars: TADOTable;
ImageList: TCustomImageList;
PerformanceAttributes: TcxCategoryRow;
HP: TcxDBEditorRow;
procedure DBVerticalGridClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
RTTIInspectorForm: TRTTIInspectorForm;
implementation
{$R *.dfm}
// using the Object Inspector assign this method at design time to the DBVerticalGrid OnClick event as an event handler
procedure TRTTIInspectorForm.DBVerticalGridClick(Sender: TObject);
begin
RTTIInspector.InspectedObject := DBVerticalGrid.FocusedRow;
end;
end.
Here is the output for the code listed above: