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.
I found something interesting about
FreeNotification()
method here.And then when I removed the line
In the method (the first component)
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.