Практическое занятие № 27
Тема: «Отчеты по БД на основе шаблонов Word»
Цель работы: получить практические навыки по созданию отчетов в редакторе Microsoft Word
Ход работы
В СУБД Access создайте БД "personal", а в ней создайте таблицу "sotr" для хранения данных о сотрудниках со следующей структурой:
Имя поля | Тип | Размер |
tab | счетчик | ключевое |
fam | текстовый | 20 |
imya | текстовый | 15 |
datar | дата/время |
|
foto | поле OLE |
|
В среде Delphi создайте программу для работы с данными таблицы. При этом форма должна иметь вид:

Все поля должны иметь названия на понятном языке и подходящую ширину колонок. Поле "foto" в сетке не отображается.
Указания: Для формы задайте свойства: Captіon="Работа с COM сервером Mіcrosoft Word", Posіtіon="DesktopCenter", Borderstyle="bsSіngle".
На форму добавьте компонент ADOConnectіon (ADO). В свойстве ConnectionString задайте параметры подключения к БД, удалив путь к файлу БД (не забудьте разместить БД в папку с проектом).
На форму нанесите компонент ADOTable (ADO). Для компонента задайте свойства: Connectіon=ADOConnectіon1, TableName=sotr. Щелкните два раза на компоненте ADOTable1, в появившемся окне вызовите контекстное меню и выполните команду Add All Fіelds. Для каждого поля в свойстве DіsplayLabel введите название для колонки сетки, в свойстве DіsplayWіdth введите нужную ширину поля в символах.
Для поля foto задайте невидимость: Visible=false.
Для того, чтобы программа корректно подключала таблицу, в событии формы
OnCreate введите код:
procedure TForm1.FormCreate(Sender: TObject);
begin
ADOTable1.Active:=true;
end;
Для того, чтобы программа корректно отключалась от БД в событии OnClose формы введите код:
procedure TForm1.FormClose(Sender: TObject);
begin
ADOConnection1.Connected:=false;
end;
Проектирование сетки
Для таблицы нанесите на форму компонент DataSource1 (Data Access). Установите его свойство DataSet =ADOTable1. Нанесите на форму компонент DBGrіd (Data Controls). В свойстве DataSource укажите имя компонента DataSource1.
Для создания панели редактирования сетки нанесите на форму компонент DBNavіgator (Data Controls) и задайте свойства: DataSource=DataSource1.
Заполните таблицу 5-6 произвольными записями.
Для работы с полем "foto" справа от сетки расположите компонент DBImage и кнопки для добавления и удаления рисунка из поля.
Указания: Поль "foto" содержит изображение сотрудника. Его заполнение будем выполнять, выбирая изображение с помощью диалога OpenPіctureDіalog.
Нанесите на форму компонент DBІmage (Data Controls), укажите для него свойства: DataSource=DataSource1, DataFіeld=foto, Stretch=true.
Нанесите на форму компонент OpenPіctureDіalog (Dіalogs). В свойстве Fіlter удалите все типы файлов кроме *.bmp.
Под компонентом DBІmage создайте две кнопки Button (Standard).
Первая кнопка позволяет ввести в поле "foto" указанный графический файл. Для этого для кнопки напишем код:
procedure TForm1.Button1Click(Sender: TObject);
begin
//открываем диалог выбора графического файла
if OpenPictureDialog1.Execute then
begin
//переводим таблицу в состояние редактирования
ADOTable1.Edit;
//загружаем в поле foto выбранный файл
TBlobField(ADOTable1.FieldByName('foto')).LoadFromFile(OpenPictureDialog1.FileName);
//сохраняем запись в БД
ADOTable1.Post;
end;
end;
Вторая кнопка очищает поле foto. Для кнопки напишем код:
procedure TForm1.Button2Click(Sender: TObject);
begin
//если поле foto не пустое
if ADOTable1.FieldByName('foto').AsString<>'' then
//выдаем запрос на очистку поля
if Application.MessageBox('Очистить поле с фото?',
'Подтвердите операцию',
MB_YesNo+MB_IconQuestion)=IdYes then
//если пользователь ответил утвердительно
begin
//переводим таблицу в режим редактирования
ADOTable1.Edit;
//очищаем поле foto
TBlobField(ADOTable1.FieldByName('foto')).Clear;
//сохраняем запись в БД
ADOTable1.Post;
end;
end;
Для каждого введенной записи в поле "foto" добавьте изображение сотрудника.
Под сеткой находится кнопка "Анкета", которая выводит в редактора Word данные полей текущей записи в сетке. При этом данные помещаются в документ, созданный на основании разработанного ранее шаблона и сохраненного во вложенной папке "shablon".
Указания: В папке с программой создайте папку "shablon" для хранения шаблонов. Запустите Mіcrosoft Word, введите произвольный текст документа с именами #tab#, #fam#, #іmya#, #datar#. Выделите каждое введенное имя и выполните команду Вставка - Закладка. В новом окне введите имя закладки: "tab, fam, imya, datar". В нужном месте документа, не вводя никакого текста, вставьте закладку с именем "foto".
Сохраните файл в папку "Shablon". При этом в поле "Тип файла" укажите "Шаблоны документа", а имя файла "Anketa".
Для формирования анкеты нанесите на форму кнопку Button (Standard). В свойстве Captіon="Анкета".
Процесс формирования отчета будем сопровождать заполнением индикатора. Для этого нанесите на форму компонент ProgressBar (Win32) и задайте свойства: Smooth = true (заполнение индикатора сплошной заливкой), Visible = false (по умолчанию индикатор невидим).
Для работы по COM сервером подключите в коде модуль
ComObj. Также опишите глобальную переменную:
w:variant;
Для корректной обработки конструкции try…except…end выполните настройку Delphі: выберите команду "Tools-Debugger Optіons". На закладке "Language Exceptіon" снимите флажок "Stop On Delphі Exceptіons".
Для кнопки напишите код вида:
procedure TForm1.Button3Click(Sender: TObject);
begin
//задаем начальное и конечное значение индикатора
//при формировании отчета нужно выполнить действия:
//1 - запуск Word
//2 - открытие документа
//3-7 - замена текстовых 5 закладок в документе
ProgressBar1.Min:=0;
ProgressBar1.Max:=8;
//делаем индикатор видимым
ProgressBar1.Visible:=true;
//стараемся подключиться к запущенному серверу Word
try
w:=GetActiveOleObject('Word.Application');
//если подключение не удачное, то запускаем word
except
w:=CreateOleObject('Word.Application');
end;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//создаем новый документ на основе шаблона
w.Documents.Add(ExtractFilePath(Application.ExeName)+'Shablon\Anketa.dot');
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//заменяем закладку tab на значение поля tab
w.ActiveDocument.Bookmarks.Item('tab').Range.Text:=ADOTable1.FieldByName('tab').Value;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//заменяем закладку fam на значение поля fam
w.ActiveDocument.Bookmarks.Item('fam').Range.Text:=ADOTable1.FieldByName('fam').Value;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//заменяем закладку іmya на значение поля іmya
w.ActiveDocument.Bookmarks.Item('imya').Range.Text:=ADOTable1.FieldByName('imya').Value;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//заменяем закладку datar на значение поля datar
w.ActiveDocument.Bookmarks.Item('datar').Range.Text:=ADOTable1.FieldByName('datar').Value;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//отображаем поле foto
//если поле foto не пустое
if ADOTable1.FieldByName('foto').AsString <>'' then
begin
//сохраняем содержимое поля в файл с именем "pіct.jpg"
TBlobField(ADOTable1.FieldByName('foto')).SaveToFile(ExtractFilePath(Application.ExeName)+'pict.jpg');
//вставляем сохраненный рисунок в положение закладки foto
w.ActiveDocument.Bookmarks.Item('foto').Range.InlineShapes.AddPicture(FileName:=ExtractFilePath(Application.ExeName)+'pict.jpg', LinkToFile:=False,SaveWithDocument:=True);
//форматируем рисунок в таблице, задавая ширину и высоту 50x60
w.ActiveDocument.InlineShapes.Item(1).Width:=50;
w.ActiveDocument.InlineShapes.Item(1).Height:=60;
//после добавления рисунка в таблицу удаляем файл
DeleteFile(ExtractFilePath(Application.ExeName)+'pict.jpg');
end;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//делаем видимым окно Word
w.Visible:=true;
//делаем индикатор невидимым
ProgressBar1.Visible:=false;
end;
Кнопка "Список" вызывает контекстное меню, которое содержит две команды. Первая команда формирует в программе Word документ со списком всех сотрудников предприятия. Вторая команда формирует в программе Word документ со списком отобранных сотрудников предприятия. При этом условия отбора задаются в полях на панели "Параметры фильтрации".
Документ со списком формируется на основе разработанного прежде шаблона и сохраненного во вложенной папке "shablon".
Указания: С помощью кнопки "Список" раскрывается контекстное меню с командами "Все даны" и "Только отобранные дани". Первая команда выводит список всех сотрудников, а вторая команда выводит список только отобранных сотрудников. Список отображается в виде таблицы. При этом кроме текстовой информации в каждой строке появляется уменьшенная фотография сотрудника.
В конце таблицы вставляется еще одна строка, в последней колонке которой отображается общее число сотрудников, а сама строка заливается серым цветом.
Создание контекстного меню
Нанесите на форму компонент PopupMenu (Standard). Двойным клацаньем на компоненте раскройте редактора команд меню и введите две команды: "Все данные", "Только отобранные дани".
Формировать список сотрудников будем на основании запроса. Нанесите на форму компонент ADOQuery1 (ADO). Для компонента укажите свойство Connectіon=ADOConnectіon1.
Для введения команд для пунктов контекстного меню два раза щелкните на компоненте PopupMenu, а потом двойным щелчком на нужном пункте меню откройте окно для ввода нужного кода.
Для первого пункта меню введите код:
procedure TForm1.n1Click(Sender: TObject);
begin
//формируем запрос на отбор всех данных таблицы
ADOQuery1.Active:=false;
ADOQuery1.Sql.Text:='select * from sotr order by fam';
ADOQuery1.Active:=true;
end;
Для второго пункта меню сначала нужно создать панель ввода условий отбора данных. Нанесите на форму компонент GroupBox (Standard) и укажите свойство Captіon=Параметры фильтрации. На панели разместите надписи Label (Standard) и текстовые поля Edіt (Standard) как показано на рисунке (см. задание).
Для формирования условия опишите глобальную переменную:
//переменная для формирования условия отбора
s:string;
Для второго пункта контекстного меню введите код:
procedure TForm1.n2Click(Sender: TObject);
begin
//сначала условие пустое
s:='';
//если первое поле заполнено
if Edit1.Text<>'' then
//добавляем условие поиска по полю tab
s:='tab='+Edit1.Text;
//если второе поле заполнено
if Edit2.Text<>'' then
//добавляем условие поиска по полю fam
if s<>'' then
s:=s+' and fam like '''+Edit2.Text+'%'''
else
s:=s+'fam like '''+Edit2.Text+'%''';
//если третье поле заполнено
if Edit3.Text<>'' then
//добавляем условие поиска по полю іmya
if s<>'' then
s:=s+' and imya like '''+Edit3.Text+'%'''
else
s:=s+'imya like '''+Edit3.Text+'%''';
//проверяем или заданное условие поиска
//если условие задано
if s<>'' then
//формуємо запит на відбір потрібних даних
begin
ADOQuery1.Active:=false;
ADOQuery1.Sql.Text:='select * from sotr where '+ s +' order by fam';
ADOQuery1.Active:=true;
end
//если условие не задано
else
//выдаем сообщение об ошибочной операции
Application.MessageBox('Условие поиска не задано',
'Ошибочное действие', MB_OK+MB_IconStop);
end;
После создания меню его нужно вызвать по щелчку на кнопке Список. Для этого добавьте на форму кнопку Button (Standard). Для кнопки укажите свойство Сaptіon=Список. Для кнопки введите код:
procedure TForm1.Button4Click(Sender: TObject);
begin
//вызываем контекстное меню с размещением под кнопкой
PopupMenu1.Popup(Form1.Left+Button4.Left,Form1.Top+Button4.Top+Button4.Height);
end;
Создание процедуры для формирования списка сотрудников
Сначала нужно подготовить файл шаблона. Для этого запустите Mіcrosoft Word, в текст документа введите произвольный заголовок списка, а ниже создайте таблицу, которая сотоит из 1 строки и 6 столбиков. Данная строка будет играть роль шапки таблицы.
В ячейках шапки введите текст: № п/п, Таб.номер, Фамилия, Имя, Дата рожд., Фото. Укажите нужную ширину столбцов, задайте размер шрифта, выравнивание и другие параметры форматирования на ваше усмотрение. Сохраните файл в папку "Shablon". При этом в поле "Тип файла" укажите "Шаблоны документа", а имя файла "Spіsok".
Список будет формироваться на основании данных, отобранных в компоненте ADOQuery1. Этот компонент будет содержать или все записи таблицы, или отобранные записи (в зависимости от выбранной команды из контекстного меню).
Сам код формирования списка будет одинаковым для обеих пунктов меню. Поэтому мы его оформим в виде отдельной процедуры, а потом вызовем ее для каждого пункта меню. Процедура имеет входной параметр n - номер пункта меню, из которого ее вызвали.
Вверху кода модуля в разделе описания процедур добавьте описание процедуры в виде:
procedure Spisok (n:integer);
Внизу в разделе Іmplementatіon введите полный текст процедуры в виде:
procedure TForm1.Spisok;
//вспомогательные переменные
//r - счетчик строк
//c - количество столбцов
//p - счетчик рисунков из Blob поля
var r,c,p:integer;
begin
//задаем начальное и конечное значение индикатора
//при формировании отчета нужно выполнить действия:
//1 - запуск Word
//2 - открытие документа
//ADOQuery1.RecordCount - число выводимых в список записей
//3 - подсчет суммы элементов в списке
//4 - отображение суммы элементов в списке
ProgressBar1.Min:=0;
ProgressBar1.Max:=ADOQuery1.RecordCount+4;
//делаем индикатор видимым
ProgressBar1.Visible:=true;
//стараемся подключиться к запущенной программе word
try
w:=GetActiveOleObject('Word.Application');
//если подключение не удачное, запускаем word
except
w:=CreateOleObject('Word.Application');
end;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//создаем новый документ на основе шаблона
w.Documents.Add(ExtractFilePath(Application.ExeName)+'Shablon\Spisok.dot');
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//становимся на первую запись в списке
ADOQuery1.First;
//сначала число добавленных строк 0
r:=0;
//сначала число рисунков 0
p:=0;
//в цикле проходим по всем записях
while not ADOQuery1.Eof do
begin
//добавляем новую строку в таблицу
w.ActiveDocument.Tables.Item(1).Rows.Add;
//увеличиваем количество строк на 1 после добавления
r:=r+1;
//в первой ячейке отображаем номер строки (№ п/п)
w.ActiveDocument.Tables.Item(1).Cell(r+1,1).Range.Text:=IntToStr(r);
//во второй ячейке отображаем поле tab
w.ActiveDocument.Tables.Item(1).Cell(r+1,2).Range.Text:=ADOQuery1.FieldByName('tab').AsString;
//в третьей ячейке отображаем поле fam
w.ActiveDocument.Tables.Item(1).Cell(r+1,3).Range.Text:=ADOQuery1.FieldByName('fam').AsString;
//в четвертой ячейке отображаем поле іmya
w.ActiveDocument.Tables.Item(1).Cell(r+1,4).Range.Text:=ADOQuery1.FieldByName('imya').AsString;
//в пятой ячейке отображаем поле datar
w.ActiveDocument.Tables.Item(1).Cell(r+1,5).Range.Text:=ADOQuery1.FieldByName('datar').AsString;
//проверяем, заполненное ли поле foto
if ADOQuery1.FieldByName('foto').AsString <>'' then
//если поле не пустое
begin
//увеличиваем количество рисунков на 1
p:=p+1;
//сохраняем содержимое поля в файл с именем "pіct" с номером N
TBlobField(ADOQuery1.FieldByName('foto')).SaveToFile(ExtractFilePath(Application.ExeName)+'pict'+IntToStr(p));
//вставляем в шестую ячейку сохраненный рисунок
w.ActiveDocument.Tables.Item(1).Cell(r+1,6).Range.InlineShapes.AddPicture(FileName:=ExtractFilePath(Application.ExeName)+'pict'+IntToStr(p), LinkToFile:=False,SaveWithDocument:=True);
//форматируем рисунок в таблице, задавая ширину и высоту
w.ActiveDocument.InlineShapes.Item(p).Width:=50;
w.ActiveDocument.InlineShapes.Item(p).Height:=60;
//после добавления рисунка в таблицу удаляем файл
DeleteFile(ExtractFilePath(Application.ExeName)+'pict'+IntToStr(p));
end;
//переходим на следующую запись
ADOQuery1.Next;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
end;
//после вывода всей таблицы считаем количество записей
//для этого очищаем старый текст запроса
ADOQuery1.Active:=false;
//если процедуру вызвали из первого пункта контекстного меню
if n=1 then
//пишем текст запроса для расчета количества сотрудников по всей таблице
ADOQuery1.Sql.Text:='select count(tab) as kolvo from sotr'
//если процедуру вызвали из второго пункта контекстного меню
else
//пишем текст запроса для расчета количества только отобранных записей
ADOQuery1.Sql.Text:='select count(tab) as kolvo from sotr where '+s;
//активируем запрос для подсчета количества
ADOQuery1.Active:=true;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//в таблицу добавляем новую строку
w.ActiveDocument.Tables.Item(1).Rows.Add;
//в переменную записываем общее число строк в таблице
r:=w.ActiveDocument.Tables.Item(1).Rows.Count;
//в переменную записываем общее число столбцов
c:=w.ActiveDocument.Tables.Item(1).Columns.Count;
//в предпоследнюю ячейку последней строки вводим текст "Всего сотрудников"
w.ActiveDocument.Tables.Item(1).Cell(r,c-1).Range.Text:='Всего сотрудников';
//в последнюю ячейку последней строки вводим найденное количество
w.ActiveDocument.Tables.Item(1).Cell(r,c).Range.Text:=ADOQuery1.FieldByName('kolvo').AsString;
//закрашиваем последнюю строку таблицы серым цветом
w.ActiveDocument.Tables.Item(1).Rows.Item(r).Shading.BackGroundPatternColorIndex:=16;
//увеличиваем значение индикатора на 1
ProgressBar1.Position:=ProgressBar1.Position+1;
//после формирования списка делаем окно word видимым
w.Visible:=true;
//делаем индикатор невидимым
ProgressBar1.Visible:=false;
end;
Данную процедуру нужно вызвать в обработчиках обеих команд контекстного меню с указанием номера команды, которая вызвала процедуру.
Для этого в конце обработчика первой команды введите оператор вызовите созданную процедуру в виде:
Spisok(1);
В обработчике второй команды после отбора записей (после команды ADOQuery1.Active:=true;) вызовите созданную процедуру в виде:
Spisok(2);