Аннотация
На примере создания справочника товаров в бухгалтерской системе управления базой данных, рассматривается ряд механизмов взаимодействия объекта «дерево элементов» с таблицей баз данных.
При создании системы управления базой данных, возникает необходимость в отдельной таблице, хранящей данные о товарах или материалах используемых предприятием. Благодаря такой таблице на предприятии есть возможность вести аналитический учёт, то есть всегда можно узнать фактическое количество определённого товара на складах, можно сформировать заказ для закупки недостающих товаров, или использовать эту информацию в других производственных целях.
Компонент – дерево элементов (TreeVeiw), достаточно сложный, но мощный и удобный способ работы с базой данных.
Прежде всего следует обратить внимание на то, что в данном случае речь идёт не просто о дереве элементов, а об объединённом объекте, состоящим из дерева элементов и таблицы с базой данных.
В основе создания такого объекта стоит задача объединения базы данных товаров с объектом дерево элементов. Получив в своё распоряжение такой объект, пользователь сможет сохранять однотипные товары по группам. Такое расположение товаров, даст возможность быстро и удобно получать доступ к необходимому товару и осуществлять поиск.
В данной работе на примере экспериментальной СУБД «Склад-Магазин» (созданной на базе языка DELPHI) представлена методика добавления нового элемента в компонент «дерево элементов» TreeView.
Внешний вид такого справочника представлен на Рис.1 В нижней части этой формы FormTree расположены связанные таблицы, хранящие данные группах и товарах. В главной таблице слева, хранятся данные о группах, а в подчинённой таблице справа - данные о товарах. Для наглядности эти таблицы пока отображаются на форме FormTree, но уже в окончательном варианте, не возникает необходимости отображения таблиц, поэтому они будут невидимы и не доступны для пользователя. Полученное дерево есть не что иное как, удобный и наглядный путеводитель по таблице товаров.
Рис. 1 Форма справочника товаров FormTreeс использованием компонента «дерево элементов» (TreeView)
Из рисунка 1 видно, что в базе данных созданы разделы товаров бытовой техники. В группе «Телевизоры» активизирована подгруппа производителя телевизоров «Sony». На правой панельке выделена необходимая марка товара. Так можно получить доступ к любому товару в базе данных.
Итак рассмотрим в каким образом можно добавить новый каталог в дерево.
Для добавления нового каталога создаётся дополнительная форма FormTreeAddFolder с кнопкой добавления названия каталога (Рис.2). По нажатию кнопки ОК выполняется процедура TFormTreeAddFolder.ButtonOKClick(Sender: TObject), программные коды которой, представленная ниже. Разберем более детально алгоритм этой процедуры.
Рис. 2 Форма добавления нового каталога FormTreeAddFolder
В разделе объявления переменных, объявлены переменные NewNode и EditNode типа TTreeNode это тип отдельного элемента дерева.
procedure TFormTreeAddFolder.ButtonOKClick(Sender: TObject);
var NewNode, EditNode: TTreeNode;
i: Integer;
begin
//В начале процедуры меняем внешний вид курсора на вид означающий что система занята и находится в процессе
Screen.Cursor:=crHourGlass;
//Удалим пустые пробелы в начале и в конце строки
Edit1.Text:=Trim(Edit1.Text);
В следующей строке процедуры проверяется значение глобальной булевой переменной EditFolder. Данная переменная хранит в себе информацию, о том какие действия хочет предпринять пользователь, запустив форму добавления каталога. Либо эта форма открыта для добавления нового узла, или же для редактирования выделенного узла. От этого будет зависеть алгоритм всей процедуры. Переменную EditFolder необходимо объявить глобальной, то есть в заголовке модуля, за пределами рассматриваемой процедуры, так как во время работы приложения, пользователь ещё до запуска формы добавления каталога FormTreeAddFolder, определяет какое ему необходимо выполнить действие, редактировать имеющийся узел или добавить новый.
if EditFolder=false then
begin //First
Итак, если текущий узел выделен не для редактирования, а для добавления нового узла-потомка, тогда будет выполняться последовательность операторов находящихся в операторных скобках между begin .. end.
Методом BeginUpdate, свойства Items объекта TreeView1 выключаем разрешение обновление дерева, до тех пор пока не будет вызван метод EndUpdate. Эти методы вызваны для того чтобы не прорисовывать дерево после каждого изменения, ведь изменений будет немало, и всё это значительно замедлит работу процедуры.
FormTree.TreeView1.Items.BeginUpdate;
Следующий цикл предназначен для проверки у выделенного узла (FormTree.TreeView1.Selected) списока всех его дочерних узлов, на идентичность их имён (FormTree.TreeView1.Selected.Item[i].Text) с именем нового вводимого дочернего узла (FormTreeAddFolder.Edit1.Text).
Ведь если допустить что в одной группе товаров, будет несколько подгрупп с одинаковыми именами, пользователь не сможет полноценно работать. Можно постоянно путаться , в какой подгруппе находится тот или иной товар?
for i:=0 to FormTree.TreeView1.Selected.Count-1 do
if
FormTree.TreeView1.Selected.Item[i].Text=
FormTreeAddFolder.Edit1.Text then
//Если название i-го каталога совпадает с введённым именем, тогда следует список операторов выполняющих вывод сообщения, и корректный выход из процедуры без добавления нового узла.
begin
//Вывод сообщения
MessageDlg('Каталог с таким названием уже существует !', mtWarning,[mbOk], 0);
//Включаем режим при котором разрешено обновление дерева
FormTree.TreeView1.Items.EndUpdate;
//Возвращаем курсор в состояние когда система не занята
Screen.Cursor:=crDefault;
//Прерываем выполнение процедуры
exit;
//Конец цикла проверки на идентичность имени
end;
//Если же выхода из процедуры не произойдет, то есть имя добавляемого узла не совпадёт с дочерними узлами, тогда выполниться метод AddChild добавления нового узла-потомка в выделенный узел дерева FormTree.TreeView1.Selected с именем из FormTreeAddFolder.Edit1.Text
NewNode:=
FormTree.TreeView1.Items.AddChild(FormTree.TreeView1.Selected,FormTreeAddFolder.Edit1.Text);
//Сортируем полученные подкаталоги в алфавитном порядке
FormTree.TreeView1.AlphaSort;
Следует обратить внимание на ту деталь, что объект TreeView выполняет сортировку в алфавитном порядке с учётом статуса узла, то есть все дочерние узлы одного уровня сортируются только в пределах этого уровня относительно узла родителя. Все родительские узлы одного уровня сортируются соответственно только в пределах своего уровня. Так устроен объект TreeView, а иначе не было бы смысла создавать такой объект, ведь после одной глобальной сортировки в дереве узлов образовался бы хаос. Дочерние узлы могли бы попасть на место родительских и наоборот.
Все данные отображаемые в дереве будут храниться в таблице баз данных ADOTableList (в модуле DataModule1), а таблица баз данных не умеет сортировать данные по уровням, как это выполняет объект TreeView. Чтобы порядок расположения узлов в базе данных можно было бы сохранить таким же как и в дереве, предлагается использовать свойство AbsoluteIndex объекта TreeView.
Дереве узлов TreeView имеет свойство Items типа TTreeNodes. В свою очередь TreeNodes имеет свойство Item типа TTreeNode у которого есть свойство AbsoluteIndex. Значение этого свойство есть не что иное как порядковый номер узла дерева, относительно первого узла в дереве. Проще говоря каждый узел добавленный в дерево имеет свой порядковый номер, вне зависимости какой это узел родительский или дочерний. На рисунке 3 показан пример узлов в дереве, справа на рисунке подписаны порядковые номера или абсолютные индексы узлов.
Благодаря этим индексам, можно будет сохранять последовательность расположения узлов в таблице ADOTableList (в модуле DataModule1), в таком же порядке как они расположены в дереве узлов.
После добавления нового элемента в дерево, естественно число абсолютных индексов увеличится на единицу, причём часть узлов поменяют значения своих индексов. Если предположить например, что в дерево из 20-ти узлов добавить новый элемент между 9-ым и 10-ым, тогда в дереве станет 21 узел. Элемент со значением абсолютного индекса 10 и все последующие узлы увеличат значения своих абсолютных индексов на единицу, а новый узел сможет занять место после узла с индексом 9 и присвоить своему абсолютному индексу значение 10. В дереве это всё выполняется автоматически, а вот в базе данных нужно проделать вручную.
Рис.3 Пример дерева элементов и абсолютных индексов его узлов
Для определения абсолютного индекса только что добавленного узла, выполним проход по всем дочерним узлам выделенного узла родителя. Как только по названию будет найден добавленный узел, значение его абсолютного индекса будет присвоено глобальной переменной Ci.
for i:=0 to FormTree.TreeView1.Selected.Count-1 do
begin
if FormTree.TreeView1.Selected.Item[i].Text=FormTreeAddFolder.Edit1.Text then
Ci:=FormTree.TreeView1.Selected.Item[i].AbsoluteIndex;
end;
//Конец списка команд и циклов добавления нового подкаталога
В дерево элементов узел добавлен, остаётся теперь добавить информацию об узле в таблицу базы данных ADOTableList. В первую очередь для корректного сохранения данных в таблицу рекомендуется выполнить обновление таблицы:
//Обновление базы данных
DataModule1.ADOTableList.Close;
DataModule1.ADOTableList.Open;
Затем ввод данных в таблицу предлагается выполнить по алгоритму показанному на рисунке 4.
Рис.4 Алгоритм ввода данных об узлах дерева в таблицу баз данных
В следующем цикле в таблице узлов ADOTableList в поле индексов выполним увеличение на единицу всех абсолютных индексов, значения которых равно значению абсолютного индекса добавленного узла Ci и выше.
//Начало цикла обновления поля индексов для таблицы списка каталогов
DataModule1.ADOTableList.First;
for i:=0 to DataModule1.ADOTableList.RecordCount-1 do
begin
if DataModule1.ADOTableListABSIndex.AsInteger>=Ci then
begin
DataModule1.ADOTableList.Edit;
DataModule1.ADOTableListABSIndex.AsInteger:=
DataModule1.ADOTableListABSIndex.AsInteger+1;
end;
DataModule1.ADOTableList.Next;
end;
//Конец цикла обновления поля индексов для таблицы списка каталогов
Второй этап алгоритма – добавление данных о названии узла в поле названий и значений абсолютного индекса узла в поле абсолютных индексов таблицы ADOTableList.
//Обновление таблицы каталогов
DataModule1.ADOTableList.Close;
DataModule1.ADOTableList.Open;
//Добавление новой записи в таблицу каталогов
DataModule1.ADOTableList.Insert;
//Ввод названия узла
DataModule1.ADOTableListDSDesigner.AsString:=FormTreeAddFolder.Edit1.Text;
//Ввод значения индекса
DataModule1.ADOTableListABSIndex.AsInteger:=Ci;
//Сохранение внесённых в таблицу изменений
DataModule1.ADOTableList.Post;
//Сохранение данных о структуре дерева во внешний файл 'tree.dat'
FormTree.TreeView1.SaveToFile(ExtractFilePath(Application.ExeName)+'tree.dat');
Третий этап – сортировка каталогов в таблице ADOTableList по полю абсолютных индексов в порядке возрастания
DataModule1.ADOTableList.Sort := 'ABSIndex ASC';
//Обновление данных в таблице, необходимо в целях правильного сохранения данных
DataModule1.ADOTableList.Close;
DataModule1.ADOTableList.Open;
//Активизируем запись в таблице каталогов ADOTableList, которой соответствует выделенный каталог в дереве каталогов
DataModule1.ADOTableList.Locate('ABSIndex', FormTree.TreeView1.Selected.AbsoluteIndex,[loCaseInsensitive]);
//Выключаем режим при котором запрещено прорисовка обновлений в дереве элементов
FormTree.TreeView1.Items.EndUpdate;
//Возвращаем вид курсора в стандартный
Screen.Cursor:=crDefault;
end // Закрываются операторные скобки, которые были открыты в начале процедуры, при проверке булевой глобальной переменной EditFolder
else //Если узел выделен для редактирования тогда выполняется следующий список операторов находящихся в операторных скобках begin … end. Алгоритм редактирования узла имеет свои особенности и требует отдельного рассмотрения в последующих статьях.
begin
. . . //Редактирование узла (каталога товаров)
end;
Обновляем все данные на форме справочника товаров процедурой обновления, которая запускается нажатием кнопки обновления
FormTree.ToolButton1Click(Sender);
end; //Конец процедуры
Таким образом мы рассмотрели как компонент «дерево элементов» можно объединить с таблицей базы данных, для хранения в этой таблице данных об узлах дерева. Ведь таблица обладает гораздо большими возможностями и надёжностью для хранения данных. Также предложен новый механизм взаимодействия дерева элементов и таблицы при добавлении новых узлов в дерево элементов.
Литература:
1. Владимир Гофман, Анатолий Хомоненко «Delphi быстрый старт» Санкт-Петербург «БХВ-Петербург» 2003
2. Михаил Фленов «Библия для программиста в среде DELPHI» Copyright 2002 г.
3. Михаил Фленов «Программирование в Delphi глазами хаккера» Санкт-Петербург «БХВ-Петербург» 2003
4. Михаил Краснов «Open GL графика в проектах DELPHI» Дюссельдорф, Киев, Москва, Санкт-Петербург 2000
5. Петр Дарахвелидзе, Евгений Марков «Программирование в DELPHI7» Санкт-Петербург «БХВ-Петербург» 2003
6. Справочная система среды программирования BORLAND DELPHI 7