Снова вопрос по Drag&Drop..теперь в пределах TreeView..т.е. перемещение узлов. Нашла в DRKB пример по этому вопросу...
procedure TFormMain.MoveNode(TargetNode, SourceNode: TTreeNode); var nodeTmp: TTreeNode; i: Integer; begin with TreeViewNew do begin nodeTmp := Items.AddChild(TargetNode, SourceNode.Text); for i := 0 to SourceNode.Count - 1 do begin MoveNode(nodeTmp, SourceNode.Item[i]); end; end; end;
procedure TFormMain.TreeViewNewDragDrop(Sender, Source: TObject; X, Y: Integer); var TargetNode, SourceNode: TTreeNode; begin with TreeViewNew do begin TargetNode := GetNodeAt(X, Y); // Get target node SourceNode := Selected; if (TargetNode = nil) then begin EndDrag(False); Exit; end; MoveNode(TargetNode, SourceNode); SourceNode.Free; end; end;
procedure TFormMain.TreeViewNewDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin if (Sender = TreeViewNew) then // If TRUE than accept the draged item begin Accept := True; end; end;
Узлы перемещаются..Но проблема в том, чтобы переместить ещё и связанный с узлом объект... Объясните пожалуйста, как это нужно делать?
Да, получилось!! Спасибо! И теперь момент, который мне не ясен..И это реализация сохранения (того, что получили на TreeViewNew)/считывания (занесение данных в TreeViewNew) в формате XML...Как это реализуется? Какие-нибудь специальные компоненты? Объясните пожалуйста, как это должно выглядеть??
Правда приведенные там процедуры чтения/записи XML потребуют небольшой доработки, поскольку кроме самих названий узлов у тебя должны сохраняться еще и связанные с ними данные (это придется добавить несколько атрибутов в ProcessTreeItem), и потом они должны восстанавливаться из файла (это в ProcessNode, чтение этих атрибутов, создание экземпляра класса TBPeriod и использование AddChildObject вместо AddChild)...
Правда приведенные там процедуры чтения/записи XML потребуют небольшой доработки, поскольку кроме самих названий узлов у тебя должны сохраняться еще и связанные с ними данные (это придется добавить несколько атрибутов в ProcessTreeItem)...
procedure ProcessTreeItem(tn: TTreeNode; iNode: IXMLNode); var cNode : IXMLNode; begin if tn = nil then Exit;
cNode := iNode.AddChild('item'); cNode.Attributes['text'] := tn.Text; cNode.Attributes['imageIndex'] := tn.ImageIndex; cNode.Attributes['stateIndex'] := tn.StateIndex; // Вот это и есть твой атрибут ... if tn.Data <> nil then begin cNode.Attributes['sData'] := TBPeriod(tn.Data).sData; end else cNode.Attributes['sData'] := '';
tn := tn.getFirstChild; while tn <> nil do begin ProcessTreeItem(tn, cNode); tn := tn.getNextSibling; end; end; (*ProcessTreeItem*)
tn := tree.TopItem; while tn <> nil do begin ProcessTreeItem (tn, iNode); tn := tn.getNextSibling; end; XMLDoc.SaveToFile(ChangeFileExt(ParamStr(0),'.XML')); XMLDoc := nil; end; (* Tree2XML *)
procedure TForm1.btnSaveXMLClick(Sender: TObject); begin Tree2XML(TreeView1); end;
Чтобы восстановить из XML сохраненное таким образом дерево:
procedure XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument); var iNode : IXMLNode;
procedure ProcessNode(Node : IXMLNode; tn: TTreeNode); var cNode : IXMLNode; newPeriod: TBPeriod; begin if Node = nil then Exit;
with Node do begin // Есть сохраненные атрибуты? Восстанавливаем ... if Attributes['sData'] <> '' then begin newPeriod := TBPeriod.Create( Attributes['text'], Attributes['sData'] ); tn := tree.Items.AddChildObject( tn, Attributes['text'], newPeriod as TObject ); end else // Нету? И не надо ... tn := tree.Items.AddChild(tn, Attributes['text']);
cNode := Node.ChildNodes.First; while cNode <> nil do begin ProcessNode(cNode, tn); cNode := cNode.NextSibling; end; end; (*ProcessNode*) begin tree.Items.Clear; XMLDoc.FileName := ChangeFileExt(ParamStr(0),'.XML'); XMLDoc.Active := True;
iNode := XMLDoc.DocumentElement.ChildNodes.First;
while iNode <> nil do begin ProcessNode(iNode,nil); iNode := iNode.NextSibling; end;
XMLDoc.Active := False; end;
procedure TForm1.btnLoadTreeClick(Sender: TObject); var ParentObj: TComponent; XMLDoc: TXMLDocument; begin ParentObj := TComponent.Create(nil); XMLDoc := TXMLDocument.Create(ParentObj); XML2Tree(TreeView2, XMLDoc); end;
(я для проверки сохранял из одного дерева, восстанавливал в другое... Из доп. информации сохраняется только строка Data, если у тебя есть еще что-то добавляй и в ProcessTreeItem, и в ProcessNode)...
Мне вот ещё что не понятно... Как быть при записи/чтении в/из *.XML в случае, если TBPeriod имеет наследника TRange, который в свою очередь - TGroup, для TGroup есть наследник TUnderGroup, и для последнего - наследник TElement (причём объекты верхних ступенек иерархии имееют одни и те же поля, а TElement - в дополнии к ним ещё и новые..как учесть это??) Объясните пожалуйста!
Мне вот ещё что не понятно... Как быть при записи/чтении в/из *.XML в случае, если TBPeriod имеет наследника TRange, который в свою очередь - TGroup, для TGroup есть наследник TUnderGroup, и для последнего - наследник TElement (причём объекты верхних ступенек иерархии имееют одни и те же поля, а TElement - в дополнии к ним ещё и новые..как учесть это??) Объясните пожалуйста!
Сохранять в XML вместе с данными еще и некоторый идентификатор, определяющий, объект какого именно типа надо будет создавать при восстановлении дерева... Скажем, при id="1", создаем TRange, если id="2", то TElement, и так далее...
Я бы сделал виртуальную функцию GetID: string, которая будет возвращать идентификатор для каждого типа объекта, тогда при записи в этот самый атрибут ID достаточно будет вызвать:
Сохранять в XML вместе с данными еще и некоторый идентификатор...
//сохраняем... if tn.Data <> nil then begin cNode.Attributes['Feature'] := TPeriod(tn.Data).Feature; cNode.Attributes['Count'] := TPeriod(tn.Data).Count; cNode.Attributes['Id']:=1; end else cNode.Attributes['sData'] := '';
Например, так? А для записи в XML наследников должен быть аналогичный код?
А для записи в XML наследников должен быть аналогичный код?
Ты ж сказала, что у тебя почти у всех классов одинаковые поля, значит изменения (т.е. добавление еще нескольких строк кода) будет только при сохранении TElement...
Ты ж сказала, что у тебя почти у всех классов одинаковые поля, значит изменения (т.е. добавление еще нескольких строк кода) будет только при сохранении TElement...
Да, так и будет.. Но я так и не поняла, где указывать эти несколько строк кода? В фрагменте из предыдущего поста?
if tn.Data <> nil then begin // значит, есть связанный с узлом объект... смотрим, какого он типа: if TBPeriod(tn.Data) is TElement then begin // здесь пишутся данные из класса TElement или его потомков end else begin // здесь - изо всех остальных классов (до TElement в цепочке наследования) end; end else cNode.Attributes['sData'] := '';
Вот такой вопрос возник.. Если я хочу создать очередной узел (по нажатию соответствующей кнопки), то сначала в TreeView мне нужно выделить узел-родитель..Хочу сделать контроль возможности неверного выделения.. Скажите пожалуйста, как в этом случае сообщить о нарушении иерархии, если вместо предполагаемого родительского узла выделен узел того же уровня, что и новый или выделен узел, стоящий выше предполагаемого родительского?
как в этом случае сообщить о нарушении иерархии, если вместо предполагаемого родительского узла выделен узел того же уровня, что и новый
Где и как задается уровень НОВОГО узла? В предыдущей версии твоей программы новый узел добавлялся как дочерний к любому выделенному... Что теперь изменилось? Есть какие-то ограничения?
Ну, если дерево будет именно таким, как было сказано в самом первом посте, то с этим проблем нет, можно в конце концов выделять римские числа в заголовках узлов и проверять, подходит ли потомок к выбранному предку... А вот если я в группу "Неметаллы" захочу внести Селен и Теллур - ты сама, не заглядывая в таблицу вряд ли сможешь определить (по смыслу) ошибся ли я, и какой именно элемент не подходит для данной группы. Так что только по смыслу здесь не пойдет. Нужна какая-то доп. информация.