Помощь - Поиск - Пользователи - Календарь
Полная версия: иерархическая структура объектов
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Делфи
18192123
Здравствуйте!
Есть тема "Иерархия структуры таблицы Менделеева", в пределах которой нужно построить иерархию объектов, состоящей как минимум из 3-х уровней.
Для демонстрации работы построенной иерархии классов, создаётся приложение, позволяющее:
создавать, удалять объекты, изменять характеристики объектов, визуализировать объекты, манипулировать на форме объектами, сохранять/считывать текущее состояние объектов в формате XML.

Есть некоторые задумки насчёт иерархии (изображение)...Может кто-нибудь подскажет другой подход?
Ну а прежде всего - трудности с визуализацией и манипулированием объектами...Может быть у кого-нибудь есть идеи, как для начала идейно подойти к этим вопросам? что можно придумать? Подскажите пожалуйста..!
volvo
Цитата
прежде всего - трудности с визуализацией и манипулированием объектами
Какие именно трудности возникли? Какими компонентами ты пользуешься для визуализации? То, что ты привела - это же TTreeView в чистом виде. Пробовала его?

Вот так выглядит:
Нажмите для просмотра прикрепленного файла

В Дельфи7 точно уже был этот компонент.

Для манипулирования узлами дерева - Drag and Drop, в Drkb есть примеры реализации
18192123
Пока описала свой класс TBPeriod:

unit UnitClasses;

interface
type TBPeriod=class
private
fBTitle:string;
function GetBTitle:string;
procedure SetBTitle(newBTitle:string);

public
property BTitle:string read GetBTitle write SetBTitle;
constructor Create(fBTitle:string);
end;

implementation
Uses UnitMain;

constructor TBPeriod.Create(fBTitle:string);
begin
inherited Create;
BTitle:=fBTitle;
end;

function TBPeriod.GetBTitle:string;
begin
Result:=fBTitle;
end;

procedure TBPeriod.SetBTitle(newBTitle:string);
begin
fBTitle:=newBTitle;
end;

end.



На форме имеется кнопка, по которой предполагается создать объект класса TBPeriod (при установке переключателя в положение "Большой"), название его считывается из Edit, и я хочу добавить создаваемый объект в TreeViewNew, с чем и возникла проблема..

unit UnitMain;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, XPMan, StdCtrls, ComCtrls, ExtCtrls, Buttons;

type
TFormMain = class(TForm)
XPManifest1: TXPManifest;
TreeViewMain: TTreeView;
LabMainStruct: TLabel;
TreeViewNew: TTreeView;
BitBtnCreateBPer: TBitBtn;
RadioGroupPeriod: TRadioGroup;
EditTitlePer: TEdit;
procedure BitBtnCreateBPerClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
FormMain: TFormMain;

implementation
Uses UnitClasses;
{$R *.dfm}

procedure TFormMain.BitBtnCreateBPerClick(Sender: TObject);
var
NewBPeriod:TBPeriod;
title:string;
begin
case RadioGroupPeriod.ItemIndex of
0: begin
title:=EditTitlePer.Text;
NewBPeriod:=TBPeriod.Create(title);
//Вот здесь меня не пропускает с несоответствием типов... Подскажите пожалуйста, как тут быть?
FormMain.TreeViewNew.Items.AddObject(title, NewBPeriod as TObject);
end;
1: ;
else
MessageDlg('Выберите пункт создания',mtError,[mbOK],0);
end;
end;

end.


volvo
Цитата
//Вот здесь меня не пропускает с несоответствием типов...
Правильно... AddObject принимает три параметра. Первый - Sibling, куда вставлять новый узел, ты про него забыла... Чтобы добавить еще один корневой узел - сделай:
FormMain.TreeViewNew.Items.AddObject(nil, title, NewBPeriod as TObject);
, ну или передавай вместо nil тот элемент, после которого будет вставлен новый. Проще всего выбрать нужный тебе узел, и сделать:
FormMain.TreeViewNew.Items.AddObject(FormMain.TreeViewNew.Selected, title, NewBPeriod as TObject);
, тогда новый узел добавится как сосед выбранного.
18192123
Вопрос насчёт создания класса-потомка для моего класса TBPeriod..

type TRange=class(TBPeriod)
// ....
end;




