Как считать содержимое дерева ttreeview из файла

Обновлено: 05.07.2024

XML - Расширяемый Язык Разметки (eXtensible Markup Language) рекомендован W3C как язык для обмена информацией между различными системами. Это ориентированный на текст способ сохранения информации. Современные языки обмена данными, такие как XHTML, так же как и большинство технологий WebServices, основаны на XML.

Это текстовый способ хранения информации, а не хранения информации в двоичном формате.

Современные языки обмена данными, такие как XHTML, а также большинство технологий WebServices, основаны на XML. Эта вики может действительно дать только краткий обзор XML, при этом основное внимание уделяется анализу и использованию файлов XML в приложениях Free Pascal. Если вас интересует более полное объяснение XML и его использования, см. здесь.

Contents

Введение

В настоящее время в Free Pascal существует ряд модулей для поддержки XML. Эти модули называются "XMLRead", "XMLWrite" и "DOM", и являются частью Free Component Library (FCL) из комплекта Free Pascal. FCL уже находится в заданном по умолчанию пути поиска файлов для компилятора в Лазарусе, таким образом Вам нужно только добавить названия модулей в строку USES чтобы получить поддержку XML в Вашей программе. Использование XML пока не документировано, поэтому данная статья даёт необходимые вводные сведения для работы с модулями поддержки XML.

DOM XML (Объектная модель документов) - это ряд стандартизированных объектов, которые предоставляют однотипный интерфейс для использования XML в различных языках и платформах. Стандарт определяет только методы, свойства и другие части интерфейса объекта, оставляя реализацию свободной для различных языков. FCL в настоящее время поддерживает полностью DOM 1.0.

Примеры

В статье даны примеры работы с XML-данными по принципу нарастающей сложности.

Использование: Юникод или Ansi

FPC предоставляет XML модули, использующие кодировку ANSI. Поэтому, они могут различаться в зависимости от платформы, в которой выполняется код и не иметь поддержку Юникода. Lazarus, в свою очередь, предоставляет собственный набор XML модулей, расположенных в пакете LazUtils. Они полностью поддерживают Юникод в формате UTF-8 и не зависят от выполняющей код платформы. Эти модули взаимозаменяемы и их использование определяется лишь наличием в списке uses.

Следующие модули FPC XML используют системную кодировку:

  • DOM
  • XMLRead
  • XMLWrite
  • XMLCfg
  • XMLUtils
  • XMLStreaming

Эти модули предоставлены Lazarus XML и имеют поддержку Юникода в формате UTF-8:

  • laz2_DOM
  • laz2_XMLRead
  • laz2_XMLWrite
  • laz2_XMLCfg
  • laz2_XMLUtils
  • laz_XMLStreaming.

Не все из них нужны в каждом примере. Однако, вам понадобится DOM, т.к. в нем содержится несколько типов, включая TXMLDocument.

Чтение текстового узла

Помните, что когда Вы работаете с TXMLDocument, текст в пределах узла считается отдельным текстовым узлом. Таким образом, Вы должны обратиться к текстовому значению узла, как к отдельному узлу. Альтернативно, свойство TextContent может быть использовано для возврата значения всех узлов лежащих ниже, которые связаны с данным.

Процедура ReadXMLFile всегда создаёт новый TXMLDocument, таким образом Вы не должны создавать его заранее. Однако Вы должны вызывать метод Free вручную после окончания работы с документом для освобождения ресурсов занятых объектом TXMLDocument.

Для примера рассмотрим следующий XML-файл:

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

Обратите внимание, что ReadXMLFile (. ) игнорирует все начальные символы пробелов при анализе документа. Раздел символы пробелов описывает, как их сохранить.

Вывод имен узлов и атрибутов

Маленькое замечание о навигации по дереву DOM:

Для последовательного доступа к узлам лучше всего использовать свойства FirstChild и NextSibling (чтобы шагать вперед по дереву) или LastChild и PreviousSibling (назад с конца дерева). Для произвольного доступа к узлам дерева можно пользоваться функциями FindNode (ищется первый узел верхнего уровня с подходящим именем) или GetElementsByTagName (создается объект TDOMNodeList, который после использования должен быть освобождён). Это поведение отличается от других реализаций DOM (например, MSXML), поскольку FCL реализация основана на объектах, а не на интерфейсах.

