Перейти к содержанию
Fire Monkey от А до Я
  • 0

Как правильно программно удалить TListBoxItem из стилевого объекта?


Kikoma

Вопрос

Следующая ситуация:

 

Есть база данных с полями: id, product_name, price, cart (корзина) - Думаю пояснять излишне.

 

по условию cart>0 формируется запрос и заполняется TListBox кастомизированным Item-ом который содержит SpinBox. при изменении SpinBox вызывается процедура, которая вносит изменение в БД (cart) и высчитывает сумму в этом Item.

 

Все работает, все хорошо, но...

Задумал я что при SpinBox = 0, у меня этот TListBoxItem исчезал, для этого на изменение SpinBox если он равен 0, я запускаю процедуру формирования (Заполнения) этого TListBox заново.

 

Вываливается ошибка Access ..to address XXX, при чем при пошаговой трассировки исключение вызывает FMX.Edit строка 3811 CustomEditBox.Change; в procedure TValueRangeCustomEditBox.DoAfterChange; (DELPHI XE6)

 

т.е. моя процедура полностью отрабатывается (Заполняется новый список Item-ов) и возникает эта ошибка.

 

При чем на 32-bit Windows все работает нормально, только на андроиде возникает это исключение, при чем приложение продолжает нормально функционировать.

 

Если я правильно понимаю, то эта процедура DoAfterChange пытается что то сделать с объектом, которого уже нет.

 

Это баг или я неправильно алгоритм построил?

 

 

Ссылка на комментарий

Рекомендуемые сообщения

  • 0
  • Администраторы

Добрый день,
 
Неправильно построили алгоритм. При скрытии стилизованных контролов, контрол выгружает (удаляет) свой стиль. Поэтому, когда вы из объекта стиля скрываете стилизуемый контрол (TListBoxItem), то он в свою очередь удаляет ваш стиль вместе с TSpinBox. В итоге управление возвращается у уже полностью разрушенный объект и вы получаете исключение.
 
Чтобы в вашем варианте скрыть TListBoxItem есть триспособа:

  • Сделать отложенное удаление штатными средствами FireMonkey:
    ListBoxItem1.Release;
    
  • Явно запретить TListBoxItem выгружать стиль, когда он скрывается со сцены. Это можно сделать так:
    ListBoxItem1.DisableDisappear := False
    
  • Поставить в очередь скрытие итема через TThread.Queen. В этом случае ваша процедура скрытия вызовется после всех действий.
    TThread.Queue(TThread.CurrentThread, procedure begin
        ListBoxItem1.Visible := False;
      end);
    
  • Сделать отложенное скрытие. То есть после изменения значения TSpinBox, вы можете, например, запустить таймер на 500-600 мс и по истечении времени скрыть итем. Правда по мне так этот способ не надежный и я бы не советовал не использовать.

P.S. Если хотите удалить итем из стилевого объекта, то лучше делать тогда вторым способом.

Изменено пользователем Brovin Yaroslav
добавлен способ с Release
Ссылка на комментарий
  • 0
  ... 
   if count = 0 then
    begin
      TThread.Queue(TThread.CurrentThread,
        procedure
        begin
          Item.Visible := False;
          Timer1.Enabled := True; // через 0,5 секунды перезагружаю TListBox
        end);

      // TabCartClick(Sender);
    end;
  end;

Все работает отлично. Большое спасибо.

 

Продолжение вопроса о красивом закрытии элемента (добавлении анимации) здесь

Ссылка на комментарий
  • 0

Я вызываю процедуру заполнения ListBox? т.к. там идет проверка или заполняем TListBoxItem-ами, или выводим один один Итем - Корзина пуста. Если не вызывать процедуру заполнения, то придется выполнять проверку -  а все ли итемы показываются и выводим итем "Корзина пуста". - Как то сложно и неправильно.

 

В Queen все равно выходит ошибка.

 

Выполняется анимация на закрытие Итема, и когда уже не находимся в процедуре под Итемом, выполняется Таймер и заново заполняется TListBox.

 

Все отлично работает через таймер. Плохо что ли?

Ссылка на комментарий
  • 0
  • Администраторы

Совершенно забыл о самом простом и существующем способе отложенного удаления объектов. У каждого контрола есть метод Release, который осуществляет отложенное удаление объекта. Используйте его:

Item.Release
Ссылка на комментарий
  • 0

Спасибо. Все прекрасно заработало, но только с обязательной перезаполнением ListBox

 

вот итоговый код:

    if (count = 0) and (TabControl1.ActiveTab = TabCart) then
      TThread.Queue(TThread.CurrentThread,
        procedure
        begin
          // Запускаем анимацию
          TListBoxItem(Item).AnimateFloat('Height', 0, 0.5);
          TListBoxItem(Item).AnimateFloatWait('Opacity', 0, 0.5);
          Item.Release;
          TabCartClick(Sender);  // запуск перерисовки ListBox
          // Timer1.Enabled := True; // запуск TabCartClick(Sender); через 0,3 сек
        end);

В моем случае перезаполнение TListBox обязательно, т.к. в SpinBox.Tag хранятся индексы Итемов.

Ссылка на комментарий
Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...