Если предполагается, что у этого класса-потомка будет поле, конструктор, так же как и у родителя, то нужно ли эти поле и конструктор писать снова для класса-потомка или можно использовать то, что есть у родителя? Объясните пожалуйста, как это будет выглядеть при описании классов?
volvo
Цитата
Если предполагается, что у этого класса-потомка будет поле, конструктор, так же как и у родителя, то нужно ли эти поле и конструктор писать снова для класса-потомка или можно использовать то, что есть у родителя?
Для инициализации тех полей, которые есть у предка, используй конструктор предка (дя этого тот конструктор, собственно, и существует - чтобы правильно выделить и заполнить поля объекта, которые ему известны). Свой конструктор используется для инициализации новых полей:

type
TPeriod = class
title: string;

constructor create(const s: string);
end;

TRange = class(TBase)
min, max: integer; // только для иллюстрации, я не знаю, что ты хочешь хранить в этом классе
constructor create(const s: string; amin, amax: integer);
end;

constructor TPeriod.create(const s: string);
begin
inherited create;
title := s;
end;

constructor TRange.create(const s: string; amin, amax: integer);
begin
inherited create(s); // <--- Поля предка инициализированы ЕГО конструктором
min := amin, max := amax; // А теперь инициализируем новые поля
end;
18192123
Цитата(volvo @ 5.12.2008 16:02) *

FormMain.TreeViewNew.Items.AddObject(FormMain.TreeViewNew.Selected, title, NewBPeriod as TObject);
, тогда новый узел добавится как сосед выбранного.


Скажите пожалуйста, а как модифицировать такую запись, если необходимо, чтобы новый узел был не соседним, а дочерним?
volvo
Цитата
чтобы новый узел был не соседним, а дочерним
надо вместо AddObject использовать AddChildObject smile.gif
18192123
Цитата(volvo @ 5.12.2008 21:38) *

надо вместо AddObject использовать AddChildObject smile.gif

Аа) спасибо!)
18192123
Ещё один вопрос по классам...
Есть класс TPeriod и его наследник TRange..

unit UnitClasses;

interface
type TPeriod=class
Title:string;
Feature:string;
constructor Create(s:string;aFeature:string);
end;

type TRange=class(TPeriod)
constructor Create(s:string);
end;


Тогда при записи



implementation
Uses UnitMain;

constructor TPeriod.Create(s:string;aFeature:string);
begin
inherited Create;
Title:=s;
Feature:=aFeature;
end;

constructor TRange.Create(s:string);
begin
inherited Create(s);
end;

end.

во 2-м конструкторе компилятор сигнализирует об ошибке..Природа этой ошибки мне ясна (не все параметры указаны)...Но как обойти эту ошибку? Ведь у меня в потомке только поле title без Feature...
volvo
Цитата
Но как обойти эту ошибку? Ведь у меня в потомке только поле title без Feature...
Неправда... У тебя в потомке И title, И feature, поскольку оба этих поля описаны в предке... Понимаешь в чем дело? Если тебе хочется сделать так, чтобы потомок содержал меньше полей, чем предок - то налицо ошибка проектирования. У каждого потомка количество полей должно быть как минимум не меньше, чем у предка.
18192123
Цитата(volvo @ 6.12.2008 18:59) *

Неправда... У тебя в потомке И title, И feature, поскольку оба этих поля описаны в предке... Понимаешь в чем дело? Если тебе хочется сделать так, чтобы потомок содержал меньше полей, чем предок - то налицо ошибка проектирования. У каждого потомка количество полей должно быть как минимум не меньше, чем у предка.

Большое спасибо за разъяснение!
18192123
Например, я добавила в TreeView объект с некоторыми характеристиками (в TreeView отображается из них заголовок)..Выделила его..Скажите пожалуйста, что нужно сделать, чтобы теперь просмотреть этот объект?
volvo
Совсем забыл уточнить, что для работы с TTreeView твои классы должны быть унаследованы от TObject... То есть, вот так:

type
TBPeriod = class(TObject) // внимательно, наследник TObject !!!
private
fBTitle:string;
function GetBTitle:string;
procedure SetBTitle(newBTitle:string);

public
sData: string; // Просто для иллюстрации - эта строка будет хранимой информацией
property BTitle:string read GetBTitle write SetBTitle;
constructor Create(fBTitle, data:string);
end;

constructor TBPeriod.Create(fBTitle, data:string);
begin
inherited Create;
BTitle := fBTitle;
sData := data;
end;