Прим.перев: кроме того, существует функция GetNamedItem для поиска аттрибута по имени в текущем узле. Упрощенный пример (оригинал кода здесь):


Следующий пример демонстрирует, как выводить имена узлов в компонент TMemo, расположенный на форме.

Ниже приведён XML-файл с именем 'C:\Programs\test.xml':

И код на Pascal, который выполняет эту задачу:

В результате программа выведет следующее:

Прим. перев.: Один из участников форума Werner Pamler (a.k.a. wp) предложил рекурсивную функцию поиска по всему дереву первого подходящего дочернего узла по имени, начиная с указанного родительского узла:

Загрузка XML в TreeView

Одно из обычных использований файла XML - разбор и показ информации в древовидном формате. Вы можете отыскать компонент TTreeView на вкладке "Common Controls" Lazarus'а.

Функция, приведённая ниже, возмёт документ XML, предварительно загруженный из файла или сгенерированный программно, и заполнит TreeView его содержимым. Заголовком каждого узла будет содержимое первого атрибута этого узла.

Другой пример, который отображает полную структуру XML, включая все значения атрибутов (примечание: длинная строка, ссылающаяся на TreeView, была разделена, поэтому она будет переносить слова для этой вики; при записи в коде вам не нужно разбивать строку, если вам не нравится форматирование ):

Изменение XML документа

Первая вещь, о которой следует помнить, TDOMDocument это хэндл (handle) к DOM. Вы можете получить экземпляр этого класса создавая или загружая XML документ.

С другой стороны, узлы не могут быть созданы как обычные объекты. Вы должны использовать методы, которые предоставляет TDOMDocument для их создания и, впоследствии, использовать другие методы для помещения их в нужное место дерева. Это говорит о том, что узлы должны принадлежать вполне определённому документу DOM.

Вот некоторые общераспространённые методы TDOMDocument:

CreateCDATASection создает раздел CDATA: обычные символы разметки XML, такие как <>, не интерпретируются в разделе CDATA. См. статью по CDATA на Wikipedia

Более удобный метод для управления атрибутами - использовать метод TDOMElement.SetAttribute, который также представлен как свойство по умолчанию TDOMElement:

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

Примечание: Имена аттрибутов узла не могут начинаться с цифры! Если имена аттрибутов у узла одинаковые, то записывается значение последнего указанного аттрибута


Вот, в качестве примера, метод, который ищет выбранный элемент в TTreeView и затем вставляет дочерний узел в документ XML, где он должен находиться . TreeView должен быть предварительно заполнен содержанием из XML файла, используя XML2Tree function.

Создание TXMLDocument из строки

Если данные XML находятся в строке MyXmlString, создать из нее DOM можно с помощью следующего кода:

Проверка достоверности документа

Начиная с марта 2007, в FCL XML парсер добавлена возможность проверки достоверности (валидации) документа с помощью DTD. Проверка достоверности определяет соответствие логической структуры документа определенным правилам, заданных с помощью "Определение Типа документа" (DTD).

Это пример XML-документа, содержащего DTD:

Этот DTD определяет, что элемент root должен содержать один или более элемент child , а элементы child могут содержать только символьные данные. Если парсер обнаружит нарушение этих правил, то он сообщит о них.

Загрузка такого документа несколько сложнее. Пусть исходные XML данные содержатся в потоке AStream:

Cимволы пробелов

Если необходимо сохранять пробельные символы в тексте узлов, следует пользоваться вышеприведённым методом загрузки XML документа. По умолчанию начальные пробельные символы игнорируются, именно поэтому функция ReadXML(. ) пропускает все пробельные символы в начале текста узлов. Перед вызовом Parser.Parse(Src, TheDoc) добавьте следующую строку:

После этого парсер будет оставлять все пробельные символы, включая все переводы строк, добавленные для повышения читаемости XML документа!

Используйте следующий код, чтобы у вас была процедура ReadXMLFile, которая сохраняет первые пробелы:

Потоковое чтение

Генерация файла XML

