Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
 Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
К сожалению у стандартного дерева в Firemonkey нет возможность подключить таблицу типа "id, parent, name".
Поэтому приходится извращаться, чтобы заполнить дерево.
Зачем нужен проход по всему дереву? Чтобы найти к чему прикрепить очередной узел, т.е. найти родителя.

т.е. процедура построения дерева такая:
function TfmTree.BuildTree: boolean;
Var
  TreeViewItem, TreeViewItemParent: TTreeViewItem;
begin
// 0 - id, 1- parent, 2 - name
// fmTree.qTree.SQL.Text := 'select id, parent, name from regions order by parent';

  if not qTree.Active then qTree.Open;
  if qTree.RecordCount <= 0 then exit(false);
  qTree.First;
  TreeView1.Clear;
  TreeView1.BeginUpdate;


  try
    while not qTree.Eof do
    begin
      if VarIsNull(qTree.Fields[1].Value) then
      begin
        AddChild(nil, qTree.Fields[2].Value, qTree.Fields[0].Value, TreeView1);// создаем root (нулевые)  TTreeViewItem`ы

      end
      else
      begin
        TreeViewItem := ItemByTag(qTree.Fields[1].Value);// ищем родителя, у которого tag = Fields[1]
        AddChild(TreeViewItem, qTree.Fields[2].Value, qTree.Fields[0].Value, TreeView1);// добавляем дочерний TTreeViewItem
      end;// else

      qTree.Next;
    end;// while

  finally
    TreeView1.EndUpdate;

  end;
  result := True;
end;



проблема с функцией ItemByTag:
function TfmTree.ItemByTag(id: integer): TTreeViewItem;
var
  Item: TTreeViewItem;
  I: Integer;
begin
// взял как пример на основе ItemByText из исходников FMX
  Result := nil;
  for I := 0 to Pred(TreeView1.GlobalCount) do
  begin
    Item := TreeView1.ItemByGlobalIndex(I);
    if Item.Tag = id then
      Exit(Item);
  end;
end;

из-за того, что само дерево не предоставляет глобального индекса, а ClobalCount - это всего лишь "количество всех видимых элементов дерева (первого уровня и подэлементов) в этом древовидном представлении".
http://docwiki.embarcadero.com/Libraries/Berlin/en/FMX.TreeView.TCustomTreeView.GlobalCount


ну и собственно AddChild
+

Function TfmTree.AddChild(parent: TFMXObject; Text: string; key_val: integer; TreeView: TTreeView): TTreeViewItem;
begin
  Result := TTreeViewItem.Create(TreeView);

  if not assigned(parent) then
    Result.parent := TreeView
  else
    Result.Parent := parent;


  Result.Text:= text;
  Result.Tag := key_val;
  if not assigned(parent) then
    OutputDebugString(pwidechar('add: ' + Result.Text))
  else
    OutputDebugString(pwidechar('add: ' + TTreeViewItem(parent).Text + '.' + Result.Text));
end;



Мне предлагают какого-то монстра (EnumControls), чтобы пройтись по всему дереву:
http://docwiki.embarcadero.com/Libraries/Seattle/en/FMX.Controls.TControl.EnumControls
А как им пользоваться, моих мозгов скилов не хватает, чтобы переложить тамошний пример на мою задачу.
Заранее благодарен за любую помощь.

Может быть кто-то знает, как проще наполнить дерево FMX из таблицы?
Delphi 10.1 (multi-platform app)
3 апр 17, 20:24    [20362724]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
Да, я понимаю, что построение всего дерева сразу - не комильфо, а нужно строить по мере открытия узлов.
Но по дереву нужно организовать поиск, получается, что нужно его сперва построить.
3 апр 17, 20:25    [20362732]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3939
X11,

>> select id, parent, name from regions order by parent

Однопроходный вариант работать не будет из-за того, что элементы могут создаваться в разное время (id идут не последовательно). Можно использовать оракловский запрос для деревьев с connect by level, чтобы правильно отсортировать, либо делать заполнение в два прохода (сначала плоский список, а потом по родителям перемещаем -- я так у себя девовский трилист сортирую).
3 апр 17, 22:45    [20363174]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
чччД
Guest
X11,
хочешь TVirtualTreeView на FMX, славы и денег?