function TBPeriod.GetBTitle:string;
begin
Result := fBTitle;
end;

procedure TBPeriod.SetBTitle(newBTitle:string);
begin
fBTitle := newBTitle;
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
var
NewBPeriod: TBPeriod;
title: string;
begin
case RadioGroupPeriod.ItemIndex of
0:
begin
title := EditTitlePer.Text;
NewBPeriod := TBPeriod.Create(title, Edit1.Text);
TreeView1.Items.AddChildObject(
// так заносим данные, это ты уже видела
TreeView1.Selected, title, NewBPeriod as TObject
);
end;

1: ;

else
MessageDlg('Выберите пункт создания',mtError,[mbOK],0);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
// А вот так - получаем доступ к данным
ShowMessage(TBPeriod(TreeView1.Selected.Data).sData);
end;
Гость
У меня возник вопрос по Drag&Drop..
Я хочу реализовать такое действие: при перетаскивании элемента из TreeView на Image соответствующий узел (и связанный с ним объект) удаляется..
Попыталась таким образом:

procedure TFormMain.ImageDelDragDrop(Sender, Source: TObject; X,
Y: Integer);
begin
if Source = TreeViewNew then
// Удаляем из источника
TTreeView(Source).Items.Delete(TTreeView(Source).Selected);
end;

procedure TFormMain.ImageDelDragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
begin
Accept := (Source = TreeViewNew);
end;


Желаемые результаты не достигаются..
Объясните пожалуйста, как нужно сделать?
18192123
Цитата(Гость @ 8.12.2008 2:10) *

У меня возник вопрос по Drag&Drop..
Я хочу реализовать такое действие: при перетаскивании элемента из TreeView на Image соответствующий узел (и связанный с ним объект) удаляется..
Попыталась таким образом:

procedure TFormMain.ImageDelDragDrop(Sender, Source: TObject; X,
Y: Integer);
begin
if Source = TreeViewNew then
// Удаляем из источника
TTreeView(Source).Items.Delete(TTreeView(Source).Selected);
end;

procedure TFormMain.ImageDelDragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
begin
Accept := (Source = TreeViewNew);
end;


Желаемые результаты не достигаются..
Объясните пожалуйста, как нужно сделать?


Этот пост - мое авторство..Случайно получилось под "Гость" smile.gif
volvo
Цитата
Желаемые результаты не достигаются..
Для того, чтобы это работало, и для TreeView и для Image свойство DragMode должно быть установлено в dmAutomatic
Гость
Пусть я определила функцию, процедуру и свойство для класса TBPeriod:

type
TBPeriod = class(TObject) // внимательно, наследник TObject !!!
private
fBTitle:string;

public
sData: string; // Просто для иллюстрации - эта строка будет хранимой информацией
function GetsData:string;
procedure SetsData(newsData:string);
property Data:string read GetsData write SetsData;
constructor Create(fBTitle, data:string);
end;

///////////
function TBPeriod.GetsData:string;
begin
Result:=sData;
end;

procedure TBPeriod.SetsData(newsData:string);
begin
sData:=newsData;
end;



В TreeView соответсвенно находится запись и связанный с ней объект..Выделяя эту запись, можем просмотреть значение поля sData. И предположим, пользователь захочет изменить это значение..А как это осуществить с помощью функции, процедуры и свойства моего класса? Не очень понятен механизм..объясните пожалуйста!
18192123
Ой..повторилась история с постом от "Гость"..
volvo
Цитата
предположим, пользователь захочет изменить это значение
Кто ж мешает?
TBPeriod(TreeViewNew.Selected.Data).Data := EditNewValue.Text; // изменяешь свойство Data нужного объекта
18192123
Снова вопрос по 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;


Узлы перемещаются..Но проблема в том, чтобы переместить ещё и связанный с узлом объект...
Объясните пожалуйста, как это нужно делать?
volvo
   nodeTmp := Items.AddChild(TargetNode, SourceNode.Text);

попробуй заменить на
   nodeTmp := Items.AddChildObject(TargetNode, SourceNode.Text, SourceNode.Data);

Должно сработать...
18192123
Цитата(volvo @ 8.12.2008 22:33) *

   nodeTmp := Items.AddChild(TargetNode, SourceNode.Text);

попробуй заменить на
   nodeTmp := Items.AddChildObject(TargetNode, SourceNode.Text, SourceNode.Data);

