Справочник по компонентам Delphi. Часть 2
Страница 26. Компонент TOutline


 

Компонент TOutline

TObject->TPersistent—>TCoinponent—”TControl—”TWinControl-> —>TCustomControl->TCustomGrid->TCustomOutline-”TOutline Модуль OUTLINE Страница Палитры компонентов Additional

TOutline создан для ведения и отображения иерархических структур данных — деревьев. Это один из наиболее сложных и интересных компонентов. В этот раздел он попал потому, что является потомком таблицы (TCustomGrid), хотя и мало чем ее напоминает.

Типичным примером применения TOutline можно назвать отображение структуры файловой системы (для этого даже есть отдельный компонент TDirectory Outline). В описанном ниже примере OBJTREE этот компонент используется для отображения дерева классов библиотеки VCL.

Дерево состоит из элементов (будем называть их узлами), упорядоченных по уровням. Каждый из них имеет родительский узел (на более высоком уровне) и список дочерних узлов-потомков. Исключением являются корневой узел (ну­левого уровня) — он не имеет предка, и узлы последнего уровня, не имеющие потомков.

 

Каждый узел является объектом класса TOutlineNode:

TOutlineNode = class(TPersistent) Рассмотрим сначала методы и свойства этого объекта. С каждым узлом можно связать информацию — имя и произвольные данные:

property Text: string;
property Data: Pointer;

Указатель на родительский узел определяется свойством:

(Ro) property Parent: TOutlineNode;

Если список дочерних узлов не пуст, что можно проверить при помощи свой­ства

(Ro) property Hasltems: Boolean;

то для получения информации о них есть метода,!:

function GetFirstChild: Longint;
function GetLastChild: Longint;

function GetNextChild(Value: Longint): Longing-function GetPrevChiId(Value: Longint): Longint;

В последних двух методах параметр Value содержит индекс предыдущего най­денного потомка. Индекс — это уникальный идентификатор узла в      дереве, определенный свойством:

(Ro) property Index: Longint;

Используя его, можно получить указатель на узел (см. ниже сам компонент TOutline). Узлы пронумерованы таким образом, что если родитель имеет номер N, то первый из его потомков — номер (N+1).

Можно узнать и полное имя (путь) узла, представляющее собой конкатенацию всех родительских имен и имени узла, разделенных специальным символом. Этот символ определен в свойстве ItemSeparator объекта TCustomOutline, ко­торому принадлежат узлы. Полный путь определяется свойством:

(Ro) property FullPath: string;

Каждый узел расположен на своем иерархическом уровне. На самом верху находится узел уровня 0. Все другие являются для пего дочерними; он не виден и не доступен пользователю. Поэтому на первом доступном уровне — с номером 1 — могут находиться по вашему желанию один или несколько узлов. Уровень иерархии узла можно узнать в его свойстве:

(ro)property Level: Cardinal;

Чтобы изменить уровень, надо вызвать метод:

procedure ChangeLevelBy(Value: TChangeRange);

которьш перемещает узел в список другого уровня. Диапазон перемещения TChangeRange ограничен:

TChangeRange = -1..1;

Чтобы переместить узел в дереве, нужно вызвать следующий метод

procedure MoveTo(Destination: Longint; AttachMode: TAttachMode);

TAttachMode = (oaAdd, oaAddChild, oalnsert) ;

Он перемещает узел (вместе со всеми потомками) в положение с индексом Destination. Родительский узел не может переместиться на место любого из своих потомков. Режим перемещения AttachMode означает:

oaAdd — добавить последним на том же уровне, что и Destination;

oaAddChild — добавить последним к потомкам узла Destination;

oalnsert — заменить в положении Destination прежний узел, которьш сме­щается дальше по списку на том же уровне.

Индекс того из предков узла, которьш находится в списке самого высокого уровня, известен из свойства:

(Ro) property Topltem: Longint;

Для корневого узла это свойство равно 0.

Компонент TOutline не только хранит древовидную структуру, он еще и отоб­ражает ее. На экране каждый узел может находиться в двух состояниях, свер­

нутом и развернутом. В свернутом состоянии потомки узла не видны, в раз­вернутом они изображаются чуть ниже и правее друг под другом. Состояние узла может иллюстрироваться значком (плюс/минус, открытая/закрытая папка — см. описание свойства OutlineStyle компонента TOutline). В таблице приведены методы и свойства, управляющие состоянием узла на экране:

property Expanded: Boolean;

Указывает, в каком состоянии находится узел; True — развернутое состояние.

procedure Collapse;

Сворачивает узел, пряча все дочерние узлы.

procedure Expand;

Разворачивает узел списка, показывая дочерние узлы.

procedure FullExpand;

Полностью разворачивает узел, показывая все дочерние узлы всех подуровней.

 