Портируй VTV на FMX!
3 апр 17, 23:59    [20363295]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
чччД
Guest
X11
Да, я понимаю, что построение всего дерева сразу - не комильфо, а нужно строить по мере открытия узлов.
Но по дереву нужно организовать поиск, получается, что нужно его сперва построить.

Если ты все еще используешь FireBird (а также мс скл, оракл...) - примени CTE:

7067436
4 апр 17, 00:04    [20363301]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
чччД
Guest
X11, и для поиска вовсе нет нужды строить все дерево: ищи нужный узел в базе, потом вытягивай всю цепочку родителей (до корня), а потом, в дереве, начиная от корня, пробегись по цепочке в обратную сторону, если нужно, подгружая пока отсутсвующие элементы, в итоге ты быстро найдешь нужный узел без перебора всего дерева.
4 апр 17, 00:09    [20363303]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
JaDi
Однопроходный вариант работать не будет из-за того, что элементы могут создаваться в разное время (id идут не последовательно).


у меня "order by parent", значит родители всегда будут раньше создаваться
4 апр 17, 08:31    [20363483]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
чччД
хочешь TVirtualTreeView на FMX


я его боюсь
4 апр 17, 08:32    [20363488]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
чччД
Если ты все еще используешь FireBird


на андроиде - SQLite

FireBird c Андроидом плохо дружит
4 апр 17, 08:33    [20363489]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
Ребята, тут проблема не с базой данных, а с сами деревом. Нужно абстрагироваться от БД.
4 апр 17, 08:33    [20363491]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
В общем, дали пример. На большом количестве записей не знаю, будет ли медленная загрузка.
http://fire-monkey.ru/topic/4018-цикл-по-ttreeview-обходит-не-все-уровни/?do=findComment&comment=25397

Работает.
Function TfmTree.ItemByTag(id_parent: integer): TTreeViewItem;
var
  aItem: TTreeViewItem;
begin
  aItem := nil;

  TreeView1.EnumControls(
    function(Control: TControl): TEnumControlsResult
    begin
      if (Control is TTreeViewItem) then
      begin
        if TTreeViewItem(Control).Tag = id_parent then
        begin
          aItem  := TTreeViewItem(Control);
          Result := TEnumControlsResult.Discard;
        end
        else
          Result := TEnumControlsResult.Continue;
      end
      else
        Result := TEnumControlsResult.Continue;
    end);

  Result := aItem;
end;
4 апр 17, 08:36    [20363494]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3939
X11
JaDi
Однопроходный вариант работать не будет из-за того, что элементы могут создаваться в разное время (id идут не последовательно).


у меня "order by parent", значит родители всегда будут раньше создаваться

Нет, не будут. Если поменяется структура дерева (какую-то ветку перебросят на уровень выше), всё накроется медным тазом.
4 апр 17, 10:19    [20363927]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
Кто перебросит?
Кто перебросит в момент построения дерева?
4 апр 17, 10:26    [20363968]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
JaDi
Member

Откуда: Сызрань, Россия
Сообщений: 3939
X11,

речь про дерево в базе данных, которое может измениться или быть записанным совсем не в той последовательно, которая нам удобна.
4 апр 17, 10:48    [20364079]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
Не забывайте, что речь не про сетевое приложение, а про локальное.
4 апр 17, 11:38    [20364398]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
Может кому-то пригодится.

function BuildTree(TreeView1: TTreeView; qTree: TUniQuery): boolean;
Var
  TreeViewItem: TTreeViewItem;
