Удаление объекта FMX внутри его обработчика событий

У меня есть следующие компоненты, tncrdragdata (tframedscrollbox) tdragdata (tgroupbox)

Основная идея состоит в том, чтобы объединить их и использовать их в качестве списка (мне это нужно таким образом).

Групповая коробка содержит пять tedit, один Tcombobox и один 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:\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.

1 ответ

Я нашел что-то интересное о FreeNotification() метод здесь.

Используйте FreeNotification для регистрации AComponent в качестве компонента, который должен быть уведомлен, когда компонент собирается быть уничтоженным. Таким образом, необходимо регистрировать компоненты только тогда, когда они находятся в другой форме или имеют другого владельца. Например, если AComponent находится в другой форме и использует компонент для реализации свойства, он должен вызвать FreeNotification, чтобы его метод Notification вызывался при уничтожении компонента.

Для компонентов с одним и тем же владельцем метод Notification вызывается автоматически, когда приложение явно освобождает компонент. Это уведомление не отправляется, когда компоненты освобождаются неявно, потому что Владелец уже освобождается.

А потом когда я убрал строку

a.FreeNotification(self);

В методе (первый компонент)

procedure Tncrdragdata.additem(Aname:string);

И проблема исчезла.

Я думаю, что проблема в том, что я вызывал метод FreeNotification() с Tdragdata, не имея другого владельца. Ясно, что я нарушал правило.

Спасибо @victoria и @CraigYoung за помощь.

Другие вопросы по тегам