Solved

Im missing the connection between the objects, can someone point me in the right direction.

Posted on 2004-04-01
20
304 Views
Last Modified: 2010-04-05
Should be a fairly easy solutioin but I must be missing something obvious.

Ive got a new class created
TCBRecord = class
  private
    Variable1, Variable2, etc
    <Procedures to set variables, functions to read>
  public
    <Constructor>
    <read/write properties>
  end

TMyForm = class(TForm)
  MyBook : TList

I create a TCBRecord and then add it to MyBook

The thing is that MyBook is not linked to TCBRecord, but to the form it is on.

Im trying to now read the entries from MyBook but I cant access the records.
Obvisouly Im putting the variables in the wrong places but I cant see how to re-arrange things.

The TList must be linked to the form as I want to try and read variables from the records in MyList to display in a stringgrid

Why cant I do
  For iCnt := 0 to (MyBook.Count - 1) do
  begin
    Stringgrid.Cells[Col, Row] := MyBook[iCnt].variable1
  end

So MyBook[iCnt].variable1 is incorrect, how can I now access the data
0
Comment
Question by:mikelittlewood
  • 11
  • 5
  • 4
20 Comments
 
LVL 26

Assisted Solution

by:Russell Libby
Russell Libby earned 100 total points
Comment Utility

Because TList returns pointers, which your object is, but just needs to be cast back to the proper type.

eg:

Stringgrid.Cells[Col, Row] := TCBRecord(MyBook[iCnt]).variable1



Regards,
Russell
0
 
LVL 11

Accepted Solution

by:
shaneholmes earned 100 total points
Comment Utility
You could also use a TObjectList    vs   a TList

(found in the Contnrs unit)


 Create it the same as you would a TList

 MyList:= TObjectList.Create;

 but then

 Set its OwnsObjects = True

 MyList:= TObjectList.Create;
 MyList.OwnsObjects:= True;


 Now when the TObjectList is destroyed  

 MyList.free

It will destroy all the objects in the list

in this case, all your  TCBRecords

else

 you will have to iterate through the entire TList and release each object your self.


I also like to wrap my TObjectLists up into my own little list, which uses my Object
so I dont have to typecast

Example:


TMyList = class(TObjectList)
  private
  function GetItem (Index : Integer):TRCBRecord
  procedure SetItem(Index: Integer; const ARecord: TRCBRecord);
  protected
  public
   constructor Create(AOwnsObjects: Boolean = True); reintroduce;
   destructor Destroy; override;
   function Add(Item: TRCBRecord): Integer;
   procedure Delete(Index: Integer);
   property Item [Index : Integer] : TRCBRecord read GetItem write SetItem; default;
  end;


function TMyList.GetItem(Index : Integer):TRCBRecord;
begin
 result := TRCBRecord(Inherited Items [Index]);
end;

procedure TMyList.SetItem(Index: Integer; const ARecord: TRCBRecord);
begin
 inherited Items[Index] := ARecord;
end;

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

function TMyList.Add(Item: TRCBRecord):Integer;
begin
 Result:= inherited Add(Item);
end;


constructor TMyList.Create(AOwnsObjects: Boolean = True);
begin
 inherited Create(AOwnsObjects);
end;

destructor TMyList.Destroy;
begin
 inherited Destroy;
end;


 Hope this helps!

Shane
0
 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
Sorry, Replace MyList with MyBook   <SMILE>

Shane
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
Thx to u both for your answers.

Is a TList better to use for say simple lists like strings and integers.
While TObjectList is better for holding objects.

Im gonna award the points to Russell as he did answer my question and solve the issue I had.
But thanks for that complete answer Shane, I think I might re-write the section and create another new class.
Im sorta trying new things out so I appreciate the complete answer.
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
In fact I split the points for you both

thanks again
0
 
LVL 26

Expert Comment

by:Russell Libby
Comment Utility

Tlist is good for record pointers, integers, pointers in general; basically anything that is not a TObject decendant. The object list provides a better means for handling objects. Even better (in this case) would have been to create a TCollection and TCollectionItem class setup.

It just depends how much extra work you wish to put into it, and if you do/don't mind handling the memory management yourself.

Thanks for the pts,
Russell


0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
>>Even better (in this case) would have been to create a TCollection and TCollectionItem class setup. <<

Don't mean to be a pain asking too many basic questions, but could you give me some logic as to why this would be better.
It doesnt help that I havent used TCollection before, maybe once I get the full program running how I want it I might do some more experimenting
0
 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