Сворачивать/разворачивать узлы дерева, помимо    щелчков мышью,        можно нажатием клавиш:

<+> — соответствует Expand;
<-> — соответствует Collapse;
<*> — соответствует FullExpand. Свойство
(Ro) property IsVisible: Boolean;

означает, может ли быть виден узел. Это возможно только в том случае, если виднь! все его родители. При отрисовке ширина узла сообщается методом:

function GetDisplayWidth: Integer;

Теперь перейдем от описания узла к описанию самого дерева — компонента TOutline. Он представляет собой совокупность узлов типа TOutlineNode. Всего в дереве содержится число узлов, равное значению свойства:

(Ro) property ItemCount: Longint;

К каждому из них можно обратиться, используя свойство:

(Ro) property Items[Index: Longint]: TOutlineNode;

На текущий (выделенный) узел можно сослаться через свойство:

property Selectedltem: Longint;

Например, выполнение оператора

Items[Items[Selectedltem].Topltem].FullExpand;

приведет в развернутое состояние ту часть дерева, в которой находится вы­деленный узел (начиная от его самого далекого предка Topltem). Свойство

property Row: Longint;

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

function GetDataItem(Value: Pointer): Longint;
function GetTextItem(const Value: string): Longint;

 

Можно найти узел и по координатам точки в клиентской области компонента (на самом деле играет роль только координата Y) с помощью следующего метода:

function GetItemfX, Y: Integer): LongInC;

Целых три пары методов добавляют новый узел в дерево. Его имя инициализируется параметром Text. Различие между первым и вторым мето­дами в парах в том, что второй позволяет связать с узлом данные (параметр Data), а в первом вместо них записывается nil.

function Add(Index: Longint; const Text: string): Longint;
function AddObject(Index: Longint; const Text: string;
const Data: Pointer): Longint;

— добавляют новый узел в качестве последнего в тот же список, где находится узел Index. Поскольку на самом верху иерархии может быть только один узел, то в случае задания Index = 0 он добавляется в список верхнего уровня. Соответствуют режиму oaAdd;

function AddChild(Index: Longint; const Text: string): Longint;
function AddChildObject(Index: Longint; const To" : string;
const Data: Pointer): Longint;

— добавляют новый узел в качестве последнего потомка узла Index. Соответ­ствуют режиму oaAddChild;

function Insert(Index: Longint; const Text: string): Longint;
function InsertObject(Index: Longint; const Text: string;
const Data: Pointer): Longint;

— вставляют узел в положение Index. При этом прежний узел с таким индексом и все другие на этом уровне сдвигаются вниз. Единственное исключение — задание в качестве параметра нулевого индекса. В этом случае узел добавляется последним в список самого верхнего уровня. Соответствуют режиму oalnsert. Все методы вызывают переиндексирование дерева и возвращают уже новый индекс для вставленного узла. Если нужно удалить узел, то метод procedure Delete(Index: Longint);

удаляет узел с индексом Index, а метод
procedure Clear;
очищает все дерево.

Если нужно вставить или удалить сразу много узлов и при этом избежать трудоемких операций переиндексации и перерисовки дерева, то соответству­ющий код надо заключить между вызовами методов:

procedure BeginUpdate;
procedure EndUpdate;

Первый устанавливает специальное состояние обновления и на время запрещает все пересчеты и перерисовки, а второй сбрасывает его и обновляет дерево. Это состояние можно изменить и при помощи метода:

procedure SetUpdateSCate(Value: Boolean);

Дерево можно полностью свернуть или полностью развернуть вызовом мето­дов:

procedure FullExpand;
procedure FullCollapse;

Каждый узел, изменяя свое состояние, предупреждает об этом родительское дерево. В зависимости от того, свернулся он или развернулся, вызываются обработчики событий

(pb) property OnExpand: EOutlineChange;

(РЬ) property OnCollapse: EOutlineChange;

EOutlineChange= procedure (Sender: TObject; Index: Longint) of object;

где параметр Index означает индекс узла, измешшшего состояние. Метод
function GetNodeDisplayWidth(Node: TOutlineNode): Integer;

возвращает ширину, занимаемую изображением узла (если установленньш стиль не osOwnerDraw, см. ниже).

Дерево может быть загружено и выгружено в поток и файл при помощи методов:

procedure LoadFromFile(const FileName: strings-procedure LoadFromStream(Stream: TStream);

procedure SaveToFile(const FileName: strings-procedure SaveToStream(Stream: TStream) ;

Для прокрутки информации TOutline по умолчанию имеет обе полосы, опре­деляемые свойством:

(Pb) property ScrollBars: TScrollStyle;

TScrollStyle = (ssNone, ssHorizontal, ssVertical, ssBoth);

Строка (символ), разделяющий имена узлов при составлении полного имени узла, содержится в свойстве:

(Pb) property ItemSeparator: string;

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

(Pb) property Lines: TStrings;

Это свойство предназначено для использования именно на этапе разработки, так как свойство Items в это время недоступно. Во время исполнения с инфор­мацией об узлах нужно работать, используя свойство Items. При рисовании дерева, помимо собственно текста, вместе с ним может изоб­ражаться одна из пиктограмм:

Из) property PicturePlus: TBitmap;

Соответствует свернутому узлу. По умолчанию — "плюс".

(Pb) property PictureMinus: TBitmap;

Соответствует развернутому узлу. По умолчанию — "минус".

(Pb) property PictureOpen: TBitmap;

Соответствует развернутому узлу. По умолчанию — "открытая папка".

(Pb) property PictureClosed: TBitmap;

Соответствует свернутому узлу. По умолчанию — "закрытая папка".

(Pb) property PictureLeaf: TBitmap;

Соответствует "листу" — узлу без потомков. По умолчанию — "документ".

 

Желательно, чтобы картинки имели "прозрачный" фон, то есть чтобы их свойство TransparentColor соответствовало цвету рабочей области компонента. Эти пиктограммы можно переопределить в случае необходимости.

Свойство OutlineStyle определяет, в частности, когда и какие пиктограммы будут показаны:

(Pb) property OutlineStyle: TOutlineStyle;

TOutlineStyle = (osText, osPlusMinusText, osPictureText, osPlusMinusPictureText, osTreeText, osTreePictureText);

В зависимости от значения этого свойства изображаются:

osText — только текст;

osPlusMinusText — текст и пиктограммы PicturePlus и PictureMinus в зависимости от состояния узла. "Лист" не имеет значка;

osPictureText — текст и пиктограммы PictureOpen, PictureClosed и PictureLeaf в зависимости от состояния узла;

osPlusMinusPictureText — объединяет в себе два предыдущих стиля;

osTreeText — текст и специальные линии, иллюстрирующие связь между родительским и дочерними узлами;

osTreePictureText — объединяет в себе стили osTreeText и osPictureText. По умолчанию установлен стиль osTreePictureText;

На внешний вид дерева влияют и опции, содержащиеся в свойстве:

(Pb) property Options: TOutlineOptions;

TOutlineOption =- (ooDrawTreeRoot, ooDrawFocusRect, ooSCretchBitmaps) ;

TOutlineOptions =- set of TOutlineOption;

Это множество может содержать элементы:

ooDrawTreeRoot — задает соединение линией всех узлов верхнего уровня. В случае отсутствия опции каждый из них выглядит как вершина от­дельного дерева. Эта опция играет роль только для стилей osTreeText, osTreePictureText;

ooDrawFocusRect — задает выделение сфокусированного узла рамкой из точек;

ooStretchBitmaps — задает приведение размеров пиктограмм к размерам шрифта текста путем масштабирования. В противном случае либо из пиктограммы вырезается часть (если она больше), либо остается свободное место (если меньше).

Рисование дерева по умолчанию осуществляется системой, но может быть возложено и на программиста. Определяется это свойством:

(№) property Style: TOutlineType;

TOutlineType = (otStandard, otOwnerDraw) ;

 

Стиль osStandard подразумевает, что дта каждого узла будет изображено то, что предусмотрено стилем OutlineStyle. Для реализации стиля otOwnerDraw нужно нарисовать содержимое узла в обработчике события:

(Pb) property OnDrawItern: TDrawItemEvent;

TDrawItemEvent = procedure(ListBox: TListBox; Index: Integer;

Rect: TRect; State: TOwnerDrawState) of object;

Параметры:
Index — индекс узла;

Rect — отведенный ему прямоугольник;

State — множество, в которое могут входить состояния odSelected, odFocused.

Высота каждого узла постоянна и в этом случае определяется свойством:

(Pb) property ItemHeight: Integer;

Для рисования у компонента есть своя канва:

property Canvas: TCanvas ;

Установить ее можно только для объекта стиля osOwnerDraw; при osStandard канва игнорируется.

Обрамление компонента задается свойством:

(Pb) property BorderStyle: TBorderStyle;

Разобраться с применением этого компонента поможет пример OBJTREE. В нем по заранее заданному массиву компонентов ClassesSet выстраивается иерархическое дерево, в которое добавляются и все объекты-предки. Для примера были выбраны только 32 компонента, при желании можно включить и все остальные. Не забывайте при этом добавить содержащие их модули к тем, что содержатся в операторе uses. Когда вы перемещаете фокус по дереву, отображается имя объекта и имя модуля, в котором он описан. Для этого используется информация, возвращаемая недокументированным (пока?) мето­дом класса TObject.ClassInfo. Возвращаемая им структура описана в исходных текстах VCL.

 
« Предыдущая статья   Следующая статья »