В данной статье посмотрим на удаление данных из дерева, поддерживающего связку с базой данных. Мы уже удаляли один узел, там нужно было удалить всех детей узла, в базе и в дереве, и только потом удалить сам узел. В данном примере расширим функциональность, теперь удаление будет происходить для всех выделенных с Shift либо выделенных с Ctrl узлов.
Вариант №3 (Рабочий)
Этот алгоритм удаляет любые выделенные узлы дерева. Причем они могут быть выделены как при помощи Shift так и при помощи Ctrl, то есть в слитном либо раздельном порядке. Я выкладываю алгоритмы в обратном порядке, вариант 3 на данный момент последний рабочий. Не самый быстрый, но рабочий.
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 |
procedure TDMDB.DeleteManyNodes3(Sender: TObject; TreeView: TTreeView); var i,k:integer; IndexOfMinimum:integer; // индекс элемента c минимальным значением в массиве DA:array of integer; //Вспомогательный массив для Selection Node:TTreeNode; // Для цикла удаления узлов begin if TreeView.SelectionCount=0 then exit; //Копируем Selections в массив и упорядочиваем его SetLength(DA,TreeView.SelectionCount); for i := 0 to TreeView.SelectionCount-1 do DA[i]:=TreeView.Selections[i].AbsoluteIndex; TArray.Sort<integer>(DA); //Сортируем массив //Когда удаляем i-й элемент, индексы в TreeView пересчитываются, и i+1 элемент становится i // Поэтому можно это учесть {СОБСТВЕННО УДАЛЕНИЕ ЭЛЕМЕНТОВ} TreeView.Items.BeginUpdate; for i := 0 to TreeView.SelectionCount-1 do begin //Удаляем всех детей while TreeView.Items.Item[DA[i]].HasChildren do begin Node:=TreeView.Items.Item[DA[i]].GetLastChild; while Node.HasChildren do Node:=Node.GetLastChild; DeleteRecord(Node); //Удаляем из базы TreeView.Items.Delete(Node); // Удаляем из дерева end; //Удаляем родителя DeleteRecord(TreeView.Items.Item[DA[i]]); TreeView.Items.Item[DA[i]].Delete; // Уменьшаем индекс в DA, чтобы в следующий раз удалить нужный элемент дерева for k := Low(DA) to High(DA) do DA[k]:=DA[k]-1; end; TreeView.Items.EndUpdate; end; |
Вариант№2 (Промежуточный)
Тут не всё так тривиально как это обычно бывает с удалением. Мало того, что сначала нужно удалять всех детей узла, так ещё и функция TreeView.Selections[i] работает нетривиальным образом. И какое-то время я с ней экспериментировал. Суть в том, что в зависимости от того, в каком направлении мы выделили элементы дерева, вверх или вниз – порядок индексов в Selections[i] будет меняться.
Я стал искать минимальный элемент в Selections[i], и используя то, что при удалении элемента, индексы пересчитываются, то можно удалять элемент с одним и тем же индексом вплоть до SelectionCount. Конечный, рабочий вариант кода получился таким.
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 |
procedure TDMDB.DeleteManyNodes2(Sender: TObject; TreeView: TTreeView); var i:integer; IndexOfMinimum:integer; // индекс элемента c минимальным значением в массиве DA:array of integer; //Вспомогательный массив для Selection Node:TTreeNode; // Для цикла удаления узлов begin if TreeView.SelectionCount=0 then exit; SetLength(DA,TreeView.SelectionCount); for i := 0 to TreeView.SelectionCount-1 do DA[i]:=TreeView.Selections[i].AbsoluteIndex; TArray.Sort<integer>(DA); IndexOfMinimum:=DA[0]; SetLength(DA,0);//Зануляем массив //Далее, когда удаляем i-й элемент, индексы в TreeView пересчитываются, и i+1 элемент становится i // Поэтому можно всё время удалять i-й элемент в пределах SelectionCount TreeView.Items.BeginUpdate; for i := 0 to TreeView.SelectionCount-1 do begin //Удаляем всех детей while TreeView.Items.Item[IndexOfMinimum].HasChildren do begin Node:=TreeView.Items.Item[IndexOfMinimum].GetLastChild; while Node.HasChildren do Node:=Node.GetLastChild; DeleteRecord(Node); //Удаляем из базы TreeView.Items.Delete(Node); // Удаляем из дерева end; //Удаляем родителя DeleteRecord(TreeView.Items.Item[IndexOfMinimum]); TreeView.Items.Item[IndexOfMinimum].Delete; end; TreeView.Items.EndUpdate; end; |
Это не быстрый алгоритм удаления. Но я и не гнался за скоростью. В данный момент я только разбираюсь с тем, как это вообще работает. Думаю, в будущем, возможно, напишу более быстрый вариант удаления, если не перескочу на VirtualStringView))
Вариант №1 (Медленный)
Здесь я использую ранее написанную функцию DeleteNode, её можно найти в первом посте серии DBTreeView своими руками. Здесь я каждый раз перед удалением, ищу узел по имени и удаляю его – это работает, но очень медленно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
procedure TDMDB.DeleteManyNodes(Sender: TObject; TreeView: TTreeView); var i,k:integer; SL:TStringList; begin SL:=TStringList.Create; for i := 0 to TreeView.SelectionCount-1 do begin SL.Add(TreeView.Selections[i].Text); end; TreeView.Items.BeginUpdate; for i := 0 to SL.Count-1 do begin FindNodeByText(Sl[i],TreeView); DeleteNode(Self,TreeView); end; TreeView.Items.EndUpdate; SL.Free; end |
Вспомогательно – поиск по имени (популярный код в интернете)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function TDMDB.FindNodeByText(Text:string; TreeView:TTReeView): TTreeNode; var Searching:boolean; Noddy:TTreeNode; begin Noddy:=TreeView.Items[0]; Searching:=true; while (Searching) and (Noddy<>nil) do begin if Noddy.Text=Text then begin Searching:=false; TreeView.Selected:=Noddy; TreeView.SetFocus; end else Noddy:=Noddy.GetNext; end; end; |
Вспомогательно – удаление одного узла
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
procedure TDMDB.DeleteNode(Sender: TObject; TreeView: TTreeView); var Node:TTreeNode; begin if not Assigned(TreeView.Selected) then exit; //Удаляем всех детей while TreeView.Selected.HasChildren do begin Node:=TreeView.Selected.GetLastChild; while Node.HasChildren do Node:=Node.GetLastChild; DeleteRecord(Node); //Удаляем из базы TreeView.Items.Delete(Node); // Удаляем из дерева end; //Удаляем родителя DeleteRecord(TreeView.Selected); TreeView.Selected.Delete; end; |
Вспомогательно – удаление записи из базы данных
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
procedure TDMDB.DeleteNode(Sender: TObject; TreeView: TTreeView); var Node:TTreeNode; begin if not Assigned(TreeView.Selected) then exit; //Удаляем всех детей while TreeView.Selected.HasChildren do begin Node:=TreeView.Selected.GetLastChild; while Node.HasChildren do Node:=Node.GetLastChild; DeleteRecord(Node); //Удаляем из базы TreeView.Items.Delete(Node); // Удаляем из дерева end; //Удаляем родителя DeleteRecord(TreeView.Selected); TreeView.Selected.Delete; end; |