Search Results for

    Show / Hide Table of Contents

    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 to TfrxComponent 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 in GetValue 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 via OnDrawLBItem 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);
    
    Back to top © 1998-2022 Copyright Fast Reports Inc.