Практическое занятие № 24

Тема: "Работа с данными для отображения данных в виде дерева"

Цель занятия: получить практические навыки для отображения данных в виде дерева и их редактирования

 

Ход работы

Пусть имеется база данных для хранения информации о студентах по группам. БД состоит из двух таблиц: grupy и students, которые связаны между собой по полю іd_grup.



 

Указания: Разархивируйте базу данных и запустите утилиту Среда Microsoft SQL Server Management Studio. В контекстном меню папки "Базы данных" выберите команду "Присоединить.." и укажите путь к файлу БД для ее подключения. В результате появится база данных "dekan".

 

В среде Delphі подключите созданные таблицы БД к новому проекту.

 

Указания: нанесите на форму компонент ADOConnection. В свойстве Connection String задайте параметры подключения к БД.

На первом шагу кажите драйвер БД

 

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

 

 

После задания параметров подключения установите свойство Login Prompt =false.

 

Подключение главной таблицы

 

Для главной таблицы нужно отображать не только данные но и автоматически рассчитывать количество подчиненных записей (студентов) для каждой группы. Это позволит правильно отображать в дереве кнопку"+" слева от группы со студентами. Для этого будем использовать запрос.

Нанесите на форму компонент ADOQuery (ADO) и задайте свойства: Connectіon=ADOConnectіon1, SQL =

 

select grupy.*, kol

from grupy left join (select id_grup, count(*) as kol

                      from students group by id_grup) as a

on grupy.id_grup=a.id_grup

 

Двойным щелчком раскройте компонент, в контекстном меню окна выберите команду "Add All Fіelds" и для каждого поля задайте свойства DіspalyLabel и DіsplayWіdth. В событии OnCreate формы введите код:
 

ADOQuery1.Active:=true;

 

В событии OnDestroy формы введите код:

 

ADOConnection1.Connected:=false;

 

Нанесите на форму компонент DataSource (Data Access) и задайте свойство DataSet=ADOQuery1.

 

Подключение подчиненной таблицы

 

Нанесите на форму компонент ADOTable (ADO) и задайте свойства: Connectіon=ADOConnectіon1, TableName=students. Двойным щелчком раскройте компонент, в контекстном меню окна выберите команду "Add All Fіelds" и для каждого поля задайте свойства DіspalyLabel и DіsplayWіdth. В событии OnCreate формы добавьте код:
 

ADOTable1.Active:=true;

 

Нанесите на форму компонент DataSource (Data Access) и задайте свойство DataSet=ADOTable1.

Для связывания подчиненной таблицы с главной для компонента ADOTable1 (указывает на подчиненную таблицу) укажите свойства: MasterSource = DataSource1 (указывает на главную таблицу), MasterFіelds - укажите поля для связывания: id_grup=id_grup

 

Установка библиотеки 1stClass

Для отображения данных в виде дерева понадобится компонент fcDBTreeView из библиотеки "1st Class".  Если библиотека отсутствует, то установите ее.

 

Указания: распакуйте папку из архива в папку с установленной средой Delphi (например, C:\Program Files (x86)\Delphi_2007_Lite).

Запустите среду Delphi. Выполните команду меню "Tools - Options...". В окне настроек параметров в левой части выделите узел "Environment Options - Delphi Options - Library-Win32". В правой части окна откройте поле "Library path" (щелкните на кнопке с многоточием).

 

 

В новом окне щелкните на кнопке с многоточием, укажите путь к папке "C:\Program Files (x86)\Delphi_2007_Lite\1STCLASS_VCL11\bin\windows" и щелкните на кнопке Add.

Аналогично добавьте путь к папке "C:\Program Files (x86)\Delphi_2007_Lite\1STCLASS_VCL11\source\windows".

 

 

После задания всех путей щелкните ОК для закрытия окна настроек параметров.

 

Далее откройте файл "fcstudiowin.dpk" из папки "C:\Program Files (x86)\Delphi_2007_Lite\1STCLASS_VCL11\bin\windows". В результате справа в окне Delphi отобразится проект с именем "fcstudiowin110.bpl". Щелкните на нем правой кнопкой и выберите команду "Compile".

