Solved

Custom Classes

Posted on 2002-05-22
8
197 Views
Last Modified: 2013-11-23
I'm trying to display some data on a form.  Each data entry has similar properties (a label, and edit box, a checkbox and some hidden string, boolean and integer info).

I would like to create a dyanmic array of components.  Each array element should have a Label, an Edit Box and a Checkbox, and some variables (such as boolean and integer variables).  I would like to make it so that I can display as many or as few of these "components" as I need on the form.

I'm assuming I have to create my own class, and create an array of them, but how do I go about doing that (syntactically).  I've been having some troubles especially with the creation of the objects.
0
Comment
Question by:PaulKorzycki
  • 4
  • 2
  • 2
8 Comments
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 7028471
you can make smth like :

TSomeStuff = class(TComponent)
  private
    FLabel: TLabel;
    FEdit: TEdit;
    FCheckBox: TCheckBox;
    FExtString: string;
    FExtBool: Boolean;
    FExtInteger: Integer;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Edit: TEdit read FEdit;
    property CheckBox: TCheckBox read FcheckBox;
    property ExtString: string read FExtString write FExtString;
    property ExtBool: boolean read FExtBool write FExtBool;
    property ExtInteger: Integer read FExtInteger write FExtInteger;
  end;


implementation

constructor TSomeStuff.Create(AOwner: TComponent);
begin
     inherited;
     FLabel:=TLabel.Create(Self);
     FEdit:=TEdit.Create(Self);
     FCheckBox:=TCheckBox.Create(Self);
     FExtBool:=false;
     FExtInteger:=0;
     FExtString:='';
end;

destructor TSomeStuff.Destroy;
begin
     // do some cleanup
     inherited;
end;




I suggest you use TObjectList instead of an array
better yet would be to make a collection
that component being a TCollectionItem
0
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 7028495
you can make smth like :

TSomeStuff = class(TComponent)
  private
    FLabel: TLabel;
    FEdit: TEdit;
    FCheckBox: TCheckBox;
    FExtString: string;
    FExtBool: Boolean;
    FExtInteger: Integer;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Edit: TEdit read FEdit;
    property CheckBox: TCheckBox read FcheckBox;
    property ExtString: string read FExtString write FExtString;
    property ExtBool: boolean read FExtBool write FExtBool;
    property ExtInteger: Integer read FExtInteger write FExtInteger;
  end;


implementation

constructor TSomeStuff.Create(AOwner: TComponent);
begin
     inherited;
     FLabel:=TLabel.Create(Self);
     FEdit:=TEdit.Create(Self);
     FCheckBox:=TCheckBox.Create(Self);
     FExtBool:=false;
     FExtInteger:=0;
     FExtString:='';
end;

destructor TSomeStuff.Destroy;
begin
     // do some cleanup
     inherited;
end;




I suggest you use TObjectList instead of an array
better yet would be to make a collection
that component being a TCollectionItem
0
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 7028499
damn .. why did it get posted twice ?

anyway a full example :

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Contnrs;

type
  TSomeStuff = class(TComponent)
  private
    FLabel: TLabel;
    FEdit: TEdit;
    FCheckBox: TCheckBox;
    FExtString: string;
    FExtBool: Boolean;
    FExtInteger: Integer;
  public
    constructor Create(AOwner: TComponent);override;
    destructor Destroy; override;
  published
    property xLabel: TLabel read FLabel;
    property Edit: TEdit read FEdit;
    property CheckBox: TCheckBox read FcheckBox;
    property ExtString: string read FExtString write FExtString;
    property ExtBool: boolean read FExtBool write FExtBool;
    property ExtInteger: Integer read FExtInteger write FExtInteger;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    MyList: TObjectList;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TSomeStuff }

constructor TSomeStuff.Create(AOwner: TComponent);
begin
     inherited;
     FLabel:=TLabel.Create(Self);
     FEdit:=TEdit.Create(Self);
     FCheckBox:=TCheckBox.Create(Self);
     FExtBool:=false;
     FExtInteger:=0;
     FExtString:='';
end;

destructor TSomeStuff.Destroy;
begin
     // do some cleanup
     inherited;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
     MyList:=TObjectList.Create(true);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
     FreeAndNil(MyList);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
     if MyList.Count > 0 then
        MyList.Delete(MyList.Count-1);
     Caption:=IntToStr(MyList.Count);
end;

procedure TForm1.Button1Click(Sender: TObject);
var tmpSS: TSomeStuff;
begin
     tmpSS:=TSomeStuff.Create(nil);
     tmpSS.FEdit.Text:='lala';
     tmpSS.xLabel.Caption:='boo';
     MyList.Add(tmpSS);
     Caption:=IntToStr(MyList.Count);
