В связи с тем, что переделал алгоритм загрузки данных для дерева до динамического, пришлось переделать алгоритм удаления из базы. При динамической подгрузке в TreeView видны узлы лишь до уровня, открытого пользователем. Всё что глубже – просто не загружено. Поэтому как минимум не получается полного прохода по дереву или по узлу.
Пользователь выбирает узел и хочет его удалить. У этого узла возможно есть дети, у которых свои дети и так далее. Чтобы удалить всё поддерево, я придумал следующий способ.
На первом шаге в базе ищутся все дети выбранного узла, у каждого из этих детей ищутся их дети и так до последнего ребенка. В результате составляется список ID тех узлов для базы, которые нужно удалить.
На втором шаге происходит удаление записей поддерева из базы.
На третьем шаге удаляются узел и всего его потомки непосредственно из TreeView. Вот, что у меня получилось на уровне кода. Здесь только идея, без всемозможных защит и обработки ошибок.
В отдельном датамодуле – вспомогательные процедуры
Объявляем поле и свойство для хранения ID узлов для удаления в отдельном датамодуле
1 2 3 4 5 6 7 8 9 10 11 12 |
.... private FIDList:Tstringlist; { Private declarations } public property IDList:Tstringlist read FIDList write FIDList; { Public declarations } ... |
Выделяем память и указываем ссылку на выделенную область памяти
1 2 3 4 5 6 7 |
procedure TDMDB.DataModuleCreate(Sender: TObject); var i:integer; begin FIDList:=Tstringlist.Create; ... end; |
Ищем рекурсивно всех потомков и записываем их в FIDList:TstringList;
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 |
procedure TDMDB.FindChildren(NodeID:integer); // Рекурсивная процедура //Выбирает детей для узла с ID в базе NodeID, добавляет их в IDList //Для каждого ребенка, если у него есть дети - повторяется, уходит в глубь //Если у него нет детей, продолжает по верхнему уровню идти до последнего ребенка var qSelectChildren2:TFdquery; begin qSelectChildren2:=TFdquery.Create(Self); qSelectChildren2.Connection:=FDConnection; FIDList.Add( NodeId.ToString ); //<< Добавили родителя with qSelectChildren2 do begin //Выбираем из базы всех детей узла SQL.Text:='SELECT * FROM treeview_db.tree where IdParent='+NodeID.ToString; Open(); if IsEmpty then exit; //Выход, если пустое множество while not eof do begin FindChildren(FieldByName('id').AsInteger); Next; end; Close(); end; qSelectChildren2.Free; end; |
Создаем FDQuery под именем qDelete
Удаляем из базы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
procedure TDMDB.DeleteNodesByID(IDList: TStringlist); var i:integer; begin for i := 0 to IDList.Count-1 do begin with qDelete.Params do ParamValues['ID']:=IDList[i]; qDelete.ExecSQL; end; end; |
В модуле, где находится TreeView
Объявляем глобальную переменную
1 |
DMDB:TDMDB; |
Создаем её в OnCreate или OnShow (На FormClose, надо соответственно удалить её)
1 |
DMDB:=TDMDB.Create(Self); |
Далее, непосредственно удаление
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 |
procedure TMainForm.DeleteSubTree; var Node: TTreeNode; i, IDSelected:Integer; begin IDSelected:=Integer(TreeView1.Selected.Data^); //----------------Удаление из TreeView //Простой вариант удаления из TreeView1 TreeView1.Selected.DeleteChildren; TreeView1.Items.Delete(TreeView1.Selected); { //Другой вариант через проход по узлу //Небольшой проход по дереву в пределах одного узла //Идея в том, что у всех детей уровни отличные от уровня //родителя и след. соседа родителя - TreeView1.Items.BeginUpdate; Node := TreeView1.Selected; repeat TreeView1.Items.Delete(Node); Node := Node.GetNext; until not (Assigned(Node)) or (Node.Level = TreeView1.Selected.Level); TreeView1.Items.EndUpdate; } //-----------------Удаление из базы DMDB.FindChildren(IDSelected); DMDB.DeleteNodesByID(DMDB.IDList); end; |
Вешаем обработку на кнопку DeleteSubTree
1 2 3 4 5 6 7 8 9 |
procedure TMainForm.bDeleteSubTreeClick(Sender: TObject); begin if not Assigned(TreeView1.Selected) then exit; DMDB.IDList.Clear; DeleteSubTree; end; |
Тестируем
Я добавил в Folder2 500 узлов и нажал на DeleteSubtree, в результате Folder2 успешно удалилась и достаточно быстро. Не стал измерять производительность, но этот алгоритм должен работать быстрее. По первым впечатлениям – так и есть.