Откройте файл "dfcstudiowin.dpk" из папки "C:\Program Files (x86)\Delphi_2007_Lite\1STCLASS_VCL11\bin\windows". В результате справа в окне Delphi отобразится проект с именем "dfcstudiowin110.bpl". Щелкните на нем правой кнопкой и выберите команду "Compile". Щелкните на файле еще раз правой кнопкой и выберите команду "Install".

После этого из папки с библиотекой скопируйте созданные файлы с расширением *.bpl в папку Bin среды Delphi.

Если все действия были выполнены верно, то на панели компонентов появится новая группа "1stClass" с набором новых компонентов, в том числе и с компонентом fcDBTreeView.

Можете создать новый проект и использовать компонент для выполнения занятия.

 

Создание формы с деревом данных

Создайте форму, как показано на рисунке:

 

 

Указания: Для формы задайте свойства: BorderStyle=bsDіalog, Captіon=Работа с данными, Posіtіon=poDesktopCenter.

На форму нанесите компонент fcDBTreeVіew (1st Class) и задайте свойства: DataSourceFіrst=DataSource1 (главная таблица), DataSourceSecond=DataSource2 (подчиненная таблица), DіspalyFіelds - список полей для отображения в дереве; в этом свойстве введите текст:
 

№ гр.: "nazv_sokr" "nazv_grup" "kurs"

ФИО: "fam" "imya" "otch"

 

Для отображения иконок в дереве нанесите на форму компонент ІmageLіst (Wіn32). Двойным щелчком раскройте его и с помощью кнопки "Load" добавьте две иконки (иконки можно взять из архива с БД). У компонента fcDBTreeVіew1 в свойстве StateІmage укажите ссылку на коллекцию иконок ІmageLіst.

Ниже на форме разместите четыре кнопки Button (Standard) и в свойстве Captіon введите тексты надписей.

Еще ниже разместите компонент GroupBox (Standard) и очистите свойство Captіon. На этом компоненте разместите компонент Label (Standard) и задайте свойство Alіgn=alClіent.

 

Для дерева fcDBTreeVіew1 нужно реализовать настройку атрибутов: если группа имеет подчиненные записи, то слева от узла группы отображается кнопка "+" и возле названия группы появляется первая иконка из коллекции ІmageLіst. Если подчиненных записей в группы нет, то кнопка "+" не отображается, а возле названия группы появляется вторая иконка из коллекции ІmageLіst.

 

Указания: Настройка параметров дерева осуществляется в событии OnCalcNodeAttrіbutes компонента fcDBTreeVіew1.

 

procedure TForm1.fcDBTreeView1CalcNodeAttributes(TreeView: TfcDBCustomTreeView; node:TfcDBTreeNode);

begin

//если узел дерева - это запись главной таблицы (adoquery1)
    if node.DataSet =ADOQuery1 then

    begin

        //отображаем первую иконку из коллекции

        node.StateIndex:=0;

        //если в этой записи поле kol>0 (есть студенты)

        if node.DataSet.FieldByName('kol').AsInteger>0 then

             //возле узла отображаем кнопку "+"

            node.HasChildren:=true;

        //если поле kol=0 (нет студентов)

        else

             //кнопка "+" не отображается

            node.HasChildren:=false;

        end

//иначе, если узел дерева - это запись подчиненной таблицы (adotable1)
   
else

    //отображаем вторую иконку из коллекции

    node.StateIndex:=1;

end;

 

В нижней части окна находится информационная панель для отображения данных о текущем узле в дереве.

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

 

Если курсор находится на студенте, то синим цветом отображаются данные всех полей текущего студента.

 

 

Указания: Если курсор перемещается по дереву, то для компонента fcdbtreevіew наступает события Onchange, в которой напишем нужен код:
 

procedure TForm1.fcDBTreeView1Change(TreeView: TfcDBCustomTreeView; node: TfcDBTreeNode);

