Перейти к содержанию
  • 0
fil23

Как найти реальный индекс узла TTreeViewItem дерева?

Вопросы

Такая задача.

Хочу создать некоторое дерево вида:

Корень
  -потомок
  -потомок
Корень
  -потомок
  -потомок
   -потомок
Корень
  -потомок
    -потомок
      -потомок

и записать данную структуру в БД (FB). А потом в обратную сторону - из данных в БД, программно создать эту же структуру.

Но как в VCL не нашел Nodes, и absoluteindex. Так сказать не на что опереться в числовом представлении.

Попробовал использовать Globalindex - вышла неудача , он выдает "правильный" результат, т.е. какой он по счету только когда все ветви полностью раскрыты.

Думаю ладно, если тебе надо чтобы были все ветки раскрыты, пусть будут, решил использовать ExpandetALL а потом уже находить Globalindex, но увы опять неудача, он просто не разворачивает ветки (указатели меняют расположение) , а данные так и лежат свернутые. Нажимаешь на любую ветку и данные разворачиваются. Но код уже отработал и выдал не правильный индекс (такой как при свернутых ветках).

 

 

Проблема - в каком виде представлять объекты? хотелось бы в числовом по уникальному ID 

Как их находить?

 

Спасибо.

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


Ссылка на сообщение

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

  • 0

Коллега, вы совершенно не верно подходите к решению задачи. TreeView - это представление, а база данных - это модель. Потому построение дерева должно опираться на данных в БД, а не наоборот. 

Дерево в базе хранят обычно в одной таблице такой структуры:

id - id объекта

parent - id родителя

text - наименование

 

например ваша структура в БД будет примерно так выглядеть:

1, null, Родитель 1

   2, 1, Потомок 1-1

   3, 1, Потомок 1-1

4, null, Родитель 2

   5, 4, Потомок 2-1

   6, 4, Потомок 2-2

      7, 6, Потомок 2-3  - обратите внимание - потомок потомка

и т.д.

 

Для того что бы связать запись БД и Item в дереве используйте свойство Tag. Это просто и удобно. 

Как строить дерево:

1. Самый плохой способ  - при запуске программы, из БД считывается все данные и по ним строится полное дерево. При добавлении/удалении записи все повторяем - перечитываем полное дерево. Таким способом лучше не делать. За такой метод бьют ногами не похвалят. Причем и пользователи (за тормоза программы) и админ сервера БД (за нагрузку на сервер).

 

2. Этот способ немного лучше -  при запуске мы точно также считываем полное дерево из БД. Но при добавлении записей, мы добавляем только конкретный Item в дерево и его Tag присваиваем ID который назначил сервер при создании записи в БД. Но тут все еще есть недостаток - полное построение дерева - если записей много, то может занимать много времени.

 

3. Самый лучший способ - при запуске считываются только корневые записи (parent = null). А остальные подгружаются по мере необходимости. Когда пользователь разворачивает ветку, то из БД получаем все записи parent = Selected.Tag и добавляем детей к этой ветке.  Добавление новых записей так же как в способе 2.

 

Рекомендую вам использовать именно способ 3. Во первых он самый быстрый, во вторых считывание из БД наиболее простое. Иначе вам без рекурсивных хранимых процедур будет сделать полное чтение дерево очень сложно.  Здесь же можно все выбирать простыми запросами. 

Еще пара советов:

если данных много и по дереву ходят много - то постепенно программа подгрузит много веток и это может занимать много памяти, что может привести к тормозам. Потому при сворачивании ветки, лучше всех детей удалять. 

Когда ветка не имеет детей, то у нее нет слева стрелочки для разворачивания. Потому возникает вопрос - как же тогда развернуть ветку, чтобы подгрузить детей? Все просто. Надо создать одного фейкового ребенка. Т.е. просто Item-пустышку не связанную с БД. А когда ветку разворачивают, то первым делом надо пустышку удалить. Потом считать БД. Естественно при сворачивании делаем наоборот - сначала удаляем все реальные Item-ы, а потом вновь добавляем пустышку.

Вот как то так.

Отредактировал DirtyBorov

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


Ссылка на сообщение
  • 0

Если есть уникальное текстовое поле ищи itemByText, но самый правильный вариант в случае когда хранилище данных FB это 3. описанный выше. Если хранилище например XML файл, то уже не факт, что не нужно строить сразу.

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