Ниже приведён код для записи XML в файле. (Взято из обучающей программы в блоге DeveLazarus). Пожалуйста помните, что модули DOM и XMLWrite должны быть включены в Uses (прим.перев. в примерах ниже для сохранениея XML-документа в файл используется перегруженная(overload) процедура WriteXMLFile)

Вот такой XML-файл у нас должен получится в результате:

Прим.переводчика: обратите внимание, что функция function CreateElement(const tagName: DOMString): TDOMElement; virtual; допускает в качестве агрумента строки, содержащие только цифры и буквы латиницы без пробелов, т.е. в выше приведенном примере попытка написать:

немедленно вызовет исключение "EDOMError in DOMDocument.CreateElement" с кодом ошибки INVALID_CHARACTER_ERR.

То же самое правило справедливо и для имен аттрибута/ов узла - см. мое замечание выше.

Пример, где вам не нужно ссылаться на элемент по индексу.

Кодировки

Начинаясь с ревизии SVN 12582, XMLReader может читать данные в любой кодировке при условии использования внешнего декодера. Смотрите XML_Decoders/ru для более детальной информации.

Согласно спецификации XML, атрибут encoding в первой строке XML является необязательным в случае, если фактическая кодировка - UTF-8 или UTF-16 (что определяется наличием BOM). В версии Lazarus 0.9.26 TXMLDocument имеет свойство Encoding, но оно игнорируется. При этом процедура WriteXMLFile всегда использует кодировку UTF-8 и не генерирует атрибут encoding в первой строке файла XML.

Как программно можно сохранить ветви с корнями и потом загрузить? Эта тема не подходит Сохранение и загрузка TreeView. Как правильно реализовать действие? Спасибо.

__________________
Помощь в написании контрольных, курсовых и дипломных работ здесь

Сохранение/загрузка TreeView
Всем привет. Возникла следующая проблема. Имею дерево: public static TreeView TV; //.

Сохранение и загрузка TreeView
Народ, почему так получается. Есть файл 1.txt в нем записано что-то типа 11 22//тут пробел перед.


Загрузка и сохранение содержимого TreeView
Всем привет! Возникла проблема с сохранением содержимого ttreeview. Нужно сделать так, чтобы.

Решение

Не знаю, может быть есть варианты получше, я пользуюсь таким, уже давно написанным.
Можно копировать данные так Nodes.CopyTo и вставить в Nodes.AddRange

MrCrown Спасибо, код сохраняет и загружает.

Почему неполучается сохранить так?

Появляется ошибка: Невозможно преобразовать значение типа
"System.Windows.Forms.TreeNode" в "String".

Решение

это и есть конверция данных в текстовый документ из xml в TreeView и обратно.

Пример сохранения с xml.