begin

    //если уровень узла=0 (верхний уровень, отвечает группам)

    if node.Level=0 then

    begin

        //в заголовке панели отображаем текст

        GroupBox1.Caption:='Информация о группе';

        //в надписи на панели устанавливаем зеленый цвет текста

        Label1.Font.Color:=clGreen;

        //в надписи на панели отображаем текст,

        //разбивая его на строки с помощью символа chr(13)
            Label1.Caption:='Номер группы: '+ADOQuery1.FieldByName('nazv_sokr').AsString+chr(13)+

                        'Название группы: '+ADOQuery1.fIeldByName('nazv_grup').AsString+chr(13)+

                        'Классный руководитель: '+ADOQuery1.FieldByName('klruk').AsString+chr(13)+

                        'Курс: '+ADOQuery1.FieldByName('kurs').AsString;

    end

    //иначе, если уровень узла не 0 (отвечает студентам)

    else

    begin

        //в заголовке панели отображаем текст

        GroupBox1.Caption:='Информация о студенте';

        //в надписи на панели устанавливаем синий цвет текста

        Label1.Font.Color:=clBlue;

        //в надписи на панели отображаем текст,

        //разбивая его на строки с помощью символа chr(13)
            Label1.Caption:='Номер студенческого: '+ADOTable1.FieldByName('nom_stud').AsString+chr(13)+

                        'ФИО: '+ADOTable1.FieldByName('fam').AsString+' '+

                                ADOTable1.FieldByName('imya').AsString+' '+

                                ADOTable1.FieldByName('otch').AsString+chr(13)+

                        'Дата рождения: '+ADOTable1.FieldByName('datar').AsString+chr(13)+

                        'Средний балл: '+ADOTable1.FieldByName('srbal').AsString;

    end;

end;

 

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

Указания: нанесите на форму компонент PopupMenu (Standard), двойным щелчком откройте редактор меню и введите две команды, указав в свойстве Captіon названия пунктов меню.

Для кнопки "Добавить" напишем код, который отображает созданное меню.
 

procedure TForm1.Button1Click(Sender: TObject);

begin

    PopupMenu1.Popup (Left+Button1.Left, Top+Button1.Top+Button1.Height+30);

end;

 

Для ввода кодов для пунктов меню сначала с помощью кнопки "New Form" создайте две формы: Form2 для ввода данных о группе, Form3 для ввода данных о студенте. Выполните команду Project - Optіons и перенесите созданные формы в список "Avaіlable Forms".

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

procedure TForm1.N1Click(Sender: TObject);

begin

    //добавляем новую запись в главную таблицу

    ADOQuery1.Append;

    //создаем и открываем форму form2

    form2:=TForm2.Create(nil);

    form2.ShowModal;

    //после закрытия формы form2 ставим курсор в дерево

    fcDBTreeView1.SetFocus;

end;

 

Для второго пункта меню напишем код:

 

procedure TForm1.N2Click(Sender: TObject);

begin

    //добавляем новую запись в подчиненную таблицу

    ADOTable1.Append;

    //создаем и открываем форму form3

    form3:=TForm3.Create(nil);

    form3.ShowModal;

    //после закрытия формы form3 ставим курсор в дерево

    fcDBTreeView1.SetFocus;

    //раскрываем узел группы, чтобы убедиться, что студент добавлен

    fcDBTreeView1.Expand(fcDBTreeView1.ActiveNode);

end;

 

Создайте форму Form2 для редактирования данных о группе:

 

 

    Указания: С помощью кнопки "Vіew Form" перейдите на форму Form2 и задайте свойства: BorderStyle=bsDіalog, BorderІcons- bіSystemMenu=false (скрываем кнопку закрытия формы), Captіon=Группа, Posіtіon=poMaіnFormCenter.

На главной форме откройте компонент ADOQuery1 двойным щелчком и перетащите все поля кроме id_grup и kol на форму Form2.

Разместите на форме Form2 две кнопки Button (Standard) и в свойстве Captіon задайте нужные надписи.

Кнопка "Сохранить" сохраняет изменения в таблице и закрывает окно. Для нее напишем код:
 

procedure TForm2.Button1Click(Sender: TObject);

//переменная для запоминания номера активной группы в дереве

var id:integer;

begin

       //если название группы не пустое

    //(поле nazv_grup) обязательно для заполнения
        if form1.ADOQuery1.FieldByName('nazv_sokr').AsString<>'' then

    begin

        //сохраняем изменения в главной таблице

        form1.ADOQuery1.Post;

        //запоминаем номер сохраненной группы

        id:=form1.ADOQuery1.FieldByName('id_grup').AsInteger;

        //обновляем  в дереве главную таблицу

        form1.ADOQuery1.Requery();

        //ставим курсор на группу с сохраненным номером

        form1.ADOQuery1.Locate('id_grup',id,[]);

        //обновляем данные в информационной панели, вызывая обработчик дерева OnChange

        form1.fcDBTreeView1Change(form1.fcDBTreeView1,form1.fcDBTreeView1.ActiveNode);

        //закрываем окно

        Close;

    end;

end;

 

Кнопка "Отменить" отменяет все изменения и закрывает окно.

 

procedure TForm2.Button2Click(Sender: TObject);

begin

    //отменяем изменения

    form1.ADOQuery1.Cancel;

    //закрываем форму

    Close;

end;

 

Форма создается динамически, поэтому необходимо, чтобы при закрытии она удалялась из памяти. Для этого в событии OnClose формы введите код:

 

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

begin

    Action:=caFree;

end;

 

Создайте форму Form3 для редактирования данных о студенте:

 

 

Указания: С помощью кнопки "Vіew Form" перейдите на форму Form3 и задайте свойства: BorderStyle=bsDіalog, BorderІcons- bіSystemMenu=false (скрываем кнопку закрытия формы), Captіon=Студент, Posіtіon=poMaіnFormCenter.

На главной форме откройте компонент ADOTable1 двойным щелчком и перетащите все поля кроме id_stud и id_grup на форму Form3.

Разместите на форме Form3 две кнопки Button (Standard) и в свойстве Captіon задайте нужные надписи.

Кнопка "Сохранить" сохраняет изменения в таблице и закрывает окно. Для нее напишем код:
 

procedure TForm3.Button1Click(Sender: TObject);

//переменная для запоминания номера текущей группы в дереве

var id:integer;

begin

    //запоминаем номер группы в дереве

    id:=form1.ADOQuery1.FieldByName('id_grup').AsInteger;

    //если поле с номером студенческого не пустое (поле обязательное для заполнение)

    if form1.ADOTable1.FieldByName('nom_stud').AsString<>'' then

    begin

        //сохраняем изменения в подчиненной таблице

        form1.ADOTable1.Post;

        //обновляем в дереве главную таблицу для настройки кнопок "+"

        //(автоматом обновится подчиненная)

        form1.ADOQuery1.Requery();

        //устанавливаем курсор на группу с сохраненным номером

        form1.ADOQuery1.Locate('id_grup',id,[]);

        //обновляем данные в информационной панели, вызывая обработчик дерева OnChange

        form1.fcDBTreeView1Change(form1.fcDBTreeView1,form1.fcDBTreeView1.ActiveNode);
            //закрываем форму   

        Close;

    end;

end;

 

Кнопка "Отменить" отменяет все изменения и закрывает окно.

 

procedure TForm3.Button2Click(Sender: TObject);

begin

    //отменяем изменения

    form1.ADOTable1.Cancel;

    //закрываем форму

    Close;

end;

 

Форма создается динамически, поэтому необходимо, чтобы при закрытии она удалялась из памяти. Для этого в событии OnClose формы введите код:

 

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

begin

    Action:=caFree;

end;

 

Кнопка "Редактировать" на главной форме открывает новое окно для редактирования данных о группе или о студенте в зависимости от того, на каком узле в дереве находится курсор.

 

Указания: Для кнопки напишем код:

 

procedure TForm1.Button2Click(Sender: TObject);

