Jump to content
msd

Extend Standard VCL

Recommended Posts

Hello Developers,

 

Is it possible to extend the standard VCL component with some features (properties) without creating a new component?

For example, I need to add two string properties to the CheckBox component (for auto-saving in an inifile, I need two value properties and a group name), and I think about a new VCL. Does someone have a better idea?

 

Thanks in advance...

Share this post


Link to post

You cannot add a property without a new class. If you don't want to extend the component you can store the additional information in a dictionary or in other data structure.

  • Like 1

Share this post


Link to post

Hello,

 

Thanks for the reply.

 

Here is a closer explanation of one of my ideas.

I have a config form with a lot of checkboxes, which holds data in an ini file.

The ini file has sections and values.

So, I have a standard checkbox with two additional properties: one is IniSection, and the other is IniProperty OK.

I would like to solve this without new components, but I need to keep this scenario: when I call save or load config dara method and I search iniFile by those 2 new properties from the checkbox,.

 

I think that now it is a little bit clearer what I want to do with my additional properties.

 

Thanks again for all the ideas...

Share this post


Link to post

Hi,

7 minutes ago, msd said:

I have a config form with a lot of checkboxes, which holds data in an ini file.

The ini file has sections and values.

So, I have a standard checkbox with two additional properties: one is IniSection, and the other is IniProperty OK.

I would like to solve this without new components, but I need to keep this scenario: when I call save or load config dara method and I search iniFile by those 2 new properties from the checkbox,.

Do it stupid simple, do you need it to be used or modified by users manually ?!!   most likely no..

So, even for both cases (Yes/No) :

Name these CheckBoxes with useful and clear names, then use the their names as Ini file Value while use for the Ini file Sections the Form/Frame name ...

Don't forget to handle missed value with notification or errors or skip silently.... ,

For saving (or loading)enumerate all the controls in the form/frame and utilize the tag property to indicate the need to be saved/stored in Ini file or not.

 

Just an idea !

  • Like 1

Share this post


Link to post

Or if you want to go fancy, then build a class/component (a manager) that do that automatically, i mean enumerate all the controls and create an ini file with them, while it such in design time will enumerate all the controls and have a list at design time that can be checked to include/exclude from saving.

 

Building such component will give you huge experience while being fun.

  • Like 2

Share this post


Link to post

Hm, I remember writing something like this (many) years ago, but never actually used it. The code should be on sourceforge, I'll have to look it up.

 

Found it:

https://sourceforge.net/projects/dzconfig/

 

As I said: That was years ago, but maybe you will find it useful.

Edited by dummzeuch
  • Like 1

Share this post


Link to post
52 minutes ago, Kas Ob. said:

Hi,

Do it stupid simple, do you need it to be used or modified by users manually ?!!   most likely no..

So, even for both cases (Yes/No) :

Name these CheckBoxes with useful and clear names, then use the their names as Ini file Value while use for the Ini file Sections the Form/Frame name ...

Don't forget to handle missed value with notification or errors or skip silently.... ,

For saving (or loading)enumerate all the controls in the form/frame and utilize the tag property to indicate the need to be saved/stored in Ini file or not.

 

Just an idea !

Hello,

 

It is just like when I develop my config form, the CheckBox Hint is used for ItemName (because of the multiple same names in the config form), and the parent component name represents the ItemScetion, so this is my scenario now. But it is not only check boxes; there are also edit components (text, number, color, date, time, folder name, etc.).

This is the main reason why I need some modifications to the base class to make some maintainable code.🙂

I have a lot of cases in my apps with this, but for some cases, like DataSet, I need to create my class or component because I need at least 10 properties to add to make my development process simpler.

 

Thanks anyway; any comment is useful in some way.

Share this post


Link to post
12 minutes ago, msd said:

(text, number, color, date, time, folder name, etc.).

I have a lot of cases in my apps with this, but for some cases, like DataSet, I need to create my class or component because I need at least 10 properties to add to make my development process simpler.

So for such cases these is "case" in Pascal/Delphi

Case the control is CheckBox then the value is Boolean type

Case of TMemo then the value is Base64 Encoded text.

Case of something need more than one property then (like DataSet) MyDataSet.XX or MyDataSet_XX is TXX type 

.....

  • Like 1

Share this post


Link to post

How about a collection of check boxes  TCheckListBox 

 

The sample simply clears the checkboxes that were in the IDE and adds the ones that are in Array<string>

 

To extend you would change the Panel and Tstrings that the UI uses update the backend. Here the back end simply updates the UI when programs are used.

 

It uses a Ddd pattern Data driven pattern.  " Update the data early and often"   

 


class function TptrApps.HookInUI(inSG: TStringGrid; inLog: TStrings; inChBxs: TcheckListBox;
     inBanner: TPanel): TptrApps;
begin
  Result := nil;
  focusedApp := @cacheApp;
  var R := TptrApps.Create;

  R.ChBxs := inChBxs;
  R.ChBxs.OnClick := R.changeExesList;
  R.ChBxs.Items.Clear;
  for var I := Low(goodApps) to High(goodApps) do
    begin
      R.ChBxs.Items.add(goodApps[I]);
      R.ChBxs.Checked[I] := True;
      ...