end;

end.




you handle the positions and other things yourself :)
but to avoid all this you can simply use Frames
create a frame, put some compos on it
then create the frames like I did with that TSomeStuff
and add it to the objectlist
0
Best Practices: Disaster Recovery Testing

Besides backup, any IT division should have a disaster recovery plan. You will find a few tips below relating to the development of such a plan and to what issues one should pay special attention in the course of backup planning.

 
LVL 9

Expert Comment

by:ITugay
ID: 7028906
Hi PaulKorzycki,

let suppose you created your own control. In additional, you can create global TList (instead of dynamic array) and automatically add control there in constructor and remove it in destructor.

interface

var
  GL: TList;
....

implemantation

constructor TSomeControl.Create(AOwner: TComponent);
begin
  Inherited;
  ...
  GL.Add(Self);
end;

destructor TSomeClass.Destroy;
begin
  GL.Remove(Self);
  ...
  inherited;
end;

...
initialization
  GL := TList.Create;
finalization
  GL.Free;
end.


-------
Igor.
0
 

Author Comment

by:PaulKorzycki
ID: 7029685
Thanks for all of your help guys.  I think I'm getting closer, I just have a few questions:

1) Lee_Nover: you talked about TObjectList, but I can't find reference to it in Delphi 4 (the help lists nothing).  Is this the same as TList?  So what is the difference between TList and TCollectionItem?  Why use one instead of the other?

2) ITugay: I understand your example, but it looks like you are adding the objects into the array within the actual constructor of the object?  Ideally I would like to have the objects defined in thier own units, and then be able to create them from their own program.  Currently I do the following:

------------------------------

(unit TSomeControl defined in seperate file...)
...
var GL: array of TSomeControl;

...
(in the main body, when I want to add my new control into the form...)

  setlength(GL, Length(GL)+1);
  GL[Length(GL)-1]:=TSomeControl.Create(self);
  with (GL[Length(GL)-1]) do
  begin
    Parent:=self;
    Top:=50
    Left:=50;
  end;

....
(on program exit...)

for i:=0 to (length(GL)-1) do GL[i].Free;

------------------------------

This does work in fact, but I suppose TList would be a better soloution.  How do I create the object and stuff it into the TList *not* in the unit (ie: do it in the main form)?  Where should I initialize and destroy the list?

3) Lee_Nover: TSomeStuff.Create creates every control, should TSomeStuff.Destroy "Free" all of them, or will the sub components get freed automatically?  Likewise, in my example when I free the array, does everything get taken with it, or do I have to do each component first?

4) (This is an aside) How can I publish certain properties and not others?  For example, if in CheckBox, I don't want the "Checked" property to get published down?

I think that's it for now...thanks again for all your help, I know most of these are newbie questions, but I think its about time I finally solidify these concepts.

Thanks!
0
 
LVL 12

Accepted Solution

