Reputation: 15
I have a custom component class that I use as a framework to load comments and user details, etc, onto a form. The Class is a sub-class of a panel and contains 3 labels and a memo.
Upon Closing my form, or trying to free the object, i get a "Invalid Pointer Operation" error. I know this is from trying to free an object twice, or accessing RAM that is not available. However, I don't know how to fix it. It's holding me back quite a bit, as I have to load different comments on different topics, and I cannot until I clear the form of the current comments.
Here is the class-related code:
type
TSkeleton = class(TPanel)
private
fName : TLabel;
fStudNo : TLabel;
fTimeAndDate : TLabel;
fComment : TMemo;
public
Constructor Create (AOwner: TComponent); overload; override;
constructor Create(AOwner:TForm; sName, sStudNo, sTime, sDate, sComment: string; ComCount: integer); overload;
end;
{ TSkeleton }
constructor TSkeleton.Create(AOwner: TComponent);
begin
//
end;
constructor TSkeleton.Create(AOwner: TForm; sName, sStudNo, sTime, sDate,
sComment: string; ComCount: integer);
begin
inherited Create(AOwner);
Parent := AOwner;
Width := 800;
Height := 250;
Top := 448+((ComCount-1)*250);
Left := 16;
BevelInner := bvSpace;
BevelOuter := bvLowered;
fName := TLabel.Create(fName);
self.InsertControl(fName);
with fName do
begin
Caption := sName;
Font.Name := 'Garamond';
Font.Size := 30;
Left := 7;
Top := 4;
end;
fStudNo := TLabel.Create(fStudNo);
self.InsertControl(fStudNo);
with fStudNo do
begin
Caption := sStudNo;
Font.Name := 'Garamond';
Font.Size := 15;
Left := 15;
Top := 52;
end;
fTimeAndDate := TLabel.Create(fTimeAndDate);
self.InsertControl(fTimeAndDate);
with fTimeAndDate do
begin
Caption := sTime + ' ' + sDate;
Font.Name := 'Garamond';
Font.Size := 20;
Left := 583;
Top := 4;
end;
fComment := TMemo.Create(fComment);
self.InsertControl(fComment);
with fComment do
begin
Lines.Add(sComment);
Font.Name := 'Garamond';
Font.Size := 12;
Left := 152;
Top := 56;
Height := 161;
Width := 633;
ReadOnly := True;
ScrollBars := ssVertical;
end;
end;
If you would like to see the other code used (reading the textfile, creating the array of objects, etc) please say so. It's not directly related to the class so I did not think it would be necessary.
Thank you in advance.
Edit: Based on @Remy Lebeau's code, and @NGLN's comments, I have decided to post everything necessary.
After fixing the class based on @Remy's code, I was still receiving the error. This led me to believe the error was where I was using the class, particularly in the array of objects I was creating.
Previously, my code was
for i := 0 to ComCount-1 do
begin
fArrObjects[i+1] := TSkeleton.Create(TargetForm);
with fArrObjects[i+1] do
begin
Parent := TargetForm;
TheName := fArrComments[i][0];
StudNo := fArrComments[i][1];
Time := fArrComments[i][2];
Date := fArrComments[i][3];
Comment := fArrComments[i][4];
ComCount := i+1;
end;
Changing
fArrObjects[i+1]
to
fArrObjects[i]
solved the issue.
Thank you to @Remy for correcting the errors in the class.
Upvotes: 0
Views: 931
Reputation: 596176
There are some problems with your code.
Parent := AOwner;
Do not set the Parent
from inside of the constructor. It is the responsibility of the caller to set the Parent
after the object is fully constructed first.
You do not have a destructor defined, and you are creating your child objects with nil
Owners. TSkeleton
should be the Owner instead, eg:
//fName := TLabel.Create(fName);
fName := TLabel.Create(Self);
...
You should not be calling InsertControl()
directly. Use the Parent
property instead, eg:
//self.InsertControl(fName);
fName.Parent := Self;
...
Why do you have two constructors? Your overridden constructor does not do anything, not even call the base constructor, and your custom constructor is not called at design-time (unless you have other code that is creating instances of TSkeleton
programmably). I suggest you get rid of the custom constructor and expose published properties to manipulate the child controls as needed.
Lastly, since you are creating sub-components, you should mark them as such via TComponent.SetSubComponen()
.
With that said, try something more like this:
type
TSkeleton = class(TPanel)
private
fName : TLabel;
fStudNo : TLabel;
fTimeAndDate : TLabel;
fComment : TMemo;
fTime: string;
fDate: string;
fComCount: Integer;
function GetTheName: string;
procedure SetTheName(const AValue: string);
function GetStudNo: string;
procedure SetStudNo(const AValue: string);
procedure SetTime(const AValue: string);
procedure SetDate(const AValue: string);
function GetComment: string;
procedure SetComment(const AValue: string);
procedure SetComCount(AValue: integer);
public
constructor Create (AOwner: TComponent); override;
published
property TheName: string read GetTheName write SetTheName;
property StudNo: string read GetStudNo write SetStudNo;
property Time: string read fTime write SetTime;
property Date: string read fDate write SetDate;
property Comment: string read GetComment write SetComment;
property ComCount: integer read fComCount write SetComCount;
end;
constructor TSkeleton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Width := 800;
Height := 250;
Left := 16;
BevelInner := bvSpace;
BevelOuter := bvLowered;
fName := TLabel.Create(Self);
fName.SetSubComponent(True);
fName.Parent := Self;
fName.Font.Name := 'Garamond';
fName.Font.Size := 30;
fName.Left := 7;
fName.Top := 4;
fStudNo := TLabel.Create(Self);
fStudNo.SetSubComponent(True);
fStudNo.Parent := Self;
fStudNo.Font.Name := 'Garamond';
fStudNo.Font.Size := 15;
fStudNo.Left := 15;
fStudNo.Top := 52;
fTimeAndDate := TLabel.Create(Self);
fTimeAndDate.SetSubComponent(True);
fTimeAndDate.Parent := Self;
fTimeAndDate.Font.Name := 'Garamond';
fTimeAndDate.Font.Size := 20;
fTimeAndDate.Left := 583;
fTimeAndDate.Top := 4;
fComment := TMemo.Create(Self);
fComment.SetSubComponent(True);
fComment.Parent := Self;
fComment.Font.Name := 'Garamond';
fComment.Font.Size := 12;
fComment.Left := 152;
fComment.Top := 56;
fComment.Height := 161;
fComment.Width := 633;
fComment.ReadOnly := True;
fComment.ScrollBars := ssVertical;
end;
function TSkeleton.GetTheName: string;
begin
Result := fName.Caption;
end;
procedure TSkeleton.SetTheName(const AValue: string);
begin
fName.Caption := AValue;
end;
function TSkeleton.GetStudNo: string;
begin
Result := fStudNo.Caption;
end;
procedure TSkeleton.SetStudNo(const AValue: string);
begin
fStudNo.Caption := AValue;
end;
procedure TSkeleton.SetTime(const AValue: string);
begin
if fTime <> AValue then
begin
fTime := AValue;
fTimeAndDate.Caption := fTime + ' ' + fDate;
end;
end;
procedure TSkeleton.SetDate(const AValue: string);
begin
if fDate <> AValue then
begin
fDate := AValue;
fTimeAndDate.Caption := fTime + ' ' + fDate;
end;
end;
function TSkeleton.GetComment: string;
begin
Result := fComment.Text;
end;
procedure TSkeleton.SetComment(const AValue: string);
begin
fComment.Text := AValue;
end;
procedure TSkeleton.SetComCount(AValue: integer);
begin
if fComCount <> AValue then
begin
fComCount := AValue;
Top := 448+((FComCount-1)*250);
end;
end;
Upvotes: 4