Yeah, i'd like to hear how this would be better also

 listening....

Shane
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
Hey Shane, got another quick question for ya .. following on from the large section you wrote.

I seem to be having some problems getting the correct record out of my TObjectList.
Ive created that wrapper you suggested around my TCBRecord
Not sure if this is correct but ...

Im reading the records from a database into my TCBRecord in a loop and adding to my ObjectList (TMyList)

Temp_CBRecord := TCBRecord.create;
while <Something> do
begin
  Temp_CBRecord.VALUE1  := IBQuery.FieldByName('VALUE1').AsString;
  Temp_CBRecord.VALUE2   := IBQuery.FieldByName('VALUE2').AsString;
  <etc>
  MyList.Add(Temp_CBRecord);
  IBQuery.next
end;

but when I try to access the records outside of this procedure, I only ever seem to read the last entry into the list
at a guess Im saying I shouldnt be adding temporary records to MyList

for RecordIndex:= 0 to (MyList.Count - 1) do
  ReadTCBRecordAndDoSomething(RecordIndex);

0
 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
We need tos ee this part

ReadTCBRecordAndDoSomething(RecordIndex);


Shane
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 26

Expert Comment

by:Russell Libby
Comment Utility

I think we can all agree on the following:

TList   - list for maintiaing generic pointers
TObject - list that is specialized for holding generic objects, but still requires casting to the correct object type in order to use it.

The TCollection is similar to the object list, but offers the
following enhancements.

1.) Takes minimal coding to hande the adding/deleting/casting, as most of this functionality is already encapsulated in the TCollection / TCollectionItemClass.

2.) Can be persisted (because it inherits from TPersistent), which is useful for making this a property of a component, as it also offers a designer.

3.) If you free an object in an object list, you must code the removal from the object list. eg

obj:=TObjectList[1];
obj.Free; // TObjectList now holds a stale pointer at index 1
// The list must now be corrected to remove the item at index 1, because it was
// freed

The collection item on the other hand, will notify the collection of the removal so the developer can feel "free" (so to speak) to call Free on any object in the list, and not have to worry about the list management. Believe it or not, this scenario tends to catch quite a few developers...

So, using your example class, this is all that is required to turn this into a collection/collection item class set.

type
  TCBRecord      = class(TCollectionItem)
  private
     FVariable1: Integer;
     FVariable2: Integer;
  published
     property    Variable1: Integer read FVariable1 write FVariable1;
     property    Variable2: Integer read FVariable2 write FVariable2;
  end;

type
  TCBRecords     =  class(TCollection)
  private
     function    GetItem(Index: Integer): TCBRecord;
     procedure   SetItem(Index: Integer; Value: TCBRecord);
  public
     constructor Create;
     function    Add: TCBRecord;
     property    Items[Index: Integer]: TCBRecord read GetItem write SetItem; default;
  end;

implementation

function TCBRecords.Add: TCBRecord;
begin
  result:=TCBRecord(inherited Add);
end;

function TCBRecords.GetItem(Index: Integer): TCBRecord;
begin
  result:=TCBRecord(inherited GetItem(Index));
end;

procedure TCBRecords.SetItem(Index: Integer; Value: TCBRecord);
begin
  inherited SetItem(Index, Value);
end;

constructor TCBRecords.Create;
begin
  inherited Create(TCBRecord);
end;

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

Anyways, it just provides a more tightly coupled way of managing same type objects, not to say that it could not have been done with TObjectList.


Russell



0
 
LVL 26

Expert Comment

by:Russell Libby
Comment Utility
Example of use:

var  cbRecords: TCBRecords;
begin

  cbRecords:=TCBRecords.Create;
  with cbRecords.Add do
  begin
     Variable1:=10;
     Variable2:=20;
  end;
  Assert(cbRecords.Count = 1, 'Incorrect count');
  cbRecords[0].Free;
  Assert(cbRecords.Count = 0, 'Incorrect count');

  cbRecords.Free;
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
Hi Shane

In the procedure I call I am just writing the info being held in the records to a stringrid
Following on from before:-

for RecordIndex:= 0 to (MyList.Count - 1) do
  ReadTCBRecordAndDoSomething(RecordIndex);