Должно сработать...

Да, получилось!! Спасибо!
И теперь момент, который мне не ясен..И это реализация сохранения (того, что получили на TreeViewNew)/считывания (занесение данных в TreeViewNew) в формате XML...Как это реализуется? Какие-нибудь специальные компоненты? Объясните пожалуйста, как это должно выглядеть??
volvo
Цитата
как это должно выглядеть??
Вот тут есть описание (правда по-английски): Exporting a TreeView to XML. Populating a TreeView from XML

Правда приведенные там процедуры чтения/записи XML потребуют небольшой доработки, поскольку кроме самих названий узлов у тебя должны сохраняться еще и связанные с ними данные (это придется добавить несколько атрибутов в ProcessTreeItem), и потом они должны восстанавливаться из файла (это в ProcessNode, чтение этих атрибутов, создание экземпляра класса TBPeriod и использование AddChildObject вместо AddChild)...
Гость
А скажите пожалуйста, где будет лежать создаваемый таким образом файлик *.XML?
18192123
Цитата(Гость @ 9.12.2008 3:13) *

А скажите пожалуйста, где будет лежать создаваемый таким образом файлик *.XML?

volvo
Судя по строке
XMLDoc.SaveToFile(ChangeFileExt(ParamStr(0),'.XML'));
, там же, где и EXE-шник.
18192123
Цитата(volvo @ 9.12.2008 0:23) *

Вот тут есть описание (правда по-английски): Exporting a TreeView to XML. Populating a TreeView from XML

Правда приведенные там процедуры чтения/записи XML потребуют небольшой доработки, поскольку кроме самих названий узлов у тебя должны сохраняться еще и связанные с ними данные (это придется добавить несколько атрибутов в ProcessTreeItem)...

А что это за атрибуты?
volvo
Твои атрибуты... Смотри, сохранять дерево (вместе с тем, что хранится в объектах, связанных с узлами), можно так:
procedure Tree2XML(tree: TTreeView);
var
tn : TTreeNode;
XMLDoc : TXMLDocument;
iNode : IXMLNode;

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*)

begin
XMLDoc := TXMLDocument.Create(nil);
XMLDoc.Active := True;
iNode := XMLDoc.AddChild('tree2xml');
iNode.Attributes['app'] := ParamStr(0);

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']);

tn.ImageIndex := Integer(Attributes['imageIndex']);
tn.StateIndex := Integer(Attributes['stateIndex']);
end;

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 - в дополнии к ним ещё и новые..как учесть это??)
Объясните пожалуйста!
18192123
Цитата(Гость @ 12.12.2008 19:29) *

Мне вот ещё что не понятно...
Как быть при записи/чтении в/из *.XML в случае, если TBPeriod имеет наследника TRange, который в свою очередь - TGroup, для TGroup есть наследник TUnderGroup, и для последнего - наследник TElement (причём объекты верхних ступенек иерархии имееют одни и те же поля, а TElement - в дополнии к ним ещё и новые..как учесть это??)
Объясните пожалуйста!


volvo
Цитата
как учесть это??
Сохранять в XML вместе с данными еще и некоторый идентификатор, определяющий, объект какого именно типа надо будет создавать при восстановлении дерева... Скажем, при id="1", создаем TRange, если id="2", то TElement, и так далее...

Я бы сделал виртуальную функцию GetID: string, которая будет возвращать идентификатор для каждого типа объекта, тогда при записи в этот самый атрибут ID достаточно будет вызвать:
cNode.Attributes['ID'] := TBPeriod(tn.Data).GetID;
18192123
Цитата(volvo @ 12.12.2008 19:50) *

Сохранять в 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 наследников должен быть аналогичный код?
volvo
Цитата
А для записи в XML наследников должен быть аналогичный код?
Ты ж сказала, что у тебя почти у всех классов одинаковые поля, значит изменения (т.е. добавление еще нескольких строк кода) будет только при сохранении TElement...
18192123
Цитата(volvo @ 12.12.2008 21:17) *

Ты ж сказала, что у тебя почти у всех классов одинаковые поля, значит изменения (т.е. добавление еще нескольких строк кода) будет только при сохранении TElement...

Да, так и будет..
Но я так и не поняла, где указывать эти несколько строк кода? В фрагменте из предыдущего поста?
volvo
Ну, например, так:

    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'] := '';