begin
  qTree.close;
  qTree.Open;// select id, parent, name
  if qTree.RecordCount <= 0 then exit(false);
  qTree.First;
  TreeView1.Clear;
  TreeView1.BeginUpdate;

  try
    while not qTree.Eof do
    begin
      if VarIsNull(qTree.Fields[1].Value) then
        AddChild(nil, qTree.Fields[2].Value, qTree.Fields[0].Value, TreeView1)
      else
      begin
        TreeViewItem := ItemByTag(TreeView1, qTree.Fields[1].Value);
        TreeViewItem.Name := 'TreeViewItem' + qTree.Fields[1].AsString;
        TreeViewItem.Height := 40;
        AddChild(TreeViewItem, qTree.Fields[2].Value, qTree.Fields[0].Value, TreeView1);
      end;// else

      qTree.Next;
    end;// while

  finally
    TreeView1.EndUpdate;

  end;
  result := True;
end;


procedure AddChild(parent: TFMXObject; Text: string; key_val: integer; TreeView: TTreeView);
Var
 TreeViewItem: TTreeViewItem;
 ALabel: TLabel;
begin
  TreeViewItem := TTreeViewItem.Create(TreeView);
  TreeViewItem.Name := 'TreeViewItem' + key_val.ToString;
  TreeViewItem.StyleLookup := 'treeviewitemstyle';

  if not assigned(parent) then
    TreeViewItem.parent := TreeView
  else
    TreeViewItem.Parent := parent;

  ALabel := TLabel.create(TreeViewItem);
  ALabel.Parent := TreeViewItem;
  ALabel.Align := TAlignLayout.Right;
  ALabel.TextSettings.HorzAlign := TTextAlign.Trailing;
  ALabel.Text := key_val.ToString;
//  ALabel.Padding.Right := 5;

  TreeViewItem.Text:= text;
  TreeViewItem.Tag := key_val;
end;


Function ItemByTag(TreeView1: TTreeView; id_parent: integer): TTreeViewItem;
var
  aItem: TTreeViewItem;
begin
  aItem := nil;

  TreeView1.EnumControls(
    function(Control: TControl): TEnumControlsResult
    begin
      if (Control is TTreeViewItem) then
      begin
        if TTreeViewItem(Control).Tag = id_parent then
        begin
          aItem  := TTreeViewItem(Control);
          Result := TEnumControlsResult.Discard;
        end
        else
          Result := TEnumControlsResult.Continue;
      end
      else
        Result := TEnumControlsResult.Continue;
    end);

  Result := aItem;
end;


Function FindItemByText(val: String; TreeView: TTreeView): TTreeViewItem;
var
  aItem: TTreeViewItem;
begin
  aItem := nil;
  val := AnsiUpperCase(val);

  TreeView.EnumControls(
    function(Control: TControl): TEnumControlsResult
    begin
      if (Control is TTreeViewItem) then
      begin
        if ContainsText((TTreeViewItem(Control).Text), val) then
        begin
          aItem  := TTreeViewItem(Control);
          Result := TEnumControlsResult.Discard;
        end
        else
          Result := TEnumControlsResult.Continue;
      end
      else
        Result := TEnumControlsResult.Continue;
    end);

  Result := aItem;
end;


procedure SelectItemByText(const sText: String; TreeView: TTreeView);
var
 TreeViewItem: TTreeViewItem;
begin
  TreeViewItem := FindItemByText(sText, TreeView);
  if assigned(TreeViewItem) then
    TreeViewItem.Select;
end;
8 янв 18, 18:43    [21087946]     Ответить | Цитировать Сообщить модератору
 Re: Проход по всему дереву FMX.TTReeView  [new]
X11
Member

Откуда: Kharkiv, Ukraine
Сообщений: 12562
При работе в Firemonkey с TTreeViewItem нужно помнить, что есть Parent, а есть ParentItem.
Т.е. если нужен цикл по родителям, то нужно шагать по ParentItem

function GetPathFromTreeViewItem(AItem: TTreeViewItem): String;
begin
  if AItem = nil then exit('nothing selected');

  while Assigned(AItem) and (AItem is TTreeViewItem) do
  begin
    Result := AItem.Text + '\' + Result;
    AItem := TTreeViewItem(AItem.ParentItem);
  end;

  Delete(Result, Length(Result), 1);
end;
8 янв 18, 19:06    [21087979]     Ответить | Цитировать Сообщить модератору
Все форумы / Delphi Ответить