删除 FMX 对象的事件处理程序



我有以下组件,tncrdragdata(tframedscrollbox)tdragdata(tgroupbox)

主要思想是将它们组合并用作列表框(我需要这种方式)。

分组箱包含五个tedit,一个组合框和一个tbutton

问题是当我尝试释放其事件处理程序中的tdragdata时。

我使用FreeNotification方法重新定位framedscrollbox中的分组框。问题是由于我不知道的某种原因,覆盖的通知方法被执行了两次。

我的问题是:为什么重写的方法执行两次?

如果我删除条件(self.components[index]<>AComponent)在重新定位项目方法中,我得到了一个 AV。当我调试它时,我注意到该方法执行了两次。

这是两个组件的代码:

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:UsersnacereddineDesktopdown-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.

我在这里发现了FreeNotification()方法的一些有趣的东西。

使用免费通知将 AComponent 注册为应 在组件即将销毁时收到通知。它只是 当组件位于不同的位置时,需要以这种方式注册组件 形成或具有其他所有者。例如,如果 Aomponent 在 另一个窗体并使用组件来实现属性,它必须 调用 FreeNotification,以便在以下情况下调用其通知方法 组件被销毁。

对于具有相同所有者的组件,将调用 Notification 方法。 当应用程序显式释放组件时自动。这 隐式释放组件时不会发出通知, 因为所有者已经被释放了。

然后当我删除该行时

a.FreeNotification(self);

在方法中(第一个组件)

procedure Tncrdragdata.additem(Aname:string);

问题就消失了。

我认为问题是我用 Tdragdata 调用了 FreeNotification() 方法,而不是有不同的所有者。显然,我违反了一个规则。

感谢@victoria和@CraigYoung的帮助。

最新更新