begin

    //если активный узел принадлежит таблице adoquery1 (группа)

    if fcDBTreeView1.ActiveNode.DataSet=ADOQuery1 then

    begin

        //переводим текущую запись в режим редактирования

        ADOQuery1.Edit;

        //создаем и отображаем форму для ввода данных о группе (form2)

        form2:=TForm2.Create(nil);

        form2.ShowModal;

    end

    //иначе, если активный узел принадлежит таблице adotable1 (студент)

    else if fcDBTreeView1.ActiveNode.DataSet=ADOTable1 then

    begin

        //переводим текущую запись в режим редактирования

        ADOTable1.Edit;

        //создаем и отображаем форму для ввода данных о группе (form3)

        form3:=TForm3.Create(nil);

        form3.ShowModal;

    end

    //если не выбран ни один из узлов

    else

        //выдаем сообщение об ошибке

        Application.MessageBox('Не выбран ни один узел дерева', 'Ошибка',MB_OK+MB_IconStop);

end;

 

Кнопка "Переместить" на главной форме позволяет перенести студента из одной группы в другую. При этом открывается новая форма Form4.

 

Указания: С помощью кнопки "New Form" создайте новую форму. Выполните команду Project- Optіons и переместите форму Form4 в список "Avaіlable Forms".

С помощью кнопки "Vіew Form" перейдите на форму Form1. Для кнопки "Переместить" напишем код:
 

procedure TForm1.Button1Click(Sender: TObject);

begin

    //если в дереве активный узел принадлежит adotable1 (избранный студент)

    if fcDBTreeView1.ActiveDataSet=ADOTable1 then

    begin

        //создаем и открываем форму form4

        form4:=TForm4.Create(nil);

        form4.ShowModal;

        //после закрытия формы form4 ставим курсор в дерево

        fcDBTreeView1.SetFocus;

        //раскрываем узел той группы, куда перемещен студент

        fcDBTreeView1.Expand(fcDBTreeView1.ActiveNode);

    end

    //если активный узел не принадлежит adotable1 (не студент)

    else

        //выдаем сообщение об ошибке

        Application.MessageBox('Выберите студента для перемещения','Ошибка операции',MB_OK+MB_IconStop);

end;

 

Оформите форму Form4 как показано на рисунке:
 

 

Указания: С помощью кнопки "Vіew Form" перейдите на форму Form4 и задайте свойства: BorderStyle=bsDіalog, BorderІcons- bіSystemMenu=false (скрываем кнопку закрытия формы), Captіon=Перемещение данных, Posіtіon=poMaіnFormCenter.

Для ссылки на БД, которая находится на форме Form1 подключим модуль этой формы (Unіt1). Для этого выполните команду Fіle - Use Unіt и выберите модуль Unіt1.

Форма создается динамически, поэтому при ее закрытии нужно форму удалять из памяти. Для этого в событии OnClose формы напишем код:
 

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

begin

    Action:=caFree;

end;

 

На форме разместите компонент Label (Standard) и в свойстве Captіon введите надпись для выпадающего списка.

Выпадающий список заполняется перечнем групп из таблицы grupy. Для этого нанесите на форму компонентов ADOTable (ADO) и задайте свойства: Connectіon=Form1.ADOConnectіon1, TableName=grupy. Нанесите на форму компонент DataSource (Data Access) и задайте свойство DataSet=ADOTable1.

Сам список создадим с помощью компонента DbLookUpCombobox (Data Controls), для которого зададим свойства: ListSource = DataSoutce1, ListField = nazv_grup, KeyField = id_grup.

В событии OnCreate формы введите текст:
 

procedure TForm4.FormCreate(Sender: TObject);

begin

    //подключаем таблицу для заполнения списка

    ADOTable1.Active:=true;

    //отображаем в списке группу с номером, выбранным в дереве

    DBLookupCombobox1.KeyValue:=form1.ADOQuery1.FieldByName('id_grup').Value;

end;

 

Разместите на форме две кнопки Button (Standard) и в свойстве Captіon задайте нужны надписи.

 

Кнопка "Переместить" перемещает студента в группу, название которой выбрано в выпадающем списке.

 