сохранение TreeView
Доброговремени суток! Столкнулся с одной проблемой(( Нужно, чтоб при изменении итема TreeView.


Сохранение TreeView списка
Вопрос таков, кто сталкивался может кто знает как не в ручную можно сохранить TreeNode из TreeView.


Опросник. Создание дерева зависимостей в treeView, сохранение дерева в XML, построение дерева в treeView из XML
Всем доброго времени суток. Тема является продолжением вот этой темы. Создаю 2ю, так как там.

Применение компонента TTreeView для работы с древовидными иерархическими данными.

Дерево TTreeView

Компонент TTreeView расположен на вкладке Common Controls Палитры компонентов, и предназначен для отображения различных древовидных иерархических структур:

Компонент TTreeView

Мы часто сталкиваемся с иерархической информацией. Это может быть т.н. Дерево каталогов в стандартном Проводнике Windows , которое отображает в виде ветвей дерева внешние и вложенные папки, и файлы, которые там хранятся. Это может быть структура предприятия, библиотечный каталогизатор, структура книги, реферата, курсовой… В общем, самая разная информация , в которой присутствуют родительские и дочерние (вложенные) объекты. Все подобные объекты можно отобразить с помощью компонента TTreeView .

Объекты, которые содержаться в данном компоненте называются узлами (англ. node), а сам компонент представляет собой список узлов. Подобно спискам TListBox и TComboBox , с которыми мы уже знакомы, TTreeView имеет свойство Items - индексированный список узлов. Каждый узел - это объект , который имеет тип TTreeNode . И родительские, и вложенные в них дочерние элементы - всё это узлы (объекты) типа TTreeNode . Причем дочерний элемент одновременно может быть родителем по отношению к другому узлу, уровень вложенности неограничен.

Но давайте-ка всё по порядку. Рассмотрим работу компонента на примере библиотечного каталога. Откройте Lazarus с новым проектом. Как обычно, форму назовите fMain, проект сохраните под именем MyLibrary в папку 19-01, модулю формы дайте имя Main. В свойстве Caption формы напишите Библиотечный каталог. Саму форму немного растяните, пусть у нас будет высота 350, а ширина 500 пикселей.

Далее, установим на форму простую панель TPanel , из нее мы сделаем своеобразную Панель инструментов программы. У панели очистите свойство Caption , в свойстве Align установите alLeft , в свойстве Width 127 пикселей.

Далее, на полученную Панель инструментов одну за другой установите 7 простых кнопок TButton. У всех кнопок в свойстве Left установите значение 1, а в свойстве Width - значение 125. Верхнюю кнопку расположите повыше ( Top = 1 ), остальные - чуть ниже, чтобы между кнопками было совсем небольшое расстояние . Нам нужно переименовать кнопки и сделать на них соответствующие надписи. Сделайте следующие настройки кнопок:

Теперь, правее панели, с вкладки Common Controls установите компонент TTreeView . Поскольку дерево у нас одно, переименовывать его мы не будем. В свойстве Align дерева также установите значение alLeft , в свойстве Width установите 360 пикселей. Поверх TTreeView с этой же вкладки установите список изображений TImageList , его тоже переименовывать не будем. В результате у нас должна получиться вот такая форма:

Полученная форма

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

Итак, выделите дерево TreeView1 и обратите внимание на его свойства. Мы рассмотрим только основные, с которыми еще не сталкивались в других компонентах.

Свойства TTreeView

Редактор элементов компонента TTreeView


Рис. 19.3. Редактор элементов компонента TTreeView

Как видите, кнопки практически идентичны тем, что мы установили на форму, и работают примерно также. Так что мы не будем рассматривать работу с этим редактором, поскольку почти всегда дерево заполняют не вручную, а программно. Обратиться к отдельному узлу дерева можно через свойство Items , указав индекс элемента; индексация начинается с нуля. Например, к первому узлу в списке обращаются так: TreeView1.Items[0] .

  • msControlSelect - с нажатой и удерживаемой <CTRL>, когда щелкают по элементам списка в произвольном порядке.
  • msShiftSelect - с нажатой и удерживаемой <SHIFT>, когда выбирают сразу диапазон элементов, щелкая сперва по первому, затем по последнему элементу.
  • msSiblingOnly - как msShiftSelect , но в диапазон включаются только узлы одного уровня.
  • msVisibleOnly - как msShiftSelect , но в диапазон не включаются нераскрытые дочерние узлы.
  • stNone - нет сортировки.
  • stText - сортировка по тексту.
  • stData - сортировка по данным.
  • stBoth - сортировка и по тексту, и по данным.

TreeLineColor и TreeLinePenStyle отвечают за цвет и тип линий ветвей дерева.

Цель лекции

Применение компонента TTreeView для работы с древовидными иерархическими данными.

Дерево TTreeView

Компонент TTreeView расположен на вкладке Common Controls Палитры компонентов, и предназначен для отображения различных древовидных иерархических структур:

Компонент TTreeView

Мы часто сталкиваемся с иерархической информацией. Это может быть т.н. Дерево каталогов в стандартном Проводнике Windows , которое отображает в виде ветвей дерева внешние и вложенные папки, и файлы, которые там хранятся. Это может быть структура предприятия, библиотечный каталогизатор, структура книги, реферата, курсовой… В общем, самая разная информация , в которой присутствуют родительские и дочерние (вложенные) объекты. Все подобные объекты можно отобразить с помощью компонента TTreeView .

Объекты, которые содержаться в данном компоненте называются узлами (англ. node), а сам компонент представляет собой список узлов. Подобно спискам TListBox и TComboBox , с которыми мы уже знакомы, TTreeView имеет свойство Items - индексированный список узлов. Каждый узел - это объект , который имеет тип TTreeNode . И родительские, и вложенные в них дочерние элементы - всё это узлы (объекты) типа TTreeNode . Причем дочерний элемент одновременно может быть родителем по отношению к другому узлу, уровень вложенности неограничен.

Но давайте-ка всё по порядку. Рассмотрим работу компонента на примере библиотечного каталога. Откройте Lazarus с новым проектом. Как обычно, форму назовите fMain, проект сохраните под именем MyLibrary в папку 19-01, модулю формы дайте имя Main. В свойстве Caption формы напишите Библиотечный каталог. Саму форму немного растяните, пусть у нас будет высота 350, а ширина 500 пикселей.

Далее, установим на форму простую панель TPanel , из нее мы сделаем своеобразную Панель инструментов программы. У панели очистите свойство Caption , в свойстве Align установите alLeft , в свойстве Width 127 пикселей.

Далее, на полученную Панель инструментов одну за другой установите 7 простых кнопок TButton. У всех кнопок в свойстве Left установите значение 1, а в свойстве Width - значение 125. Верхнюю кнопку расположите повыше ( Top = 1 ), остальные - чуть ниже, чтобы между кнопками было совсем небольшое расстояние . Нам нужно переименовать кнопки и сделать на них соответствующие надписи. Сделайте следующие настройки кнопок:

Теперь, правее панели, с вкладки Common Controls установите компонент TTreeView . Поскольку дерево у нас одно, переименовывать его мы не будем. В свойстве Align дерева также установите значение alLeft , в свойстве Width установите 360 пикселей. Поверх TTreeView с этой же вкладки установите список изображений TImageList , его тоже переименовывать не будем. В результате у нас должна получиться вот такая форма:

Полученная форма

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

Итак, выделите дерево TreeView1 и обратите внимание на его свойства. Мы рассмотрим только основные, с которыми еще не сталкивались в других компонентах.

Свойства TTreeView

Редактор элементов компонента TTreeView


Рис. 19.3. Редактор элементов компонента TTreeView

Как видите, кнопки практически идентичны тем, что мы установили на форму, и работают примерно также. Так что мы не будем рассматривать работу с этим редактором, поскольку почти всегда дерево заполняют не вручную, а программно. Обратиться к отдельному узлу дерева можно через свойство Items , указав индекс элемента; индексация начинается с нуля. Например, к первому узлу в списке обращаются так: TreeView1.Items[0] .

  • msControlSelect - с нажатой и удерживаемой <CTRL>, когда щелкают по элементам списка в произвольном порядке.
  • msShiftSelect - с нажатой и удерживаемой <SHIFT>, когда выбирают сразу диапазон элементов, щелкая сперва по первому, затем по последнему элементу.
  • msSiblingOnly - как msShiftSelect , но в диапазон включаются только узлы одного уровня.
  • msVisibleOnly - как msShiftSelect , но в диапазон не включаются нераскрытые дочерние узлы.
  • stNone - нет сортировки.
  • stText - сортировка по тексту.
  • stData - сортировка по данным.
  • stBoth - сортировка и по тексту, и по данным.

TreeLineColor и TreeLinePenStyle отвечают за цвет и тип линий ветвей дерева.

Методы TTreeView

События TTreeView

Компонент имеет несколько специфичных событий, которые могут оказаться полезными.

Свойства и методы TTreeView.Items

Как уже упоминалось, при программной обработке дерева (а чаще всего, такая обработка и используется) приходится пользоваться свойством Items , которое имеет тип TTreeNodes и само является объектом, а потому имеет собственный набор свойств и методов. Разберем основные из них:

Свойства

Нередко возникает необходимость обойти всё дерево, от первого до последнего узла. Например, изменить текст всех узлов можно так (пример выполнять не нужно):

В этом случае мы получим дерево с текстом

Методы

Основные свойства узла TTreeNode

К отдельному узлу можно получить доступ через свойство Item , например, TreeView1.Item[0] .

Пожалуйста, не путайте - свойство компонента Items имеет тип TTreeNodes , и представляет собой индексированный список узлов. А отдельный узел имеет тип TTreeNode - это не одно и то же!

C:\Lazarus\Images

Не будем копаться по вложенным папкам - прямо тут находятся две подходящих пиктограммы. Первым добавьте изображение folder.jpg, это будет пиктограмма для родительских узлов. Пиктограмма получила индекс 0. Вторым добавьте изображение template.jpg, это будет изображение вложенных подразделов. Картинка встала под индексом 1. Можно закрыть редактор ImageList кнопкой "ОК".

Теперь приступим к программированию кнопок. Сгенерируйте событие OnClick для кнопки "Новый раздел". Её код будет следующим:

Как видно из кода и комментариев, эта кнопка добавляет в конец списка узлов новый родительский узел. Функцией-запросом InputQuery() мы получаем у пользователя заголовок для будущего узла. Если пользователь закрыл диалог, не введя этого заголовка, то мы просто выходим из события, ничего не предпринимая. Но если он что-то туда ввел, то этот заголовок попадает в переменную NodeCaption , и мы приступаем к созданию родительского узла, что и делает код:

То, что узел родительский, говорит параметр nil - ничто, указывающий, что у нового узла нет родителя.

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

Код для кнопки "Новый подраздел" очень похож на предыдущий:

Разницы тут две. Во-первых, в этот раз мы используем метод Items.AddChild , который добавляет именно дочерний узел. В параметре вместо nil вы видите уже TreeView1.Selected , что означает ссылку на выделенный в данный момент раздел. Именно для этого раздела будет создаваться подраздел.

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

Если у нового узла все-таки нет родителя ( NewNode.Parent = nil ), то присваиваем узлу изображение 0, иначе это будет изображение 1.

Для кнопки "Удалить" код будет следующим:

Код очень простенький. Если выделенный узел не равен nil (то есть, если вообще какой-то узел выделен), то мы удаляем из списка этот выделенный узел.

Код для кнопки "Переименовать":

Здесь мы точно также пытаемся получить у пользователя новый заголовок. Если он его ввел, то этот новый заголовок мы присваиваем свойству Text выделенного в данный момент узла.

Теперь напишем код для кнопки "Сортировать". Её код совсем простой:

Метод AlphaSort возвращает истину, если сортировка прошла успешно. Но нам нет смысла проверять эту успешность, поэтому сам метод мы вызываем, а на возвращаемое им значение не обращаем внимания.

Далее на очереди у нас кнопка "Свернуть список". Её код не сложнее:

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

Методы FullCollapse и FullExpand мы изучали выше.

Кнопки мы запрограммировали. Однако пока толку от нашей программы - ноль. Пользователь потратит время, заполняя список разделов и подразделов библиотеки, но стоит ему только выйти из программы, и вся эта работа потеряется. Нам нужно научить программу этот список сохранять в файл, и загружать его из файла. Файл списка назовем MyLibrary.dat. MyLibrary - потому, что так называется наша программа, dat - такое расширение традиционно имеют файлы с данными. Где лучше всего загружать этот список? Конечно, в событии OnCreate главной формы! Это событие возникает однажды, когда загружается программа, но перед ее отображением на экране. Если вам требуется сделать какую то подготовительную работу перед открытием вашей программы, то OnCreate для этого - самое место. Выделите форму fMain - это можно сделать, щелкнув по маленькому свободному участку правее компонента TreeView1 , или выбрав fMain в верхней части Инспектора объектов. Затем перейдите на вкладку "События" Инспектора объектов, найдите и сгенерируйте событие OnCreate . Код будет следующим:

Здесь мы сначала с помощью функции FileExists() проверяем, есть ли вообще в текущей папке файл MyLibrary.dat? Функция вернет истину, если такой файл есть. В этом случае мы его загружаем в дерево TreeView1 . Но дерево выйдет без пиктограмм, их еще нужно загрузить. Это мы делаем в цикле for , обходя все узлы дерева. Если узел родительский ( TreeView1.Items[i].Parent=nil ), мы присваиваем ему картинку с индексом 0, иначе - картинку с индексом 1.

Осталось научить программу сохранять список. Делать это лучше всего при выходе из программы, в событии OnClose главной формы. В этом событии обычно делают все завершающие действия - закрывают открытые ресурсы, сохраняют параметры программы, и т.п. Код события следующий:

Тут мы просто сохраняем список в файл. Сохраните проект, запустите его и попробуйте заполнить разделы и подразделы библиотеки. У меня получилось примерно так:

Работающая программа

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


Теперь нужно указать у нашего дерева в свойстве images установленный набор картинок. После этого можно переходить к программированию.

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

if not InputQuery( ' Ввод имени', 'Введите заголовок элемента',CaptionStr) then exit;

NewNode: =TreeView1.Items. Add (TreeView1. Selected, CaptionStr) ;

if NewNode.Parentonil then NewNode.ImageIndex:=1;

Здесь мы объявили две переменные:

  • CaptionStr — типа строка string;
  • NewNode — типа TTreeNode (тип TTreeNode — это тип отдельного элемента дерева).

В первой строке кода мы обнуляем строку CaptionStr. Эта строка в будущем

будет использоваться для хранения имени нового элемента дерева. Вторая строка имеет следующий код:

if not InputQuery('Ввод имени', 'Введите заголовок элемента', CaptionStr) then exit;

Дерево элементов (TTreeViert) в Delphi

Здесь выполняется функция InputQuery, которая используется для вывода на экран окна ввода. У этой функции есть три параметра.

  • Заголовок окна ввода.
  • Текст-пояснение, который подсказывает пользователю, что ему надо вводить.
  • Строковая переменная, в которой мы передаем значение по умолчанию и полу­чаем результат ввода.

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

На рис. вы можете увидеть это окно ввода.

Если окно было закрыто не кнопкой ОК, то происходит выход из процедуры. Об этом говорит фрагмент кода:

Следующая строка кода добавляет новый элемент в наше дерево:

NewNode: =TreeView1. Items.Add(TreeViewl. Selected, CaptionStr);

У компонента Treeviewi есть свойство items, в котором хранятся все элементы дерева. Это свойство имеет объектный тип TTreeNodes. Чтобы добавить туда новый элемент, нужно вызвать метод Add объекта items. Получается, что в объекте Treeview1 есть еще один объект— items, в котором хранятся все элементы. Мы уже сталкивались с такими случаями, когда внутри одного объекта был другой объект. У метода Add есть два параметра:

  • элемент, к которому надо добавить новый (здесь мы передаем выделенный эле­мент Treeview1.Selected);
  • заголовок нового элемента.

Результат выполнения этого метода— указатель на новый элемент. Этот ре­зультат мы сохраняем в переменной NewNode. Теперь можно изменять и другие зна­чения этого элемента. Например, как в следующем коде изменяется картинка:

if NewNode. Par en tonil then NewNode.Imageindex:=1;

Здесь идет проверка, если свойство Parent нашего дерева не равно нулю (т. е. компонент не является верхним в дереве), то изменить значение imageindex соз­данного нами элемента на 1 (по умолчанию это значение 0).

Для события, вызываемого нажатием кнопки Добавить элемент, напишем код, показанный в листинге:

var CaptionStr:String; NewNode:TTreeNode;

if not InputQuery('Ввод имени подэлемента', 'Введите заголовок подэлементаCaptionStr) then exit;

NewNode: =TreeViewl. Items.AddChild(TreeViewl. Selected, CaptionStr) ;

if NewNode. Par en tonil then NewNode.Imageindex: =1 ;

Здесь код практически тот же, что и для кнопки Добавить. Единственная раз­ница в том, что при добавлении нового элемента используется метод Addchiid. От­личие этого метода от просто Add заключается в том, что он добавляет дочерний элемент. Например, если вы выделили в списке какой-то элемент и передали его в качестве первого параметра в Addchiid, то новый элемент будет как бы подчи­няться выделенному. При использовании метода Add новый элемент будет нахо­диться на одном уровне дерева с переданным в качестве первого параметра. Теперь напишем код для кнопки Удалить:

if TreeViewl .Selectedonil then TreeViewl.Items.Delete(TreeViewl.Selected);

Здесь нужно удалить выделенный элемент, поэтому сначала мы проверяем, есть ли вообще выделенный элемент в дереве: if TreeViewl.Selectedonil then

Если такой элемент есть, то выполнится следующий код:

TreeViewl.Items.Delete(TreeViewl.Selected);

Здесь используется метод Delete объекта items, чтобы удалить элемент дерева. В качестве параметра надо передать элемент, который мы хотим удалить (мы пере­даем выделенный TreeViewl.Selected).

Для кнопки Изменить заголовок мы напишем код листинга:

var CaptionStr:String; begin CaptionStr:='';

if not InputQuery('Ввод имени','Введите заголовок элемента',CaptionStr) then exit;

Здесь снова вызывается окно InputQuery, чтобы пользователь смог ввести новое имя для выделенного элемента. Теперь, чтобы изменить имя, надо изменить свой­ство Text ДЛЯ выделенного элемента:

TreeView1 .Selected.Text.

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

procedure TTreeViewForm. FormClose (Sender: TObject; var Action: TCloseAction);

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

ExtractFilePath(Application.ExeName)+'tree.dat'

Application. ExeName — указывает на имя запущенного файла. ExtractFilePath— извлекает путь к файлу из указанного в качестве параметра пути К файлу. Получается, ЧТО ВЫЗОВ ExtractFilePath (Application. ExeName) вернет путь к папке, откуда была запущена программа. Остается только к этому пути при­бавить имя файла (у нас это 'tree.dat') и можно быть уверенным, что файл обяза­тельно будет находиться там же, где и запускаемый файл.

Теперь нужно загрузить сохраненные данные. Для этого по событию Onshow на­пишем следующий код:

procedure TTreeViewForm.FormShow(Sender: TObject);

begin if FileExists(ExtractFilePath(Application.ExeName) +1 tree.dat') then TreeViewl.LoadFromFile(ExtractFilePath(Application.ExeName)+ 'tree.dat');

Здесь сначала проверяется с помощью вызова функции FileExists существова­ние файла. Этой функции нужно передать полное имя файла, и если он существует, то функция вернет true, иначе false.

Если файл существует, то можно его загрузить с помощью вызова метода LoadFromFile.

Обязательно проверяйте файл на существование. Если его нет или кто-то его удалил, а вы попытаетесь загрузить данные из несуществующего файла, произой­дет ошибка.

Попробуйте запустить пример. Если создать несколько элементов и потом переза­пустить программу, то вы сразу же сможете заметить, что старые дочерние элементы имеют один рисунок, а вновь созданные другой. Почему так получилось? Ведь мы же всегда меняем рисунок, если это дочерний элемент? Просто после закрытия про­граммы дерево сохраняется в файле, а после загрузки оно загружается без учета изо­бражений. Состояние картинок не сохраняется — об этом нам нужно заботиться са­мостоятельно. Но это уже отдельная история, о которой мы поговорим в другой раз. В следующем примере мы избавимся от этого неприятного эффекта.

Итак, чтобы восстановить состояние картинок после загрузки данных из файла, надо скорректировать обработчик события Onshow следующим образом:

procedure TTreeViewForm.FormShow(Sender: TObject); var i:Integer;

if FileExists(ExtractFilePath(Application.ExeName)+'tree.dat') then

TreeViewl. LoadFromFile (ExtractFilePath (Application. ExeName) + 'tree.dat');

for i:=0 to TreeViewl.Iterns.Count-1 do

if TreeViewl.Items.Parent=nil then TreeViewl.Iterns.Imageindex:= 0 else TreeViewl.Iterns.Imageindex:=1;

Здесь сначала происходит загрузка данных из файла, а потом запускается цикл, в котором перебираются все элементы дерева. В цикле мы делаем проверку, если у текущего элемента свойство Parent равно nil, значит, у элемента нет родитель­ского элемента и он корневой, поэтому устанавливаем нулевую картинку. Если свойство не равно нулю, то элемент дочерний, и мы устанавливаем ему картинку с номером 1.

Вот и все. Вот таким простым способом мы восстанавливаем картинки элемен­тов, потому что они не сохраняются в файле.

Читайте также: