Шаг 13.
Вкладка Win32. Компонент TTreeView

    На этом шаге мы рассмотрим компонент TTreeView.

    Компонент TTreeView (Дерево) используется для отображения сложных структур данных. Например таких, как структура каталогов в проводнике: слева иерархическая структура диска в виде раскрывающихся значков с символами "-" (развернут) и "+" (свернут), а справа - содержимое выбранного каталога в виде списка. Ниже перечислены свойства, управляющие формированием и отображением узлов дерева при проектировании.

    Свойства компонента TTreeView.

Таблица 1. Свойства компонента TTreeView
Свойство Описание
type TBorderStyle = (bsNone, bsSingle, bsDouble, bsRaisedPanel, bsSunkenPanel, bsRaised3d, bsSunken3d, bsEtched, bsEmbossed);
property BorderStyle: TBorderStyle;
Определяет стиль рамки, охватывающей компонент (поэкспериментируйте)
property DropTarget: TTreeNode; Определяет узел, который может служить приемником для операций перетаскивания (Drag&Drop)
property HideSelection: Boolean; При значении True с элемента, теряющего фокус, снимается выделение
property Images: TImageList; Содержит набор изображений, которые будут использоваться при прорисовке узлов
property Indent: Integer; Определяет число пикселей для визуального разделения по горизонтали узлов дерева
property Items: TTreeNodes; Открывает доступ к любому узлу по его индексу. Индексация начинается с 0 и соответствует просмотру всех узлов полностью развернутого дерева
property ReadOnly: Boolean; Запрещает/разрешает редактирование надписей в узлах
property ShowLines: Boolean; Определяет наличие соединительных линий в иерархической последовательности (только VCL)
property ShowButtons: Boolean; Определяет наличие символов "+" или "-"
property ShowRoot: Boolean; Определяет отображение элементов верхнего уровня (только VCL)
property SortType: TSortType; Определяет вид сортировки:
  • stData - сортировка при изменении объекта Data;
  • stText - сортировка при изменении свойства Техt;
  • stBoth - сортировка при изменении и Data, и Text;
  • stNone - сортировка не выполняется
property RightClickSelect: Boolean; При значении True разрешается выделять узлы дерева с помощью правой кнопки мыши
property ChangeDelay: Integer; Пауза в миллисекундах между выделением узла дерева и генерацией сообщения OnChange. Обработка этого сообщения позволяет, например, отобразить содержимое данного узла в другой части формы
property AutoExpand: Boolean; При значении True выбранные узлы дерева будут автоматически разворачиваться, при этом ранее развернутые узлы будут свернуты

    Для создания подобных деревьев, отображающих иерархические структуры данных, в системе Delphi реализован компонент TTreeView. Начальная структура формируется на этапе проектирования в редакторе TreeView Items (Элементы дерева). Для каждого узла дерева можно указать изображение . Его номер указывается в поле редактора Imagelndex (Номер картинки), а сам список картинок задается в свойстве Images (для этого в форме должен находиться компонент TImageList). Дополнительно для каждого узла можно указать номер картинки, отражающей его выделенное состояние (свойство SelectedIndex), и номер картинки, дополнительно размещаемой слева от основной картинки (свойство Slatelndex, для которого определяется свой список изображений в свойстве StateImages). Значение -1, присвоенное этим свойствам по умолчанию, подавляет вывод изображений.

    Доступ к узлам по номеру и особенно формирование новых элементов дерева во время работы программы требует значительных вычислительных ресурсов. Поэтому желательно выполнять основную часть работы по формированию структуры дерева на этапе проектирования.

    Имена узлов можно редактировать как обычные названия объектов Windows. Узлы хранятся в свойстве Items, которое представляет класс TTreeNodes, содержащий свойство Item - массив объектов типа TTreeNode. Основные свойства класса TTreeNode приведены ниже.

