Search code examples
delphicomponentsvcl

Delphi component design, add a field in TFieldDataLink


I am making a new vcl component, which is kinda finished except for one small detail.

The component uses TFieldDataLink to make a Datasource and Datafield property, this is working just fine, the problem is i need a second field property visible in the object inspector, from the same Datasource as the DataField.

I don't really need a 'TFields' property where you can add multiple fields, one additional FieldProperty is enough.

I added a simple string property 'DataTypeField': String in the published section of my class, which works fine, but you have to type the value in the object inspector in design time and it would of course be better if the property would let the user pick the field from the Datalink.

Does anyone have an idea how to do that?

Basicly my class looks like this (i have removed lots of other irrelevant properties and functions)

TFlexRtfEditor = class(TcxGroupBox)
  Editor: TdxRichEditControl;
private
  FDataLink: TFieldDataLink;
  function GetDataField: string;
  function GetDataSource: TdataSource;
  procedure SetDataField(const Value: string);
  procedure SetDataSource(const Value: TdataSource);
protected
public
published
  property DataField: string read GetDataField write SetDataField;
  property DataSource: TdataSource read GetDataSource write SetDataSource;
end;

in constructor FDataLink := TFieldDataLink.Create; FDataLink.Control := Self;

in destructor FDataLink.Free;

I figured if i needed a second field i would need to make a new class, on type TFieldDataLink, add the property DataTypeField in the published section and use this class to create the FDatalink property in the constructor

TFlexRtfEditorDataLink = Class(TFieldDataLink)
private
   fDataTypeField: String;
   procedure SetDataTypeField(const Value: String);
published
   property DataTypeField: String read fDataTypeField write SetDataTypeField;
End; 

FDataLink := TFlexRtfEditorDataLink.Create;
FDataLink.Control := Self;

But although this compiles it doesn't make the DataTypeField property available in the object inspector.

Does anyone know what i'm doing wrong? Is this because the FDataLink variable is in the private section of the base class?


