Reputation: 2591
I have the following components, tncrdragdata
(tframedscrollbox
) tdragdata
(tgroupbox
)
The main Idea is to combine them and use them as a list box (I need it this way).
The groupbox contains five tedit
, one tcombobox and one tbutton
.
The problem is when I try to free the tdragdata
inside its event handler.
I use the FreeNotification
method to relocate the group boxes in the framedscrollbox
. The problem is that the overridden notification method is executed twice for some reason I don't know.
My question is: Why the overridden method is executed twice?
If I remove the condition (self.components[index]<>AComponent)
in the relocate items method I get an AV. When I debugged this I noticed that the method is executed twice.
This is the code for the two components:
unit ncrdragdataunit;
interface
uses
System.SysUtils, System.Classes, FMX.Layouts, FMX.Controls.Presentation,
FMX.StdCtrls, system.Generics.collections, dragdataunit, FMX.objects,
system.types, FMX.graphics, FMX.dialogs, System.Messaging;
type
Tncrdragdata = class(TFramedScrollBox)
private
{ private declarations }
Faddimage: timage;
Fnextcoor: tpointf;
Fitemcounter: integer;
Fncrdata: tlist<tdragdata>;
Flocate: boolean;
function calculate_next_coor: tpointf;
procedure additem(Aname: string);
procedure relocate_items(AComponent: TComponent);
procedure createaddimage(path: unicodestring);
procedure clickaddimage(sender: tobject);
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
protected
{ protected declarations }
public
{ public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure extract_dragdata(var dragdata: tlist<tdragdatafields>);
published
{ published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('ncrcontrols', [Tncrdragdata]);
end;
{tncrdragdata}
constructor tncrdragdata.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{spesific data}
Fncrdata: = tlist<tdragdata>.create;
Flocate: = true;
Fnextcoor.X: = 0;
Fnextcoor.Y: = -60;
Fitemcounter: = 0;
if not(csDesigning in ComponentState) then
begin
createaddimage('C:\Users\nacereddine\Desktop\down-arrow-2.png');
additem('item' + inttostr(Fitemcounter));
end;
end;
destructor tncrdragdata.Destroy;
begin
Flocate: = false;
Faddimage.Free;
Fncrdata.Free;
inherited;
end;
function Tncrdragdata.calculate_next_coor: tpointf;
begin
if(self.componentcount = 0) then
begin
result.x: = 20;
result.y: = 20;
end
else
begin
result.x: = 20;
result.y: = Fnextcoor.y + 80;
end;
end;
procedure Tncrdragdata.additem(Aname: string);
var
a: tdragdata;
begin
Fnextcoor: = calculate_next_coor;
a: = tdragdata.create(self);
Fncrdata.Add(a);
inc(Fitemcounter);
with a do
begin
name: = Aname;
text: = '';
position.y: = Fnextcoor.y;
position.x: = Fnextcoor.x;
parent: = self; // parent name
a.FreeNotification(self); <---- this is the problem
end;
Faddimage.Position.X: = Fnextcoor.x + 260;
Faddimage.Position.y: = Fnextcoor.y + 60;
end;
procedure Tncrdragdata.relocate_items(AComponent: TComponent);
var
index: Integer;
begin
if self.componentcount<1 then exit;
Fnextcoor.X: = 0;
Fnextcoor.Y: = -60;
for index: = 1 to self.componentCount-1 do
begin
if (self.components[index] is Tdragdata)and(self.components[index]<>AComponent) then
begin
Fnextcoor: = calculate_next_coor;
(self.components[index] as Tdragdata).Position.Y: = Fnextcoor.y;
(self.components[index] as Tdragdata).Position.x: = Fnextcoor.x;
end;
end;
Faddimage.Position.X: = Fnextcoor.x + 260;
Faddimage.Position.y: = Fnextcoor.y + 60;
end;
procedure Tncrdragdata.createaddimage(path: unicodestring);
begin
Faddimage: = timage.Create(self);
Faddimage.Parent: = self;
Faddimage.Width: = 40;
Faddimage.Height: = 40;
Faddimage.Bitmap.LoadFromFile(path);
Faddimage.onclick: = clickaddimage;
end;
procedure Tncrdragdata.clickaddimage(sender: tobject);
begin
additem('item' + inttostr(Fitemcounter));
end;
procedure Tncrdragdata.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent is Tdragdata)and Flocate then
begin
relocate_items(AComponent);
Fncrdata.remove(Tdragdata(AComponent));
end;
end;
procedure Tncrdragdata.extract_dragdata(var dragdata: tlist<tdragdatafields>);
var
I: Integer;
begin
for I: = 0 to Fncrdata.Count-1 do
begin
dragdata.Add(Fncrdata.Items[I].dragdatafields);
end;
end;
end.
unit dragdataunit;
interface
uses
System.SysUtils, System.Classes, FMX.Types, FMX.Controls,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.listbox, FMX.edit, System.Messaging;
type
tsectiontype = (ST_vertical, ST_curved, ST_straight);
tdragdatafields = record
TVD, MD, VS, Inc, Alfa30: single;
sectiontype: tsectiontype;
end;
tdragdatafield = (df_TVD, df_MD, df_VS, df_Inc, df_Alfa30);
tdragdata = class(tgroupbox)
private
(* private declarations *)
Fdata: array[0..4] of single;
OTVD, OMD, OVS, OInc, OAlfa30: tedit;
Fsectiontype: tsectiontype;
Osectiontype: tcombobox;
headerlabel: tlabel;
Odeletebtn: tbutton;
procedure onchangevalue(sender: tobject);
procedure ondeletebtnclick(sender: tobject);
function getdata: tdragdatafields;
protected
(* protected declarations *)
public
(* public declarations *)
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
(* published declarations *)
property dragdatafields: tdragdatafields read getdata;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('ncrcontrols', [Tdragdata]);
end;
{tdragdata}
constructor tdragdata.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{spesific data}
SetBounds(10, 10, 550, 60);
self.Text: = '';
OTVD: = tedit.create(self);
with OTVD do
begin
text: = '';
SetBounds(10, 30, 80, 21);
onchange: = onchangevalue;
parent: = self;
end;
OMD: = tedit.create(self);
with OMD do
begin
text: = '';
SetBounds(100, 30, 80, 21);
onchange: = onchangevalue;
parent: = self;
end;
OVS: = tedit.create(self);
with OVS do
begin
text: = '';
SetBounds(190, 30, 80, 21);
onchange: = onchangevalue;
parent: = self;
end;
OInc: = tedit.create(self);
with OInc do
begin
text: = '';
SetBounds(280, 30, 80, 21);
onchange: = onchangevalue;
parent: = self;
end;
OAlfa30: = tedit.create(self);
with OAlfa30 do
begin
text: = '';
SetBounds(370, 30, 80, 21);
onchange: = onchangevalue;
parent: = self;
end;
Osectiontype: = tcombobox.create(self);
with Osectiontype do
begin
SetBounds(460, 30, 80, 21);
items.Add('STvertical');
items.Add('STcurved');
items.Add('STstraight');
//Selected.Text: = 'STvertical';
onchange: = onchangevalue;
parent: = self;
end;
headerlabel: = tlabel.create(self);
with headerlabel do
begin
text: = 'TVD (m) MD (m) VS (m) '
+ 'Inc (°) Alfa (°/30m) Section type';
SetBounds(10, 9, 560, 21);
parent: = self;
end;
Odeletebtn: = tbutton.create(self);
with Odeletebtn do
begin
text: = '';
SetBounds(537, 9, 10, 10);
parent: = self;
onclick: = ondeletebtnclick;
end;
end;
destructor tdragdata.Destroy;
begin
OTVD.free;
OMD.free;
OVS.free;
OInc.free;
OAlfa30.free;
Osectiontype.free;
headerlabel.free;
Odeletebtn.Free;
inherited;
end;
procedure tdragdata.onchangevalue(sender: tobject);
function getvalue(st: tedit): single;
begin
try
result: = strtofloat(st.Text);
except
result: = -1;
st.Text: = '-1';
end;
end;
function gettype(st: tcombobox): tsectiontype;
begin
if st.Selected.Text = 'STvertical' then result: = ST_vertical
else if st.Selected.Text = 'STcurved' then result: = ST_vertical
else if st.Selected.Text = 'STstraight' then result: = ST_vertical
else begin result: = ST_vertical; end;
end;
begin
if sender = OTVD then
begin
Fdata[ord(df_TVD)]: = getvalue(OTVD);
end
else
begin
if sender = OMD then
begin
Fdata[ord(df_MD)]: = getvalue(OMD);
end
else
begin
if sender = OVS then
begin
Fdata[ord(df_VS)]: = getvalue(OVS);
end
else
begin
if sender = OInc then
begin
Fdata[ord(df_Inc)]: = getvalue(OInc);
end
else
begin
if sender = OAlfa30 then
begin
Fdata[ord(df_Alfa30)]: = getvalue(OAlfa30);
end
else
begin
if sender = Osectiontype then
begin
Fsectiontype: = gettype(Osectiontype);
end
else
Exception.Create('sender unknown');
end;
end;
end;
end;
end;
end;
function tdragdata.getdata: tdragdatafields;
begin
result.TVD: = Fdata[ord(df_TVD)];
result.MD: = Fdata[ord(df_MD)];
result.VS: = Fdata[ord(df_VS)];
result.Inc: = Fdata[ord(df_Inc)];
result.Alfa30: = Fdata[ord(df_Alfa30)];
result.sectiontype: = Fsectiontype;
end;
procedure tdragdata.ondeletebtnclick(sender: tobject);
begin
self.Release;
end;
end.
Upvotes: 0
Views: 245
Reputation: 2591
I found something interesting about FreeNotification()
method here.
Use FreeNotification to register AComponent as a component that should be notified when the component is about to be destroyed. It is only necessary to register components this way when they are in a different form or have a different owner. For example, if AComponent is in another form and uses the component to implement a property, it must call FreeNotification so that its Notification method is called when the component is destroyed.
For components with the same owner, the Notification method is called automatically when an application explicitly frees the component. This notification is not sent out when components are freed implicitly, because the Owner is already being freed.
And then when I removed the line
a.FreeNotification(self);
In the method (the first component)
procedure Tncrdragdata.additem(Aname:string);
And the problem was gone.
I think the problem is that I was calling the FreeNotification() method with Tdragdata, not having a different owner. Clearly I was breaking a rule.
Thanks to @victoria and @CraigYoung for their help.
Upvotes: 1