Шаблоны и отчеты MS Word
01.01.2007
Многие популярные информационно-правовые системы содержат шаблоны различных документов в формате Word. Используя их и информацию данной статьи, вы можете легко и быстро создавать отчеты, если пишете свои программы на Delphi. Если пишете на другом языке, это не помеха, описанный подход справедлив к различным языкам.
Когда мы формируем сложный документ по шаблону, и когда в этом шаблоне есть таблицы, количество строк которых в выходном документе может быть произвольным и зависеть от объема информации, то нам недостаточно просто функций создания таблиц и записи в ячейки определенной информации. Для создания такого документа необходимо, как минимум, еще несколько функций работы с таблицами, в перечень которых входит перемещение по таблицам, добавление строк (столбцов) в конец или в середину таблицы. Также необходимо определять размер (количество строк, столбцов) и номер текущего столбца и строки. Чтобы понимать, о чем речь, необходимо просмотреть части 1-3 данной статьи, опубликованных в предыдущих номерах. Для того, чтобы применить свои знания к конкретной задаче, сделаем ее постановку. Например, в нашем документе есть таблица, которая представляет собой шаблон и заполняется из массива информации, который имеет произвольную длину. Таким документом может быть счет-фактура, заголовок которой представляет собой сложную таблицу, средняя часть представляет таблицу переменной длины, а нижняя также представляет сложную таблицу. Для заполнения такого шаблона можно использовать способ, описанный во второй части данной статьи. Этот способ основан на поиске в шаблоне переменных (неповторяющиеся строковые значения длиной 3-5 символов) и подстановке вместо них реальных значений на этапе формирования документа. Поэтому для добавления информации в такую таблицу придется осуществить поиск и позиционирование в строку (по переменной), в которую и перед которой необходимо вставлять строки, и запомнить, в какие колонки какую записывать информацию, но для начала необходимо определить, находится курсор в таблице или нет. Для этого используем свойство Information объекта Selection, в качестве параметра которого будет константа wdWithInTable. В этом случае этот метод возвращает TRUE, если курсор в таблице, или FALSE, если нет. Для использования в нашем приложении создадим функцию GetSelectionTable.Function GetSelectionTable:boolean; const wdWithInTable=12; begin GetSelectionTable:=true; try GetSelectionTable:=W.Selection.Information(wdWithInTable); except GetSelectionTable:=false; end; End;Если в нашем документе может быть более одной таблицы, то, скорее всего, необходима возможность перехода и позиционирование курсора на следующей или предыдущей таблице. Объект Selection дает нам эту возможность через методы GoToNext и GoToPrevious, в этом случае в качестве их параметров должна использоваться константа wdGoToTable.
Function GoToNextTable (table_:integer):boolean; const wdGoToTable=2; begin GoToNextTable:=true; try W.Selection.GoToNext (wdGoToTable); except GoToNextTable:=false; end; End; Function GoToPreviousTable (table_:integer):boolean; const wdGoToTable=2; begin GoToPreviousTable:=true; try W.Selection.GoToPrevious(wdGoToTable); except GoToPreviousTable:=false; end; End;Когда мы позиционируемся на таблице, можем определить количество столбцов и строк в ней. Для этого также используем свойство Information объекта Selection, но в качестве аргументов используем константы wdMaximum Number Of Columns и wdMaximum NumberOfRows.
Function GetColumnsRowsTable(table_:integer; var Columns,Rows:integer):boolean; const wdMaximumNumberOfColumns=18; wdMaximumNumberOfRows=15; begin GetColumnsRowsTable:=true; try Columns:=W.Selection.Information (wdMaximumNumberOfColumns); Rows:=W.Selection.Information (wdMaximumNumberOfRows); except GetColumnsRowsTable:=false; end; End;Кроме размера таблицы, нам может быть необходим номер колонки и строки, на которой позиционирован курсор. Для этого так же используем свойство Information объекта Selection, но в качестве аргументов используем константы wdStartOfRangeColumnNumber, wdStartOfRangeRowNumber. Для реализации этого в Delphi создадим функцию GetColumnRowTable.
Function GetColumnRowTable(table_:integer; var Column,Row:integer):boolean; const wdStartOfRangeColumnNumber=16; wdStartOfRangeRowNumber=13; begin GetColumnRowTable:=true; try Column:=W.Selection.Information (wdStartOfRangeColumnNumber); Row:=W.Selection.Information (wdStartOfRangeRowNumber); except GetColumnRowTable:=false; end; End;После того, как мы нашли таблицу в шаблоне документа и позиционировались на определенной ячейке, нам необходимо выполнить некоторые действия с ней (добавить, вставить строки, записать информацию). Очевидно, что нам нужен будет набор функций для ее модификации. Обычно во время формирования таблицы мы не знаем, сколько будет строк. Они могут добавляться в конец или вставляться в середину таблицы. Если для формирования документа мы используем шаблон таблицы и в нем уже есть, например, заголовок, то нам не обойтись без процедур добавления или вставления строк. Добавить строку в конец таблицы можно, используя метод Add коллекции Rows. Чтобы это сделать из приложения на Delphi, достаточно создать и использовать функцию. Определим ее как AddRowTableDoc.
Function AddRowTableDoc (table_:integer):boolean; begin AddRowTableDoc:=true; try W.ActiveDocument.Tables.Item(table_).Rows.Add; except AddRowTableDoc:=false; end; End;Для того, чтобы вставлять строки в середину таблицы, удобно использовать пару операторов. Первый выделяет строку, перед которой необходимо вставить новую, второй вставляет строку (строки). Смотрите функцию InsertRowsTableDoc.
Function InsertRowsTableDoc(table_,position_, count_:integer): boolean; begin InsertRowsTableDoc:=true; try W.ActiveDocument.Tables.Item(table_).Rows.Item(position_).Select; W.Selection.InsertRows (count_); except InsertRowsTableDoc:=false; end; End;Для добавления одной строки можно использовать также и метод Add коллекции Rows, но с параметром, в качестве которого выступает ссылка на строку, перед которой необходимо вставить новую. Первый оператор получает ссылку на строку, второй вставляет новую. Смотрите реализацию на Delphi (InsertRowTableDoc).
Function InsertRowTableDoc(table_,position_: integer):boolean; var row_:variant; begin InsertRowTableDoc:=true; try row_:=W.ActiveDocument.Tables.Item(table_).Rows.Item(position_); W.ActiveDocument.Tables.Item(table_).Rows.Add(row_); except InsertRowTableDoc:=false; end; End;Когда мы в своем распоряжении имеем набор функций для изменения таблицы, можно приступать к решению задачи - созданию документа типа счета-фактуры на базе шаблона. Полный исходный текст и полную версию шаблона счета-фактуры можно скачать по адресу www.kornjakov.ru/st1_4.zip. Здесь мы рассмотрим фрагмент данного документа. Создадим шаблон - документ формата DOC - и разместим его на диске в каталоге нашего проекта. Внешний вид шаблона смотрите на рисунке. Здесь будем заполнять только табличную часть. О том, как заполнять остальное, читайте вторую часть данной статьи. Для начала наши новые функции скопируем в библиотеку MyWord, которую мы создавали, начиная с первой части статьи. Затем создадим новый проект, на форме которого разместим кнопку, а в процедуре обработки ее нажатия напишем следующий программный текст.
procedure TForm1.Button1Click(Sender: TObject); var tablica_:integer; col_,row_:integer; a_:integer; metki_:array[1..12] of record col:integer; row:integer; metka:string; end; tovar:array[1..2,1..12] of variant; begin // Заполняем массив данными. Массив используется //для простоты демонстрации, в реальной программе //данные берутся из базы данных. tovar[1,1]:='Стул офисный'; tovar[1,2]:='шт.'; tovar[1,3]:=2; tovar[1,4]:=520.00; tovar[1,5]:=1040.00; tovar[1,6]:='-'; tovar[1,7]:=20; tovar[1,8]:=208.0; tovar[1,9]:=1248.00; tovar[1,10]:=62.40; tovar[1,11]:='Россия'; tovar[1,12]:='-'; tovar[2,1]:='Телефон'; tovar[2,2]:='шт.'; tovar[2,3]:=3; tovar[2,4]:=315.25; tovar[2,5]:=945.75; tovar[2,6]:='-'; tovar[2,7]:=20; tovar[2,8]:=189.15; tovar[2,9]:=1134.90; tovar[2,10]:=56.70; tovar[2,11]:='Беларусь'; tovar[2,12]:='-'; if CreateWord then begin VisibleWord(true); If OpenDoc(ExtractFileDir (application.ExeName) +'\sf.doc') then begin tablica_:=1; for a_:=1 to 12 do begin StartOfDoc; if FindTextDoc('###M'+inttostr(a_)+'&') then if GetSelectionTable then begin messagebox(handle,'Находимся в таблице, запоминаем метку(переменную), номер колонки и строки!', pchar('Номер колонки/строки = '+inttostr(col_)+'/'+inttostr(row_)),0); metki_[a_].col:=col_; metki_[a_].row:=row_; metki_[a_].metka:='###M'+inttostr(a_)+'&'; end; end; Messagebox(handle,'Заполняем первую строку','',0); for a_:=1 to 12 do begin SetTextToTable(tablica_,metki_[a_].row,metki_[a_].col,tovar[1,a_]); end; a_:=1; Messagebox(handle,'Добавляем строку','',0); InsertRowTableDoc(tablica_, metki_[a_].row); Messagebox(handle,'Заполняем вторую строку','',0); for a_:=1 to 12 do begin SetTextToTable(tablica_,metki_[a_].row,metki_[a_].col,tovar[2,a_]); end; SaveDocAs(ExtractFileDir(application.ExeName)+'\Счет - фактура.doc'); Messagebox(handle,'Текст сохранен','',0); CloseDoc; end; Messagebox(handle,' Текст закрыт','',0); CloseWord; end; end;Мы сформировали фрагмент сложного документа, но вы, возможно, захотите в дальнейшем сами развивать эту тему и использовать все возможности Word.Application. В следующей части я постараюсь на примерах объяснить, каким образом это сделать. По всем вопросам вы можете обратиться к автору по адресу www.kornjakov.ru или _kvn@mail.ru. Василий КОРНЯКОВ Литература: Н. Елманова, С. Трепалин, А. Тенцер "Delphi 6 и технология COM" "Питер" 2002.