Перейти к содержанию
  • Регистрация
  • 0
Kikoma

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

Вопрос

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

 

Есть база данных с полями: 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 хранятся индексы Итемов.

Поделиться сообщением


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

  • Похожий контент

    • От genakust
      Здравствуйте,
      я создал свой ListBoxItem (FMX) который выглядит следующим образом:

      затем я добавил Items динамически в ListBox и получилось следующее:

      Когда я запускаю программу на Windows 10 и выбираю мышкой Item он селектируется, а если запускаю на Андроид- таблете, то я не могу выбрать (селектировать) Item.
      Подскажите пожалуйста, какие могут быть причины длы такого поведения.
      Заранее спасибо.
       
    • От Barbanel
      Всем хай.
      Пол дня бьюсь, уже с ума начинаю сходить, и как назло не с кем посоветоваться сегодня. Выручайте)
      Суть:
      Есть TListBox. В него динамически из кода создаются TListBoxItem.
      Каждому итему в поле TagObject присваивается некий объект, хранящий дополнительные данные.
      По клику на итем, вытаскивается этот объект и в зависимости от данных, выполняются разные действия.
      Проблема:
      Под Windows ее собсно нет)
      Под Android при клике на TListBoxItem оказывается что TagObject = nil.
      Голова уже дымится, у меня закончились мысли, что это может быть...
      Код, на всякий:
      while not query.Eof do begin item := TListBoxItem.Create(lbTeilLeistungsArten); lbTeilLeistungsArten.AddObject(item); item.StyleLookup := 'listboxitemTL'; item.Height := 60; item.Text := query.FieldByName('Bezeichnung').AsString; item.OnClick := OnTeilleistungenClick; item.Tag := query.FieldByName('ID').AsInteger; item.DisableDisappear := true; item.ApplyStyleLookup(); lStObj := TStoreObject.CreateTL(query.FieldByName('ID').AsInteger); lStObj.TeilLeistung.Name := query.FieldByName('Bezeichnung').AsString; item.TagObject := lStObj; query.Next(); end; Всем заранее спасибо!
    • От Виталий Иванов
      Есть такая идея. 
      Загружаю в TListView много элементов (от 500 до 1000 может и более) ситуация вынуждает грузить именно столько . Так вот когда начинаю создавать их кастомно после создания приходится вызвать oItem.Adapter.ResetView(oItem) (Если этого не делать то не корректно рассчитывается высота и ещё пару багов )  что соответсвенно вызывает подвисание интерфейса на Windows все круто это не так в глаза бросается, а на Android печалька форма зависает от 10 до 30 секунд 
      Так вот сам вопрос можно ли как это сделать в отдельном потоке что бы пользователю показывать какой нибудь Waiter. Или может кто как по другому предложит реализовать ? 
      Смотрел в сторону динамической подгрузки итемов но хотелось бы что и его поиск работал . 
    • От gonzales
      Доброго времени суток!
      Решаю следующую задачу, в приложении динамически формируются разные объекты, наследники от одного класса. При формировании объектов заполняется динамический массив этих элементов. Далее я хочу в отдельном потоке для каждого из элементов массива получить его состояние, то есть делаю запрос к серверу. Все это повешено на таймер, каждую секунду должен отрабатываться запрос. Все более менее работает в Windows, а на Андроиде со временем приложение валится. Вот код таймера, для читаемости я удалил куски с различными вариантами E. RootElements - это массив TEssense от которого есть наследники. Функции GetBoardCurrentValue, GetBoardMaxValue - по сути запросы к серверу. 
      Подскажите, правильно ли я оформляю работу с потоками для работы на Андроиде?
      procedure TForm1.MasterTimerTimer(Sender: TObject); begin TTask.Run( procedure var l, d, a: byte; i,j:integer; E: TEssence; p: Pointer; VirtualNode: IXMLNode; VirtualElementNode: IXMLNode; id: byte; begin l := Length(Form1.RoomElements); for j := 0 to l - 1 do begin E := Form1.RoomElements[j]; // Реле if E is TRele then begin d := (E as TRele).Device_ID; a := (E as TRele).Device_Adress; if Form1.GetBoardCurrentValue(d, a) = true then begin TThread.Synchronize(nil, procedure begin (E as TRele).ReleSwitch.IsChecked := Form1.device[d].Board[a].CurrentValue.ToBoolean; end); end; // (E as TRele).ReleOnTimer(E) end // Диммер else if E is TDimmer then begin d := (E as TDimmer).Device_ID; a := (E as TDimmer).Device_Adress; if Form1.GetBoardMaxValue(d, a) = true then begin TThread.Synchronize(nil, procedure begin if (Form1.device[d].Board[a].Type_ID = TType.Светодиод) or (Form1.device[d].Board[a].Type_ID = TType.Диммер220) then begin (E as TDimmer).DimmerValue.Text := (Form1.device[d].Board[a].MaxValue).ToString; end; end); end; // (E as TDimmer).DimmerOnTimer(E) end // Таймер else if E is TSTimer then begin id := (E as TSTimer).STimerIndex; Form1.FillHTTPRequest(0, 0, HTTP_GET_TIMER_INFO, id); if Form1.AnswerIsComming = HTTP_GET_TIMER_INFO then begin TThread.Synchronize(nil, procedure begin if Form1.HTTPAnswer.Data1 = 0 then (E as TSTimer).Interval.Text := 'OFF' else (E as TSTimer).Interval.Text := 'ON' end); end; // (E as TSTimer).STimerOnTimer(E); end; end; end); end;   
    • От Barbanel
      Здравствуйте!
      Подскажите пожалейста, устанавливаются ли какие-либо стандартные Define при компиляции приложения для Play Store?
      Понятно что можно установить свои дефайны, разные для дебага и релиза, но хочется знать, существуют ли стандартные.
      Спасибо, всем хорошего дня =)
    • От Alex Bakulin
      Есть простая вроде бы задачка - в фоне загружать картинки для ListView. Делаю это так:
      procedure TImageThread.Execute; var i: integer; begin for i := 0 to FListView.Items.Count -1 do begin FimageIndex := i; Synchronize(LoadImage); end; end; Ожидается, что с ListView в это время будет относительно комфортно работать, и в фоне в ImageList будут подгружаться картинки и появляться в ListView. А на деле получается так, что все приложение виснет аццки на время загрузки картинок и не отвечает. И это как под Windows так и под Android.
    • От Mazzay
      Всем добрый день!
      Приоритет нитки для Windows имеет тип TThreadPriority:
      enum DECLSPEC_DENUM TThreadPriority : unsigned char { tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical }; __property TThreadPriority Priority = {read=GetPriority, write=SetPriority, nodefault}; Тут по названиям элементов множества всё предельно понятно.
       
      Для Android тип уже int:
      __property int Priority = {read=GetPriority, write=SetPriority, nodefault}; Нигде не могу найти возможные варианты значений для приоритета. Кто-нибудь знает?
       
    • От notricky
      На Android код, который работает исправно на Win  вызывает ошибку "CalledFromWrongThreadException: Only the original thread that created a view hierarcy can touch its views" 
      Смысл таков, что я пытаюсь показать форму из треда, у которой BorderStyle=none (роли это не играет).
      Решение в Андроиде заключается в том, чтобы пускать через  runOnUiThread  (то есть выполнять интерфейсные штуки в главном потоке). Как я понимаю, в firemonkey эту фичу должен выполнять Synchronize().
      Тем не менее, ошибка возникает.
      А при запуске в режиме дебага на андроид девайсе событие кнопки вообще не срабатывает иногда. А если срабатывает, то возникает описанное выше исключение.
      Я собрал тестовый пример и в нем не сразу видна ошибка, тогда как получил я ее на рабочем проекте.
      Цель: показать бизибокс на время бекграундных действий. Этот бизибокс у меня сначала был просто на каждой форме и я интерфейсно его вызывал, но теперь решил сделать отедльной формой (как и тоаст), но почему так происходит я не понял. Вы что скажете?
       
      unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; TWorkThread = class(TThread) public procedure Execute; override; end; var Form1: TForm1; implementation {$R *.fmx} uses unit2; procedure TForm1.Button1Click(Sender: TObject); var t: TWorkThread; begin t := TWorkThread.Create(True); t.FreeOnTerminate := true; t.Start; end; { TWorkThread } procedure TWorkThread.Execute; begin inherited; Self.Synchronize(procedure begin Form2.Show; Form2.Top := Form1.Top; Form2.Left := Form1.Left; Form2.BringToFront; end); Terminate; end; end.  
    • От Alex7wrt
      Добрый день.
      Знаю, что на форуме есть несколько тем о том, как удалять объекты под Android и счетчике ссылок, однако использование рекомендаций оттуда мне пока не помогло.
      Суть вопроса: создаю свой класс

       
      type TRext =class(TRectangle) Text: TText; procedure RextMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); procedure RextMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); procedure RextMouseLeave(Sender: TObject); Constructor Create(AOwner: TComponent); override; Destructor Destroy; override; end; type TMyChoose = class Item1, Item2, Item3: TRext; Edits: TEdit; procedure ItemClick(Sender: TObject); procedure OnEditFocus(Sender: TObject; var ACanFocus: Boolean); Constructor Create(Form: TForm); Destructor Destroy; override; end; ..... constructor TRext.Create(AOwner: TComponent); begin inherited Create(AOwner); Text:=TText.Create(Self); Text.Parent:=Self; Text.Align:=TAlignLayOut.Client; Self.Text.OnMouseDown:=RextMouseDown; Self.Text.OnMouseUp:=RextMouseUp; Self.Text.OnMouseLeave:=RextMouseLeave; end; Destructor TRext.Destroy; begin Text.Release; Text:=nil; inherited; end; constructor TMyChoose.Create(Form: TForm); begin inherited Create; Item1:=TRextCreate(Form); Item1.Parent:=Form; Item1.Align:=tAlignLayout.MostLeft; Item2:=TRextCreate(Form); Item2.Parent:=Form; Item2.Align:=tAlignLayout.MostLeft; Item3:=TRextCreate(Form); Item3.Parent:=Form; Item3.Align:=tAlignLayout.MostLeft; Edits:=TEdit.Create(Form); Edits.Parent:=Form; Edits.Align:=tAlignLayout.MostLeft; ......... end; Destructor TMyChoose.Destroy; begin Item1.Release; Item1:=nil; Item2.Release; Item2:=nil; Item3.Release; Item3:=nil; Edits.Release; Edits:=nil; inherited; end; Под Windows все нормально работает и уничтожается. Под Android вызов Destroy у объекта типа TMyChoose не приводит ни к чему.
      Вместо Release и nil использовал также DisposeOf и Nil, а также FreeAndNil - результат аналогичный.
      Как правильно уничтожать составные объекты?
    • От Steepe_Hare
      Как ведет себя приложение, написанное для Андроид, со множеством таймеров? Не подтормаживает ли? Корректно ли считают время таймеры?
      Делаю мобильное приложение что-то вроде Фермы или Запорожья, и там многое построено на отсчетах промежутков времени.
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

×
×
  • Создать...