Таблица 2. Свойства класса TTreeNode
Свойство Описание
property AbsoluteIndex: Integer; Абсолютный номер узла в дереве. Самый первый узел имеет номер 0, далее нумеруются все потомки этого узла. При этом, если у потомка есть подчиненные узлы, то нумерация продолжается с первого потомка
property Count: Integer; Число потомков узла
property Cut: Boolean; При значении True изображение для соответствующего узла отображается блекло, как при операциях Cut, Paste
property Date: Pointer; Свойство имеет тип Pointer, и указывает на связанный с узлом объект
property Deleting: Boolean; При значении True данный узел находится в состоянии удаления. Этот процесс может быть длительным, если удаляется узел с большим числом потомков
property DropTarget: Boolean; Содержит значение True, если узел может служить приемником операции перетаскивания (Drag&Drop)
property Expanded: Boolean; При значении True узел развернут, т.е. кнопка находится в состоянии "-"
property Focused: Boolean; При значении True узел получил фокус ввода
property HasChildren: Boolean; При значении True узел имеет потомков
property ImageIndex: TImageIndex; Номер изображения в компоненте TImageList
property Index: Longint; Номер узла в списке потомков вышестоящего родителя. Первый узел-потомок имеет номер 0, второй - 1 и т.д.
property IsVisible: Boolean; При значении True узел виден
property Item [Index: Integer]: TTreeNode; Массив узлов, являющихся потомками данного
property ItemId: HTreeItem; Содержит уникальный Windows-дескриптор узла
property Level: Integer; Глубина узла. Верхний уровень имеет номер 0, следующий уровень - 1 и т.д.
property OverlayIndex: Integer; Содержит индекс оверлейного значка. Оверлейный значок вычеркивается поверх основного, чтобы, например, указать, что узел стал недоступен
property Parent: TTreeNode; Содержит ссылку на родительский узел
property Selected: Boolean; При значении True узел выделен
property SelectedIndex: Integer; Номер изображения, показываемого при выделении узла
property Text: String; Текст, выводимый в узле
property TreeView: TCustomTreeView; Ссылка на родительский объект
property Owner: TTreeNodes; Содержит ссылку на владельца данного узла

    Напишем небольшое приложение, позволяющее создавать дерево во время выполнения программы. Для этого в форме разместим пустой объект TreeView, надпись и две кнопки с заголовками Узел и Потомок. После ввода имени и щелчка на кнопке Узел в дерево добавляется новый узел на текущем уровне. При щелчке на кнопке Потомок новый узел добавляется в число потомков текущего узла. Для добавления нового узла можно использовать метод AddNode, объявление которого выглядит следующим образом.

function AddNode (Node, Relative: TTreeNode; const S: string; 
      Ptr: Pointer; Method: TNodeAttachMode): TTreeNode;

    Метод добавляет новый узел в коллекцию. При этом возвращается вновь добавленный узел и используются следующие параметры.

    Память, распределяемая для параметра Ptr, не освобождается при освобождении объектов, связанных с узлами.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    TreeView1: TTreeView;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure TreeView1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
var
  Nodes : TTreeNodes;
  Node : TTreeNode;
procedure TForm1.FormCreate(Sender: TObject);
begin
 Nodes := TTreeNodes.Create (TreeView1);
 Node := TTreeNode.Create (Nodes);
 Node := nil;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  NewNode : TTreeNode;
  i : Integer;
begin
  NewNode := TTreeNode.Create(Nodes);
  i := Nodes.Count;
  NewNode := Nodes.AddNode (NewNode, Node, 'Узел '+
  IntToStr(i), nil, naAdd);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  NewNode : TTreeNode;
begin
  NewNode := TTreeNode.Create(Nodes);
  NewNode := Nodes.AddNode (NewNode, Node, 'Потомок '+
  IntToStr (TreeView1.Items.Count), nil, naAddChild);
end;
procedure TForm1.TreeView1Click(Sender: TObject);
begin
  Node := TreeView1.Selected;
  Label1.Caption := Node.Text;
end;
end.
Текст этого примера можно взять здесь.

    Щелчки на кнопках будут добавлять новые узлы и их потомков, в зависимости от выбранного узла, который выбирается щелчком мыши на дереве, как показано на рис.1.


Рис.1. Создание дерева

    Когда дерево создано во время работы программы, его структуру можно сохранить на жестком диске до следующего сеанса. Это можно сделать с помощью следующего метода.

   TreeView1.SaveToFile ('TREE.TXT');

    Дерево сохраняется в текстовом формате в наглядном виде - с отступами. Загрузить дерево из файла можно с помощью следующего метода:

  procedure  LoadFromFile (const  FileName: String);

    Узлы в дереве можно сортировать. Момент сортировки задается в свойстве SortType: stNone, stData, stText, stBoth, т.е. при изменении или свойства Data или свойства Text, или в обоих случаях.

    Способ сортировки определяется обработчиком события OnCompare. Например, чтобы отсортировать все элементы дерева в убывающем порядке их названий, установите значение свойства SortType равным stText и напишите следующий текст обработчика.