Solution

  • If you want your alternate DataField to show up in the OI and select its value from the drop-down list of fields you would usually get in the DataField property of a TDBEdit, this is fairly easy to do, but you need to register a property editor for it in the IDE for it to work. Also, I think there is an easier way to do it than deriving a TFieldDataLink descendant.

    I'm afraid I found your terminology and description of what you are trying to do a bit confusing, so in the following, I'm going to refer to this alternate DataField as AltDataField to avoid confusion. I'm also going to show how to add a MainDataField that works as an alias for the DataField property.

    In the main component code:

    type
        TFlexRtfEditor = class(TcxGroupBox)
          //Editor: TdxRichEditControl; omitted for simplicity
        private
          FDataLink: TFieldDataLink;
          FAltDataLink: TFieldDataLink;
          function GetMainDataField: string;
          function GetDataSource: TdataSource;
          procedure SetMainDataField(const Value: string);
          procedure SetDataSource(const Value: TdataSource);
        function GetAltDataField: String;
        procedure SetAltDataField(const Value: String);
        protected
        public
          constructor Create(AOwner : TComponent); override;
          destructor Destroy; override;
        published
          property MainDataField: string read GetMainDataField write SetMainDataField;  // a quasi-alias for DataField
          property DataField: String read GetMainDataField write SetMainDataField;  //  the usual DB-aware component's DataField
          property AltDataField: String read GetAltDataField write SetAltDataField;
          property DataSource: TdataSource read GetDataSource write SetDataSource;
        end;
    
    implementation
    
    constructor TFlexRtfEditor.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      FDataLink := TFieldDataLink.Create;
      FAltDataLink := TFieldDataLink.Create;
    end;
    
    destructor TFlexRtfEditor.Destroy;
    begin
      FAltDataLink.Free;
      FDataLink.Free;
      inherited;
    end;
    
    function TFlexRtfEditor.GetMainDataField: string;
    begin
      Result := FDataLink.FieldName;
    end;
    
    function TFlexRtfEditor.GetDataSource: TdataSource;
    begin
      Result := FDataLink.DataSource;
    end;
    
    procedure TFlexRtfEditor.SetMainDataField(const Value: string);
    begin
      FDataLink.FieldName := Value; //FDataLink.DataSource.DataSet.FindField(Value);
    end;
    
    procedure TFlexRtfEditor.SetDataSource(const Value: TdataSource);
    begin
      FDataLink.DataSource := Value;
      FAltDataLink.DataSource := Value;
    end;
    
    function TFlexRtfEditor.GetAltDataField: String;
    begin
      Result := FAltDataLink.FieldName;
    end;
    
    procedure TFlexRtfEditor.SetAltDataField(const Value: String);
    begin
      FAltDataLink.FieldName := Value;
    end;
    

    As you can see, the AltDataField is provided with its own FieldDataLink, which is far simpler than having to to derive (and debug) your own TFieldDataLink-descendant.

    In your component registration unit:

    interface
    
    uses SysUtils, Classes, DesignIntf, DesignEditors, DSDesign, DBReg, FlexRTFu;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
      RegisterComponents('MA', [TFlexRtfEditor]);
      RegisterPropertyEditor(TypeInfo(string), TComponent, 'MainDataField', TDataFieldProperty);
      RegisterPropertyEditor(TypeInfo(string), TComponent, 'AltDataField', TDataFieldProperty);
    end;
    
    end.
    

    Btw, you might want to look at the source code of TDBEdit for some extra details of how the component's properties interact with its FieldDataLink.

    Also btw, my package's Requires clause looks like this:

    requires
      rtl,
      vcl,
      dbrtl,
      vcldb,
      cxLibraryD7,
      dxThemeD7,
      dxCoreD7,
      dxGDIPlusD7,
      vclx,
      cxEditorsD7,
      cxDataD7,
      vcljpg,
      dxSkinBlackD7,
      dxSkinsCoreD7,
      dxSkinBlueD7,
      dxSkinBlueprintD7,
      dxSkinCaramelD7,
      dxSkinCoffeeD7,
      dxSkinDarkRoomD7,
      dxSkinDarkSideD7,
      dxSkinDevExpressDarkStyleD7,
      dxSkinDevExpressStyleD7,
      dxSkinFoggyD7,
      dxSkinGlassOceansD7,
      dxSkinHighContrastD7,
      dxSkiniMaginaryD7,
      dxSkinLilianD7,
      dxSkinLiquidSkyD7,
      dxSkinLondonLiquidSkyD7,
      dxSkinMcSkinD7,
      dxSkinMetropolisD7,
      dxSkinMetropolisDarkD7,
      dxSkinMoneyTwinsD7,
      dxSkinOffice2007BlackD7,
      dxSkinOffice2007BlueD7,
      dxSkinOffice2007GreenD7,
      dxSkinOffice2007PinkD7,
      dxSkinOffice2007SilverD7,
      dxSkinOffice2010BlackD7,
      dxSkinOffice2010BlueD7,
      dxSkinOffice2010SilverD7,
      dxSkinOffice2013DarkGrayD7,
      dxSkinOffice2013LightGrayD7,
      dxSkinOffice2013WhiteD7,
      dxSkinPumpkinD7,
      dxSkinSevenClassicD7,
      dxSkinSevenD7,
      dxSkinSharpD7,
      dxSkinSharpPlusD7,
      dxSkinSilverD7,
      dxSkinSpringTimeD7,
      dxSkinStardustD7,
      dxSkinSummer2008D7,
      dxSkinTheAsphaltWorldD7,
      dxSkinValentineD7,
      dxSkinVS2010D7,
      dxSkinWhiteprintD7,
      dxSkinXmas2008BlueD7,
      designide,
      vclactnband,
      dcldb,
      vcldesigner,
      designdgm,
      dclstd;