const
  selfClass = 'FrmPat';

  GoodApps: Tarray<string> = [cEveryThing,'Shell_TrayWnd','Notepad', 'TAppBuilder', 'Window',
   'Chrome_WidgetWin_1', 'Notepad++', selfClass, 'TfmPeInformation' ];
      

 

 

  • Like 1

Share this post


Link to post

Programmers have a strange way of thinking that I don't understand. Because I'm not a pro-programmer. That's the case here.
I'm dealing with something similar. It is about setting values and appearance of individual components on a form. I'm using JSON to do this. That's a significant difference, because you're using IniFile. The principle is simple. The title consists of the names:
Owner (form) + Parent + Component

The result is e.g.:

 "frmsubMatchesByRoundfdnvDraw": {
  "jstpnlTeamVst": {
   "jstvstTeam": {
    "VTSearchDirection": 0,
    "Width": [
     120,165,100,84,167],
    "Position": [
     0,1,2,3,4],
    "SortColumn": -1,
    "VTSearchStart": 2,
    "ShowSortGlyphs": false,
    "FirstSetFocus": false,
    "VTSortDirection": 0
   }
  },
  ...
 },

To do this, functions of the type Read..., Write...

function TAppearance.DoCareNoEdit(const ACont: TControl): Boolean;
begin
  Result := True;

  if (ACont is TAdvOfficeRadioButton) then
    CareAdvOfficeRadioButton(TAdvOfficeRadioButton(ACont))
  else
  if (ACont is TjstVirtualStringTree) then
    CarejstVirtualStringTree(TjstVirtualStringTree(ACont))
  ...

procedure TAppearance.CarejstVirtualStringTree(const AComp: TjstVirtualStringTree);
begin
  AComp.ParentDoubleBuffered := True;
  FjstVst := AComp;
  FJsonItemName := oGlobVar.ActualForm.Name +'.'+ FjstVst.Parent.Name +'.'+ FjstVst.Name +'.';
  CreateVstPopupMenu;
  SetVstProperties;
end;

Hopefully this will help in some way. If I haven't hit the topic at all, then ignore it

  • Like 1

Share this post


Link to post

An easy way to introduce new methods and properties to components on a form is to use interposers:

type
  TCheckBox = class(StdCtrls.TCheckBox)
  private
    FGroup: string;
    FValue1: string;
    FValue2: string;
  public
    property Group: string read FGroup write FGroup;
    property Value1: string read FValue1 write FValue1;
    property Value2: string read FValue2 write FValue2;
  end;

Insert this in the interface section of your form unit. It must be before the declaration of the form.

You can also put it in a separate unit but then you need to ensure that that unit is referenced after StdCtrls.

 

Basically, this trick fools the Delphi DFM loader into creating an instance of your TCheckBox class instead of the standard one in StdCtrls.

 

Note that interposers only enables you to extend a component at run-time. The design-time environment (i.e. the Delphi IDE) knows nothing about this trickery so the new properties will not appear in the property inspector (which is also why I didn't bother declaring them as published). If you want design-time support you will have to create and register a custom component the normal way.

  • Like 1
  • Thanks 1

Share this post


Link to post
6 hours ago, msd said:

Is it possible to extend the standard VCL component with some features (properties) without creating a new component?

In a word, no.

 

However, if you intend to use the extended component in just one form/project, and don't mind setting the new properties in code at runtime rather than with the Object Inspector at design-time, then you could use an interposer class to add the properties, and that way you don't have to make the extra effort of putting the new component in a new package and installing it into the IDE.

6 hours ago, msd said:

For example, I need to add two string properties to the CheckBox component (for auto-saving in an inifile

For example:

unit MyUnit;

interface

uses
  ..., Vcl.Forms, Vcl.StdCtrls, System.IniFiles, ...;

type
  TCheckBox = class(Vcl.StdCtrls.TCheckBox)
  public
    IniSection: string;
    IniProperty: string; 
    procedure LoadFromIni(Ini: TCustomIniFile);
    procedure SaveToIni(Ini: TCustomIniFile);
  end;

  TMyForm = class(TForm)
    SomeCheckBox: TCheckBox;
    procedure FormCreate(Sender: TObject);
    ...
  private
    procedure LoadConfig;
    procedure SaveConfig;
  end;

...

implementation

...

procedure TCheckBox.LoadFromIni(Ini: TCustomIniFile);
begin
  Checked := Ini.ReadBool(IniSection, IniProperty, False);
end;

procedure TCheckBox.SaveToIni(Ini: TCustomIniFile);
begin
  Ini.WriteBool(IniSection, IniProperty, Checked);
end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  SomeCheckBox.IniSection := ...;
  SomeCheckBox.IniProperty := ...;
end;

procedure TMyForm.LoadConfig;
var
  Ini: TIniFile;
begin
  ...
  SomeCheckBox.LoadFromIni(Ini);
  ...
end;

procedure TMyForm.SaveConfig;
var
  Ini: TIniFile;
begin
  ...
  SomeCheckBox.SaveToIni(Ini);
  ...
end;

 

Edited by Remy Lebeau
  • Like 1
  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×