Ссылка на сообщение

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


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

    • От x11
      Может кому пригодится.
      Немножко запутался я, т.к. в FMX почти любая визуальная компонента может быть контейнером для другой компоненты.
       
      Рабочий код.
      function GetPathFromNode(AItem: TTreeViewItem): String; begin if AItem = nil then exit('ничего не выбрано'); while Assigned(AItem) and (AItem is TTreeViewItem) do begin Result := AItem.Text + '/' + Result; AItem := TTreeViewItem(AItem.ParentItem); end; Delete(Result, Length(Result), 1); end;  
      проблема была у меня AItem.Parent
      а правильно - AItem.ParentItem
    • От x11
      Почему этот цикл "видит" только первый уровень дерева?
      Тогда как правильно организовать поиск нужного TreeViewItem`а по всем узлам, по всему дереву?
       
      function TfmTree.FindItemByIndex(id_parent: Integer): TTreeViewItem; var i: Integer; begin Result := nil; for i:= 0 to Pred(TreeView1.Count) do if TreeView1.Items[i].Tag = id_parent then begin Result := TreeView1.Items[i]; break; end; end;  
    • От david_yusupov
      Точнее, как работает наследование.
        TNodeItem = class(TTreeViewItem)   public     constructor Create(AOwner: TComponent); override;     destructor Destroy; override;   end;     TNodeItem2 = class(TNodeItem)   public     constructor Create(AOwner: TComponent); override;     destructor Destroy; override;   end; реализация { TNodeItem } constructor TNodeItem.Create(AOwner: TComponent); begin   inherited Create(AOwner); end; destructor TNodeItem.Destroy; begin   inherited; end; { TNodeItem2 } constructor TNodeItem2.Create(AOwner: TComponent); begin   inherited Create(AOwner); end; destructor TNodeItem2.Destroy; begin   inherited; end; // Вот самое интересное, не много не понятно почему так
      procedure TForm1.Button2Click(Sender: TObject); var   xTree: TTreeViewItem; begin   // Тут все работает   xTree := TTreeViewItem.Create(nil);   xTree.Text := 'Тест2';   xTree.Parent := TreeView1; end; procedure TForm1.Button3Click(Sender: TObject); var   xTree: TNodeItem; begin   // И тут все работает   xTree := TNodeItem.Create(nil);   xTree.Text := 'Тест3';   xTree.Parent := TreeView1; end; procedure TForm1.Button4Click(Sender: TObject); var   xTree: TNodeItem2; begin   // А вот тут не работает, узел добавляется, а текста нет.   xTree := TNodeItem2.Create(nil);   xTree.Text := 'Тест4';   xTree.Parent := TreeView1; end;  
    • От golomeen
      Поэкспериментировал и нашел простейший случай - узел и в нем два листа.
      Алгоритм следующий (упрощенно)
      каждый лист показывает пару key=value
      при клике в узел остается только key= и показывается листвью с выбором value. Затем мы получаем грубо key=new value (хотя не обязательно)
      После такой операции вы обязательно получаем то что 2 листа меняются местами
       
      С узлом ничего не происходит ни программно ни фактически
       
      В более сложных случаях тоже шевеление наблюдается, но я его не изучал подробно
    • От slava_d2000
      Добрый день!
       
      Прошу подсказать как программно выделить элемент дерева.
    • От Dzuyba
      Мне требуется динамически добавлять и удалять элементы дерева. И чтобы лучше понять как работать с FMX.TreeView.TTreeView я написал простой тестовый примерчик и который не могу заставить работать. Приложение вообще падает. Может подскажите где я ошибаюсь.
       
      Описание тестового примера
      Нужно когда пользователь разворачивает (Expand) элементы дерева, я хочу удалять все дочерние элементы затем добавлять новые дочерние элементы и потом уже пускай родительский TreeViewItem раскрывается (Expand) для показа новых дочерних элементов.
       
      Для эмуляции OnExpand и OnCollapse пользуюсь способом, описанным тут: Как можно реализовать в TTreeView события OnExpand и OnCollapse?
       
      Тест кейс
      Достаточно создать корневой элемент, В него добавит дочерний элемент  Потом раскрывать (Expand) корневой элемент. И приложение падает. Когда происходит ошибка
      Добавляешь корневой элемент дерева, потом в него дочерний элемент. Пытаешься развернуть (Expand) корневой элемент (кликая мышкой по плюсику) и приложение падает.
       
      Тестовый проект: Test_Tree.zip
    • От Dzuyba
      Как можно реализовать данное событие а лучше целых 2 события OnExpand и OnCollapsed
  • Последние посетители   0 пользователей онлайн

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

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