procedure TForm1.TreeView1Compare (Sender:   TObject;
     Node1, Node2: TTreeNode; Data: Integer; var Compare: Integer); 
begin
     if Node1.Text > Node2.Text then 
            Compare := -1
     else
           if Node1.Text < Node2.Text then
                    Compare := +1
            else Compare := 0
end;

    Реально процедура сортировки выполняется, когда происходит редактирование и названия узла или связанных с ним данных, а также при изменении значения свойства SortType.

    Основные методы класса TTreeView.

function AlphaSort: Boolean;
Сортировка всех узлов дерева в алфавитном порядке.
procedure ClearSelection (KeepPrimary: Boolean = False); 
               virtual;
Убирает выделение со всех выделенных узлов. Если параметр KeepPrimary имеет значение True, то первый элемент в свойстве Selections не переключается.
function CustomSort (SortProc: TTVCompare; Data: Longint; 
                   ARecurse: Boolean = True): Boolean; 
Сортировка узлов с использованием процедуры сравнения, указанной для параметра SortProc (процедурный тип объявлен как type TTVCompare = function (lParaml,lParam2,lParamSort:Longint): Integer stdcall;). Параметр Data передается в процедуру сравнения. Дополнительный параметр ARecurse определяет рекурсивную сортировку для каждого последовательного узла. Если значение параметра SortProc равно nil, то используется процедура сравнения по умолчанию.
procedure Deselect (Node: TTreeNode); virtual;
Убирает все выделения для определенного узла. При значении True свойства MultiSelect и значении msControlSelect свойства MultiSelectStyle метод не работает.
function FindNextToSelect: TTreeNode; virtual;
Начиная с узла, указанного в свойстве Selected, ищет следующий невыделенный узел. При значении True свойства MultiSelect в свойстве Selected может быть указан невыделенный узел. В этом случае возвращается именно этот узел.
procedure FullCollapse; 
Сжатие всех раскрытых узлов дерева.
procedure FullExpand; 
Раскрытие всех узлов дерева.
function GetHitTestInfoAt (X, Y: Integer): THitTests; 
Подробная информация о том, какой части дерева (тип THitTests) принадлежит указанная точка клиентской области (координаты в пикселях).
function GetNodeAt(X, Y: Integer): TTreeNode; 
Получение узла дерева, которому принадлежит указанная точка клиентской области. Возвращает значение nil, если такого узла нет.
function GetSelections (AList: TList): TTreeNode; 
Очищает передаваемый через параметр AList список и копирует туда все выделенные узлы. Возвращаемым значением является свойство Selected.
function IsEditing: Boolean;
Возвращает значение True, если выполняется редактирование одного из узлов дерева.
procedure Select (const Nodes: 
array of TTreeNode); overload; virtual;
procedure Select (Nodes: TList); overload; virtual;, 
procedure Select (Node: TTreeNode; ShiftState: TShiftState = []); overload; 
                               virtual;
Метод используется для выделения одного или нескольких узлов.

    В первых двух вариантах процедуры все узлы, передаваемые через параметр Nodes, выделяются, а для остальных выделенных узлов выделение отменяется. В третьем варианте узлы выделяются как при щелчке на них мышью. Достигается эффект использования клавиш <Ctrl>, <Shift> и правой кнопки мыши при значении параметра ShiftState равном ssCtrl, ssShift или ssRight.

procedure Subselect (Node: TTreeNode; Validate: Boolean = False); 
                       virtual; 
Производит инвертирование состояния выделения узлов. Если параметр Validate имеет значение True, о производится проверка возможности выделения.

    Необходимо отметить, что обращение к узлам дерева по номеру - операция очень неэффективная. Для быстрого перебора всех узлов можно использовать следующий способ.

var
  CurItem: TTreeNode;
begin
  CurItem: = TreeView1.Items.GetFirstNode;
  while CurItem<>nil do
  begin
      // Выполнить нужные действия над узлом CurItem.
      CurItem: = CurItem.GetNext;
  end;
end;

    Основные методы класса TTreeNode перечислены в таблице 3.

