Для ускорения DBTreeView в данном алгоритме реализована так называемая динамическая подгрузка веточек. Суть в том, что в сначала загружаются все узлы нулевого уровня. Потом, при каждом Expanding узла, если у него есть потомки – дозагружаются его потомки первого уровня и так далее. В результате грузятся только те узлы, которые нужны, а это экономит ресурсы.
Особой проблемой было отображение плюсика возле узла. Плюсик существует если есть хотя бы один ребенок, но поскольку в этом алгоритме загружается только один уровень в один момент времени, то плюсика не было. Пришлось искусственно добавлять GhostNode и удалять его тогда, когда это не требуется.
Основная рабочая процедура ExpandingUpdate из модуля uUpdateTree.
В остальном – ничего сложного – работа с базой и деревом. Запросы цепляются из модуля SQLQueries.
Первоначальное раскрытие дерева происходит следующим образом
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
procedure TDBTreeView.InitialUpdate; var UpdateTree:TUpdateTree; DMEtcetraLocal:TDMEtcetra; begin try Screen.Cursor:=crSQLWait; //Do_Something //Очистка дерева DMEtcetraLocal:=TDMEtcetra.Create(Self); DMEtcetraLocal.ClearTree(Self); FreeAndNil(DMEtcetraLocal); //Обновление дерева UpdateTree:=TUpdateTree.Create(Self); UpdateTree.ExpandingUpdate(nil,Self); // <<Раскрываем нулевой уровень if Self.Items.GetFirstNode<>nil then // <<Раскрываем первый узел нулевого уровня Self.Items.GetFirstNode.Expand(false); finally Screen.Cursor:=crDefault; FreeAndNil(UpdateTree); end; |
Также нужно в onEXpanding прописать следующее
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
procedure TDBTreeView.DBTreeViewExpanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); var UpdateTree:TUpdateTree; begin If not FEnableExpanding then exit; UpdateTree:=TUpdateTree.Create(Self); UpdateTree.ExpandingUpdate(Node,Self); // << Теперь раскрытие будет на каждом узле с детьми FreeAndNil(UpdateTree); end; |
Далее представлен код модуля uUpdateTree
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
unit uUpdateTree; interface uses System.SysUtils, System.Classes,vcl.dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.MySQLDef, FireDAC.VCLUI.Wait, FireDAC.Comp.UI, FireDAC.Phys.MySQL, Data.DB, FireDAC.Comp.Client,uTreeView,Vcl.ComCtrls,uDBConnectionBPL,uSQLQueries; type TUpdateTree=class(TComponent) private procedure GhostNodeDelete(Node: TTreeNode; DBTreeView: TDBTreeView); public procedure ExpandingUpdate(Node: TTreeNode; DBTreeView: TDBTreeView); end; //-------------------------------------------------------------------------------------------------- IMPLEMENTATION //-------------------------------------------------------------------------------------------------- { TUpdateTree } //----------------------------ExpandingUpdate------------------------------------------------------- procedure TUpdateTree.ExpandingUpdate(Node: TTreeNode; DBTreeView: TDBTreeView); var Buffer:string; id, Obj, TrashPointer:^integer; // Указатели для вставки узлов, см. код ChildNode:TTreeNode; // Добавляемый узел GhostNode:TTreeNode; // Чтобы у последнего узла, если у него есть дети, был плюсик qSelect,qSelectChildren:TFDQuery; DBConnectionBPL:TDBConnectionBPL; SQLQueries:TSQLQueries; idField,NameField:string; IsCheckedField:string; begin idField:=DBTreeViewGlobal.DBTreeViewTable.id; NameField:=DBTreeViewGlobal.DBTreeViewTable.NodeName; IsCheckedField:=DBTreeViewGlobal.DBTreeViewTable.IsChecked; //Создание qSelect:=TFDQuery.Create(Self); qSelectChildren:=TFDQuery.Create(Self); SQLQueries:=TSQLQueries.Create(Self); DBConnectionBPL:=TDBConnectionBPL.Create(Self); qSelect.Connection:=DBConnectionBPL.FDConnection; qSelectChildren.Connection:=DBConnectionBPL.FDConnection; GhostNodeDelete(Node, DBTreeView); //--------------------------------РАБОТА С QSELECT-------------------------------------------------- Buffer:=qSelect.SQL.Text; new(id); if Node<>nil then begin id^:=integer(Node.Data^); end else id^:=-1; //Выбираем из базы всех детей узла qSelect.SQL.Text:=SQLQueries.SelectAllChildren.Text+id^.ToString+ ' order by '+'`'+DBTreeViewGlobal.DBTreeViewTable.IndexField+'`'; // 'SELECT * FROM treeview_db.tree where IdParent='+id^.ToString // +' order by `index`'; dispose(id); // Предотвращаем повторное добавление в узел (наблюдал за программой - выявил повторное добавление) if Node<>nil then if Node.Count<>0 then exit; with qSelect do begin Open(); while eof<>true do // Начинаем перебирать все записи begin //Добавление дочернего узла из текущей записи к Node New(Obj); Obj^:=FieldByName(idField).AsInteger; ChildNode:=DBTreeView.Items.AddChildObject(Node,FieldByName(NameField).AsString,Obj); //--------------------Добавление GhostNode------------------------ // Проверяем есть ли дети у узла, чтобы добавить Ghost, если нужно qSelectChildren.SQL.Text:=SQLQueries.SelectAllChildren.Text+Integer(Obj^).ToString; // 'SELECT * FROM treeview_db.tree where idParent='+Integer(Obj^).ToString; qSelectChildren.Open(); // Добавлем Ghost, если у узла есть дети, чтобы у последнего узла был плюсик //И его можно было раскрыть. Этот Ghost при раскрытии будет удаляться if not qSelectChildren.IsEmpty then begin new(TrashPointer); GhostNode:=DBTreeView.Items.AddChildObject(ChildNode,'GhostNode',TrashPointer); GhostNode.Parent.Collapse(false); // <<Прячем его end; qSelectChildren.Close(); //--------------------Добавление GhostNode---------------------------- //Расставляем галочки согласно базе if FieldByName(IsCheckedField).AsBoolean=true then ChildNode.StateIndex:=2 else ChildNode.StateIndex:=1; Next; end; Close; // qSelect // DBTreeView.Items.EndUpdate; qSelect.SQL.Text:=Buffer; // Возвращаем запрос на место end; //Освобождение FreeAndNil(DBConnectionBPL); FreeAndNil(qSelect); FreeAndNil(SQLQueries); // end Expanding Update end; //-------------------------------------------------------------------------------------------------- //---------------------Вспомогательная процедура удаления Ghost узла procedure TUpdateTree.GhostNodeDelete(Node: TTreeNode; DBTreeView: TDBTreeView); begin if Node <> nil then begin // Удаляем Ghost - вспомогательный узел для динамической подгрузки if (Node.HasChildren) and (Node.Item[0].Text = 'GhostNode') then begin Dispose(Node.Item[0].Data); // << Вот так работает верно DBTreeView.Items.Delete(Node.Item[0]); end; end; end; end. |