by:
Lee_Nover earned 100 total points
ID: 7030374
I have D6 so I don't know what classes are there in D4
TObjectList is in the contnrs unit (don't know about D4)
if you want to hide a property from the OI then move it to the public section

you can create your own list of your own type
here's an example:

type
  TSections = class;

  TSection = class(TObject)
  private
  public
    Area: TRect;
    SecCol: Integer;
    SecRow: Integer;
    SecIndex: Integer;
    Sensitivity: Byte;
    LastChange: Byte;
    Selected: Boolean;
    FSections: TSections;
    constructor Create(ASections: TSections);
    procedure Clear;
  end;

  TSections = class(TList)
  private
    FRows: Integer;
    FColumns: Integer;
    FHeight: Integer;
    FWidth: Integer;
    FSectionHeight: Integer;
    FSectionWidth: Integer;
    FDefSensitivity: Byte;
  protected
    procedure Notify(Ptr: Pointer; Action: TListNotification); override;
    function GetItem(Index: Integer): TSection;
    procedure SetItem(Index: Integer; Item: TSection);

    function GetSection(Column, Row: Integer): TSection;
    procedure SetSection(Column, Row: Integer; Item: TSection);

  public
    constructor Create(AColumns, ARows: Integer);
    destructor Destroy;override;

    function Add(Section: TSection): Integer;overload;
    function Add: TSection;overload;
    function Insert(Index: Integer; Section: TSection): Integer;overload;
    function Insert(Index: Integer): TSection;overload;
    procedure Delete(Index: Integer);
    procedure Clear;override;

    procedure SetColumns(Value: Integer);
    procedure SetRows(Value: Integer);
    procedure SetHeight(Value: Integer);
    procedure SetWidth(Value: Integer);

    function LoadFromFile(const FileName: string): Boolean;
    function SaveToFile(const FileName: string): Boolean;
    procedure Update;
    procedure UpdateSectionRects;

    function SectionToIndex(const Col, Row: Integer): Integer;
    function IndexToSection(const Index: Integer; var Col, Row: Integer): Integer;

    // properties
    property Rows: Integer read FRows write SetRows;
    property Columns: Integer read FColumns write SetColumns;
    property Height: Integer read FHeight write SetHeight;
    property Width: Integer read FWidth write SetWidth;
    property DefaultSensitivity: Byte read FDefSensitivity write FDefSensitivity default 50;
    property Items[Index: Integer]: TSection read GetItem write SetItem;
    property Section[Column, Row: Integer]: TSection read GetSection write SetSection;

  end;



implementation

{ ---- TSections ----}

constructor TSections.Create(AColumns, ARows: Integer);
begin
     inherited Create();
     if AColumns < 1 then AColumns:=1;
     if ARows < 1 then ARows:=1;
     Add;
     FColumns:=1;
     FRows:=1;
     Rows:=ARows;
     Columns:=AColumns;
end;

destructor TSections.Destroy;
begin
     inherited;
end;

function TSections.Add(Section: TSection): Integer;
begin
     Result:=inherited Add(Section);
end;

function TSections.Add: TSection;
begin
     Result:=TSection.Create(Self);
     Result.SecIndex:=Count;
     Result.Sensitivity:=FDefSensitivity;
     inherited Add(Result);
end;

function TSections.Insert(Index: Integer): TSection;
begin
     Result:=TSection.Create(Self);
     Result.Sensitivity:=FDefSensitivity;
     inherited Insert(Index, Result);
end;

function TSections.Insert(Index: Integer; Section: TSection): Integer;
begin
     Section.SecIndex:=Index;
     inherited Insert(Index, Section);
     Result:=Index;
end;

procedure TSections.Delete(Index: Integer);
begin
     inherited Delete(Index);
end;

procedure TSections.Clear;
var I: Integer;
begin
     if Count < 2 then exit;
     for I:=Count-1 downto 0 do
         Delete(I);

     Add;
     FColumns:=1;
     FRows:=1;
     inherited;
end;

procedure TSections.Notify(Ptr: Pointer; Action: TListNotification);
begin
     if Action = lnDeleted then begin
        FreeAndNil(Ptr);
     end;
     inherited Notify(Ptr, Action);
end;

function TSections.GetItem(Index: Integer): TSection;
begin
     Result:=TSection(inherited Items[Index]);
end;

procedure TSections.SetItem(Index: Integer; Item: TSection);
begin
     inherited Items[Index]:=Item;
end;

function TSections.SectionToIndex(const Col, Row: Integer): Integer;
begin
     Result:=(Row * FColumns) + Col;
end;

function TSections.IndexToSection(const Index: Integer; var Col, Row: Integer): Integer;
begin
     if (Index < FColumns) then begin // because of division by zero
        Row:=0;
        Col:=Index;
     end else begin
        Row:=(Index div FColumns);
        Col:=(Index mod FColumns);
     end;
     Result:=0;
end;

function TSections.GetSection(Column, Row: Integer): TSection;
var I: Integer;
begin
     I:=SectionToIndex(Column, Row);
     if I >= Count then I:=Count-1;
     Result:=TSection(inherited Items[I]);
end;

procedure TSections.SetSection(Column, Row: Integer; Item: TSection);
var I: Integer;
begin
     I:=SectionToIndex(Column, Row);
     if I >= Count then I:=Count-1;
     inherited Items[I]:=Item;
end;

procedure TSections.SetRows(Value: Integer);
var r, c, I: Integer;
begin
     if Value = FRows then exit;
     if Value > FRows then begin
        for r:=FRows to Value-1 do
            for c:=0 to FColumns-1 do
                with Add do begin
                  SecRow:=r;
                  SecCol:=c;
                end;
     end else begin
        for r:=FRows-1 downto Value do
            for c:=FColumns-1 downto 0 do begin
                I:=SectionToIndex(c, r);
                Delete(I);
            end;
     end;
     FRows:=Value;
     FSectionHeight:=FHeight div FRows;
     Update;
end;

procedure TSections.SetColumns(Value: Integer);
var r, c, cc, I: Integer;
begin
     if Value = FColumns then exit;
     cc:=FColumns;
     if Value > FColumns then begin
        FColumns:=Value;
        for r:=0 to FRows-1 do
            for c:=cc to Value-1 do begin
                I:=SectionToIndex(c, r);
                with Insert(I) do begin
                  SecCol:=c;
                  SecRow:=r;
                end;
            end;
     end else begin
        for r:=FRows-1 downto 0 do
            for c:=FColumns-1 downto Value do begin
                I:=SectionToIndex(c, r);
                Delete(I);
            end;
     end;
     FColumns:=Value;
     FSectionWidth:=FWidth div FColumns;
     Update;
end;

procedure TSections.SetHeight(Value: Integer);
begin
     if Value = FHeight then exit;
     FHeight:=Value;
     FSectionHeight:=FHeight div FRows;
     Update;
end;

procedure TSections.SetWidth(Value: Integer);
begin
     if Value = FWidth then exit;
     FWidth:=Value;
     FSectionWidth:=FWidth div FColumns;
     Update;
end;

procedure TSections.Update;
begin
     UpdateSectionRects;
end;

procedure TSections.UpdateSectionRects;
var c, r: Integer;
begin
     if (Rows < 1)or(Columns < 1) then exit;
     for r:=0 to FRows-1 do
         for c:=0 to FColumns-1 do
             try
                with Section[c, r].Area do begin
                  Top:=(FRows - r -1) * FSectionHeight; // r * FSectionHeight; // it really should be like this !!! dunno why it show weird values
                  Bottom:=Top + FSectionHeight -1;
                  Left:=c * FSectionWidth;
                  Right:=Left + FSectionWidth -1;
                end;
             finally
             end;
end;

function TSections.LoadFromFile(const FileName: string): Boolean;
var Ini: TIniFile;
    I: Integer;
    DefSens: Byte;
begin
     Result:=true;
     Ini:=TIniFile.Create(FileName);
     with Ini do
       try
          if Count > 0 then Clear;
          Columns:=ReadInteger('Sections', 'Columns', 1);
          Rows:=ReadInteger('Sections', 'Rows', 1);
          DefSens:=ReadInteger('Detection', 'DefSensitivity', 50);

          // setting the columns and rows already creates the items
          for I:=0 to Count-1 do
              with Items[I] do begin
                Sensitivity:=ReadInteger('Sections', Format('Sec%d:', [I]), DefSens);
                LastChange:=0;
              end;

       finally
          Free;
       end;
end;

function TSections.SaveToFile(const FileName: string): Boolean;
var Ini: TIniFile;
    I: Integer;
begin
     Result:=true;
     Ini:=TIniFile.Create(FileName);
     with Ini do
       try
          if Count < 1 then begin
             EraseSection('Sections');
             WriteInteger('Sections', 'Columns', Columns);
             WriteInteger('Sections', 'Rows', Rows);
             WriteInteger('Sections', 'DefSensitivity', 50);
          end else begin
             WriteInteger('Sections', 'Columns', Columns);
             WriteInteger('Sections', 'Rows', Rows);
             WriteInteger('Sections', 'DefSensitivity', 50);

             for I:=0 to Count-1 do
                 with Items[I] do
                   try
                      WriteInteger('Sections', Format('Sec%d:', [I]), Sensitivity);
                   except
                   end;
          end;
       finally
          Free;
       end;
end;



check out Add, Insert, GetItem, SetItem
0
 
LVL 9

Expert Comment

by:ITugay
ID: 7031373
No, problem. Yoy can remove GL.Add and GL.Remove from constructor and destructor.

declare FList: Tlist property in your control. In destructor, check that this property assigned and then remove reference. Bellow you can see a sample:

  TSomeControl = ...
  private
    FList: TList;
    procedure SetList(AList: TList);
  public
   
    property List: TList read FList write SetList;
    destructor destroy; override;
...

procedure TSomeControl.SetList(Alist: TList);
begin
  FList := AList;
  if FList <> nil then
    FList.Add(Self);
end;

destructor TSomeControl.Destroy;
begin
  if FList <> nil then
    FList.Remove(Self);
  inherited;
end;


.....
// how to create control and place it into TList
var
  GL: TList; // place it and initialize it in any unit you like.

var
  C: TSomeControl;
begin
  C := TSomeControl.Create(Self);
  C.List := GL; // here is list assignment
  C.Width := 100;
  C.Parent := Self;
end;

------
Igor.
0
 

Author Comment

by:PaulKorzycki
ID: 7047657
Thanks for all of the help!  Sorry it took so long for me to accept the answer!

Paul
0

Featured Post

PRTG Network Monitor: Intuitive Network Monitoring

Network Monitoring is essential to ensure that computer systems and network devices are running. Use PRTG to monitor LANs, servers, websites, applications and devices, bandwidth, virtual environments, remote systems, IoT, and many more. PRTG is easy to set up & use.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Email security requires an ever evolving service that stays up to date with counter-evolving threats. The Email Laundry perform Research and Development to ensure their email security service evolves faster than cyber criminals. We apply our Threat…
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question