Таблица 3. Методы класса TTreeNode
Метод Описание
function AlphaSort: Boolean; Сортировка всех потомков узла
procedure Соllарsе (Recursе: Boolean); Сжатие узла
procedure Delete; Удаление узла и всех его потомков
procedure DeleteChildren; Удаление всех потомков узла
function DisplayRect (TextOnly: Boolean); TRect; Возвращает прямоугольник, которым узел ограничивается на экране. Если значение параметра TextOnly равно True, то в прямоугольник записывается только область текстового имени узла
procedure EndEdit (Cancel: Boolean); Завершает редактирование узла. Если значение параметра Cancel равно True, то восстанавливается прежнее значение свойства Text
procedure Expand (Recurse: Boolean); Разворачивает узел. Если значение параметра Recurse равно True, то разворачиваются и все потомки
function GetFirstChild: TTreeNode; Возвращает первый узел из списка потомков
function GetLastChild: TTreeNode; Возвращает последний узел из списка потомков/TD>
function GetNext: TTreeNode; Возвращает следующий узел по отношению к текущему с учетом невидимых узлов и потомков
function GetNextChild (Value: TTreeNode): TTreeNode; Возвращает следующий потомок по отношению к потомку Value
function GetNextSibling: TTreeNode; Возвращает следующий узел на уровне текущего узла, независимо от того, виден он или нет
function GetNextVisible: TTreeNode; Возвращает следующий видимый узел
function GetPrev: TTreeNode; Возвращает предыдущий узел по отношению к текущему с учетом невидимых узлов и потомков
function GetPrevChild (Value: TTreeNode): TTreeNode; Возвращает предыдущий потомок по отношению к потомку Value
function GetPrevSibling: TTreeNode; Возвращает предыдущий узел на уровне текущего узла, независимо от того, виден он или нет
function GetPrevVisible: TTreeNode; Возвращает предыдущий видимый узел
function HasAsParent (Value: TTreeNode): Boolean; Возвращает значение True, если узел Value является родительским для текущего узла
function IndexOf (Value: TTreeNode): Integer; Возвращает позицию узла в списке потомков узла Value. Если узел Value не прямой родитель текущего узла, то функция возвращает значение -1
procedure MakeVisible; Разворачивает подходящие вышестоящие узлы таким образом, чтобы текущий узел стал видимым
procedure MoveTo (Destination: TTreeNode; Mode: TNodeAttachMode); Перемещает текущий узел в область узла Destination. Конкретное положение определяется значением параметра Mode

    Основные методы класса TTreeNodes.

function AlphaSort (Arecurse: Boolean = False): Boolean; 
Сортировка узлов.
function AddChild (Node: TTreeNode; const 
            S: string): TTreeNode; 
Добавление узла как последнего потомка узла Node.
function AddChildFirst (Node: TTreeNode; const 
          S: string): TTreeNode; 
Добавление узла как первого потомка узла Node.
function AddChildObject (Node: TTreeNode; const 
                S: string; Ptr: Pointer): TTreeNode; 
Добавление узла последним потомком узла Node. Параметр S определяет свойство Text для нового узла. Указатель Ptr определяет свойство Data. Метод возвращает узел, который был добавлен.
function AddChildObjectFirst (Node: TTreeNode; 
     const S: string; Ptr: Pointer): TTreeNode; 
Добавление узла как первого потомка узла Node.
function AddFirst (Node: TTreeNode; const 
          S: string): TTreeNode;
Добавить узел первым на уровне узла Node.
function AddNode (Node, Relative: TTreeNode; const 
      S: string; Ptr: Pointer; Method: TNodeAttachMode): TTreeNode;
Добавить узел в список.
function AddObject (Node: TTreeNode; const 
          S: string; Ptr: Pointer): TTreeNode;
Добавляет узел в конец списка узлов на уровне узла Node. С новым узлом через его свойство Data связывается объект, передаваемый через указатель Ptr.
function AddObjectFirst (Node: TTreeNode; const 
            S: string; Ptr: Pointer): TTreeNode; 
Добавляет узел в начало.
procedure BeginUpdate; procedure EndUpdate; 
Приостановка и возобновление перерисовки дерева. Применяется для ускорения продолжительных операций над деревом.
function GetFirstNode: TTreeNode; 
Получает первый узел дерева.
function GetNode (ItemId: HTreeItem): TTreeNode;
Возвращает узел с дескриптором, передаваемым через параметр ItemId.
function Insert (Node: TTreeNode; const 
          S: string): TTreeNode; 
Добавляет узел перед узлом Node.
function InsertNode (Node, Sibling: TTreeNode; const 
               S: string; Ptr: Pointer): TTreeNode; 
Вставляет узел вместе с данными.
function InsertObject (Node: TTreeNode; const 
           S: string; Ptr: Pointer): TTreeNode;
Вставляет узел вместе с данными перед узлом, указанным в параметре Node.

   

    На следующем шаге мы рассмотрим компонент TListView.




Предыдущий шаг Содержание Следующий шаг