18192123
Вот такой вопрос возник..
Если я хочу создать очередной узел (по нажатию соответствующей кнопки), то сначала в TreeView мне нужно выделить узел-родитель..Хочу сделать контроль возможности неверного выделения..
Скажите пожалуйста, как в этом случае сообщить о нарушении иерархии, если вместо предполагаемого родительского узла выделен узел того же уровня, что и новый или выделен узел, стоящий выше предполагаемого родительского?
volvo
Цитата
как в этом случае сообщить о нарушении иерархии, если вместо предполагаемого родительского узла выделен узел того же уровня, что и новый
Где и как задается уровень НОВОГО узла? В предыдущей версии твоей программы новый узел добавлялся как дочерний к любому выделенному... Что теперь изменилось? Есть какие-то ограничения?
18192123
Цитата(volvo @ 16.12.2008 21:54) *

Есть какие-то ограничения?

ТОлько по смыслу)
volvo
Ну, если дерево будет именно таким, как было сказано в самом первом посте, то с этим проблем нет, можно в конце концов выделять римские числа в заголовках узлов и проверять, подходит ли потомок к выбранному предку... А вот если я в группу "Неметаллы" захочу внести Селен и Теллур - ты сама, не заглядывая в таблицу вряд ли сможешь определить (по смыслу) ошибся ли я, и какой именно элемент не подходит для данной группы. Так что только по смыслу здесь не пойдет. Нужна какая-то доп. информация.
18192123
Цитата(volvo @ 16.12.2008 22:18) *

Ну, если дерево будет именно таким, как было сказано в самом первом посте, то с этим проблем нет, можно в конце концов выделять римские числа в заголовках узлов и проверять, подходит ли потомок к выбранному предку... А вот если я в группу "Неметаллы" захочу внести Селен и Теллур - ты сама, не заглядывая в таблицу вряд ли сможешь определить (по смыслу) ошибся ли я, и какой именно элемент не подходит для данной группы. Так что только по смыслу здесь не пойдет. Нужна какая-то доп. информация.

Немного другое имела ввиду:
есть кнопки
добавить период
добавить ряд
добавить группу
добавить подгруппу
добавить элемент

и например моё TreeView
Код

I-й период
   1-й ряд
      I-я группа
         Ia
      VIII-я группа

И я хочу добавить в I-я группу ещё один узел Iб, т.е для этого я должна выделить этот узел [I-я группа] и нажать кнопку
добавить подгруппу. И если выделить узел Ia или 1-й ряд, то при нажатии на кнопку мне приложение должно запретить действия и "сказать", что "подгруппа - это наследник Группа"..
Вот чего хочу добиться, только не пойму, как..
volvo
А, вот ты о чем... Ну, это просто: у каждого узла дерева есть поле Level (уровень, на котором этот узел находится), тебе надо будет всего навсего проверить его...

Скажем, при нажатии на кнопку в твоем примере проверка должна быть:
if TreeView1.Selected.Level = 2 then begin // Level начинается с 0
// Здесь добавление узла, как обычно
end
else ShowMessage('Подгруппа - это наследник Группа');
18192123
Спасибо!!)
Получилось, как и хотела!!)
И ещё один вопрос..в таком же духе..
Вот таким образом было сделано перемещение узлов в пределах TreeViewNew:


procedure TFormMain.MoveNode(TargetNode, SourceNode: TTreeNode);
var
nodeTmp: TTreeNode;
i: Integer;
begin
with TreeViewNew do
begin
nodeTmp := Items.AddChildObject(TargetNode, SourceNode.Text, SourceNode.Data);
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;



Можно ли как-нибудь учесть, чтоб перемещение осуществлялось в таком духе:
1-й ряд переместить только во 2-й период (ну и наоборот), исключая возможность чтоб ряд попал в группу??
ну по такой же схеме с остальным..Объясните пожалуйста!
Код

I период
   1 ряд
      I группа
      VIII группа
II период
   2 ряд
      I группа
      II группа
volvo
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) or (TargetNode.Level + 1 <> SourceNode.Level) then // <---
begin
EndDrag(False);
Exit;
end;
MoveNode(TargetNode, SourceNode);
SourceNode.Free;
end;
end;
Это имела в виду?
18192123
Цитата(volvo @ 17.12.2008 1:42) *

Это имела в виду?

Да, большое спасибо!!
Теперь понятно!!
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.