procedure ReadTCBRecordAndDoSomething(RecordIndex: integer);
begin
  Cells[Col1,Selected_Row] := MyList[RecordIndex].Value1;
  Cells[Col2,Selected_Row] := MyList[RecordIndex].Value2;
  Cells[Col3,Selected_Row] := MyList[RecordIndex].Value3;
  <etc>
  Inc(Selected_Row)
end

Im hoping this should loop through the TObjectList and write the values to the grid.
The issue is that it only ever seems to read the last record from the TObjectList,
whatever index value I use for MyList it contains the same data, the last record entered
into the list. Its almost like the last record is repeated at every index, like it overwrote
previous data for some reason.

Temp_CBRecord := TCBRecord.create;
while <Something> do
begin
  Temp_CBRecord.VALUE1  := IBQuery.FieldByName('VALUE1').AsString;
  Temp_CBRecord.VALUE2   := IBQuery.FieldByName('VALUE2').AsString;
  <etc>
  MyList.Add(Temp_CBRecord);
  IBQuery.next
end;

is where I populated the TObjectList.
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
Thanks for that other info btw Russel, that was new to me so nice to learn.
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
Where I create my Temp_CBRecord for populating my TObjectList, the only thing I can think about is that the reason it is displaying the same record is that I am over-writing previous records with the last one.

Am I using this incorrectly .. MyList.Add(Temp_CBRecord) .. and then deleting values when I go back round the loop again to create a new MyList.Add(Temp_CBRecord)
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
Ok .. think Im definitely overwriting my TCBRecord everytime I populate my MyList .. I just assumed it would "add" the record to MyList and then let me create a new one over the top of it
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
This is where the problem area is .. after a bit of debugging

Procedure TThisForm.Populate_MyList;
var
  Temp_CBRecord: TCBRecord;
begin
  Temp_CBRecord := TCBRecord.create;

  //dmAcc is a datamodule holding my TIBQuery IBQ_Cashbook
  with dmAcc, IBQ_Cashbook do  
  begin

     //Loop and add records to a certain date
    while (fieldbyname('ENTRY_DATE').AsDateTime < EndOfTheMonth(DTP_1Month.Date)) and not eof do  
    begin
      Temp_CBRecord.sCR_DR  := FieldByName('CR_DR').AsString;
      Temp_CBRecord.sDesc   := FieldByName('CR_DR_DESC').AsString;
      Temp_CBRecord.sReason := FieldByName('REASON').AsString;
      Temp_CBRecord.dAmount := FieldByName('AMOUNT').AsFloat;
      Temp_CBRecord.dCBDate := FieldByName('ENTRY_DATE').AsDateTime;
      Temp_CBRecord.sOrderNo:= FieldByName('ORDER_NO').AsString;

      MyList.Add(Temp_CBRecord);   //Overwriting previous records added to MyList every loop it seems.
      next
    end;
  end
end;
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
I know Ive made an object just to hold data ...
Do you think maybe I would have been better just creating a record instead

Unless there is an easy solution Im not spotting in my n00bness
I presume I need a number of Temp_CBRecord 's .. but I dont know how many I will need   :o/
0
 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
You just needed to move the following line down:

    Temp_CBRecord := TCBRecord.create;


Procedure TThisForm.Populate_MyList;
var
  Temp_CBRecord: TCBRecord;
begin
  //dmAcc is a datamodule holding my TIBQuery IBQ_Cashbook
  with dmAcc, IBQ_Cashbook do  
  begin
     //Loop and add records to a certain date
    while (fieldbyname('ENTRY_DATE').AsDateTime < EndOfTheMonth(DTP_1Month.Date)) and not eof do  
    begin
      Temp_CBRecord := TCBRecord.create;  //*************** create a record for each in table
      Temp_CBRecord.sCR_DR  := FieldByName('CR_DR').AsString;\
      Temp_CBRecord.sDesc   := FieldByName('CR_DR_DESC').AsString;
      Temp_CBRecord.sReason := FieldByName('REASON').AsString;
      Temp_CBRecord.dAmount := FieldByName('AMOUNT').AsFloat;
      Temp_CBRecord.dCBDate := FieldByName('ENTRY_DATE').AsDateTime;
      Temp_CBRecord.sOrderNo:= FieldByName('ORDER_NO').AsString;
      MyList.Add(Temp_CBRecord);   //Overwriting previous records added to MyList every loop it seems.
      next
    end;
  end
end;

Shane
0
 
LVL 15

Author Comment

by:mikelittlewood
Comment Utility
geez .. silly mistake.

thanks for all your help!
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
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…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

771 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now