We help IT Professionals succeed at work.

Check out our new AWS podcast with Certified Expert, Phil Phillips! Listen to "How to Execute a Seamless AWS Migration" on EE or on your favorite podcast platform. Listen Now

x

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

Mike Littlewood
on
Medium Priority
344 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
Comment
Watch Question

Russell LibbySoftware Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005
Commented:
Unlock this solution with a free trial preview.
(No credit card required)
Get Preview
Unlock this solution with a free trial preview.
(No credit card required)
Get Preview
Sorry, Replace MyList with MyBook   <SMILE>

Shane
Mike LittlewoodEngineer

Author

Commented:
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.
Mike LittlewoodEngineer

Author

Commented:
In fact I split the points for you both

thanks again
Russell LibbySoftware Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005

Commented:

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


Mike LittlewoodEngineer

Author

Commented:
>>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
Yeah, i'd like to hear how this would be better also

 listening....

Shane
Mike LittlewoodEngineer

Author

Commented:
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);

We need tos ee this part

ReadTCBRecordAndDoSomething(RecordIndex);


Shane
Russell LibbySoftware Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005

Commented:

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



Russell LibbySoftware Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005

Commented:
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;
Mike LittlewoodEngineer

Author

Commented:
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.
Mike LittlewoodEngineer

Author

Commented:
Thanks for that other info btw Russel, that was new to me so nice to learn.
Mike LittlewoodEngineer

Author

Commented:
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)
Mike LittlewoodEngineer

Author

Commented:
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
Mike LittlewoodEngineer

Author

Commented:
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;
Mike LittlewoodEngineer

Author

Commented:
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/
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
Mike LittlewoodEngineer

Author

Commented:
geez .. silly mistake.

thanks for all your help!
Unlock the solution to this question.
Thanks for using Experts Exchange.

Please provide your email to receive a free trial preview!

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.