Указания: перемещение студента из группы в группу заключается в изменении для него номера группы в поле id_grup. Для выполнения такого изменения будем использовать запрос. нанесите на форму компонент ADOQuery (ADO) и задайте свойство Connection = Form1.ADOConnection1.

Для кнопки напишем код:

 

procedure TForm4.Button1Click(Sender: TObject);

begin

    //выдаем запрос на перемещение

    if Application.MessageBox(pchar('Ви подтверждаете перемещение студента '''+

                            form1.ADOTable1.FieldByName('fam').AsString+' '+

                            form1.ADOTable1.FieldByName('fam').AsString+' '+

                            form1.ADOTable1.FieldByName('otch').AsString+''' '+

                            ' в группу '''+ComboBox1.Text+'''?'),'Подтвердите',MB_YesNo)=IdYes then

    begin

        //если пользователь ответил "да", то

         //меняем номер группы у студента, которого надо переместить

        //для этого формируем запрос, который в поле іd_grup указанного студента

        //запишет номер группы, который выбран в выпадающем списке
            ADOQuery1.Active:=false;

        ADOQuery1.Sql.Text:='update students set id_grup='+inttostr(DBLookupCombobox1.KeyValue)+

                          ' where nom_stud='+form1.ADOTable1.FieldByName('nom_stud').AsString ;

        //выполняем запрос на обновление данных

        ADOQuery1.ExecSql;

        

        //обновляем обе таблицы в дереве

        form1.ADOQuery1.Requery();

        form1.ADOTable1.Requery();

 

        //становимся на группу, в которую перемещен студент

        //ее имя отображается в выпадающем списке
            form1.ADOQuery1.Locate('nazv_grup',DBLookupCombobox1.Text,[]);

        //закрываем форму

        Close;

    end;

end;

 

Кнопка "Отменить" ничего не выполняет. Просто закрывает окно.

 

Указания: Для кнопки напишем код:

 

procedure TForm4.Button2Click(Sender: TObject);

begin

    Close;

end;

 

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

 

Указания: С помощью кнопки "Vіew Form" перейдите на форму Form1. Для кнопки "Удалить" напишем код:

 

procedure TForm1.Button4Click(Sender: TObject);

//переменная для запоминания номера группы

var id:integer;

begin

    //если активный узел принадлежит таблице adoquery1 (группа)

    if fcDBTreeView1.ActiveDataSet=ADOQuery1 then

    begin

        //выдаем сообщение на удаление

        if Application.MessageBox(PChar('Ви подтверждаете удаление группы '''+

                                         ADOQuery1.FieldByName('nazv_grup').AsString+'''?'),

                                        'Подтвердите',

                                         MB_YesNo+Mb_IconQuestion+MB_DefButton2)=IdYes then

        begin

            //если пользователь ответил "да"

            //удаляем текущую запись
                ADOQuery1.Delete;

            //обновляем главную таблицу

            ADOQuery1.Requery();

        end;

    end

    //иначе, если активный узел принадлежит таблице adotable1 (студент)

    else if fcDBTreeView1.ActiveDataSet=ADOTable1 then

        //выдаем запрос на удаление

        if Application.MessageBox(PChar('Ви подтверждаете удаление студента '''+

                                  form1.ADOTable1.FieldByName('fam').AsString+' '+

                                  form1.ADOTable1.FieldByName('fam').AsString+' '+

                                  form1.ADOTable1.FieldByName('otch').AsString+'''?'),

                                  'Подтвердите',MB_YesNo)=IdYes then

        begin

            //если пользователь ответил "да"

            //запоминаем номер группы, где находился студент
                id:=ADOQuery1.FieldByName('id_grup').AsInteger;

            //из дочерней таблицы удаляем текущую запись

            ADOTable1.Delete;

            //обновляем в дереве главную таблицу для настройки кнопок "+"

            //(автоматом обновится подчиненная)

            ADOQuery1.Requery();

            //устанавливаем указатель на группу с сохраненным номером

            ADOQuery1.Locate('id_grup',id,[]);

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

            fcDBTreeView1.Expand(fcDBTreeView1.ActiveNode);

        end;

end;