|
Экспорт из БД в Word
К статье прилагаются исходный код примера и база данных . Выполнены в Delphi 7 и Access 2003 соответственно.
- закладками; - колонтитулами; - надписями; - шрифтами; - курсором. Не буду углубляться в особенности позднего и раннего связывания, т.к. информации на эту тему уже достаточно. W1: TWordApplication; Vr: OleVariant; У W o d есть коллекция документов: Vr := номер нужного документа; //так можно обратиться к нужному документу w1. Documents . Item ( vr ) ; У всякой коллекции есть свойство count - количество таким образом w1.Documents.count - количество документов. 1. буквы (characters): // так можно получить доступ // к коллекции букв активного документа w1. ActiveDocument . Characters Теперь можно получить доступ к: w1. ActiveDocument . Characters . I tems ( vr ) … // конкретной букве w1. ActiveDocument . Characters . C ount … // количеству букв 2. таблицы: W1. activedocument . tables А вот так: W1. ActiveDocument . Tables . Item ( W1. ActiveDocument . Tables . Count ) . Columns . Item ( 2 ) . Select ; можно выбрать вторую колонку последней таблицы. 3. абзацы: w1. ActiveDocument . Paragraphs 4. фигуры: W1. ActiveDocument . Shapes Ну и т.д…. Кстати, очень часто приходится работать с объектом range . Так, например, чтобы вытащить текст из документа, можно написать: st:=w1. ActiveDocument . Range ( 1 , 5 ) . Text ; // Это будет текст с 1-го по 5-й символ st:=W1. ActiveDocument . Paragraphs . Item ( 1 ) . Range . Text ; // это будет текст первого абзаца текущего документа Теперь перейдем к конкретной реализации. Flat(IdFlat,NameFlat) Korp(IdKorp,NameKorp) Ul(IdUl,NameUl) и дочерняя таблица Main (Id,Tel,Name,IdUl,House,IdKorp,IdFlat). Почему структура именно такая - не важно… это - просто пример. Конкретная задача - экспортировать жильцов улиц Ахметова и Летчиков. 1. Без использования шаблона. Получаем следующий макрос: Sub Макрос 1 () ' ' Макрос1 Макрос ' Макрос записан 08.11.2004 YurikGL ' ActiveDocument. Shapes ( " Text Box 8 " ) . Select Selection. TypeText Text:= " текст на титульной " Selection. EndKey Unit:=wdStory If ActiveWindow. View . SplitSpecial <> wdPaneNone Then ActiveWindow. Panes ( 2 ) . Close End If If ActiveWindow. ActivePane . View . Type = wdNormalView Or ActiveWindow. ActivePane. View . Type = wdOutlineView Then ActiveWindow. ActivePane . View . Type = wdPrintView End If ActiveWindow. ActivePane . View . SeekView = wdSeekCurrentPageHeader Selection. HeaderFooter . Shapes ( " Text Box 5 " ) . Select Selection. TypeText Text:= " Текст в колонтитуле " ActiveWindow. ActivePane . View . SeekView = wdSeekMainDocument End Sub Если преобразовать в код делфи, то получим: //выбираем первую надпись
vr:= ' Text Box 8 ' ;
w1. ActiveDocument . Shapes . Item ( vr ) . Select ( EmptyParam ) ;
//пишем туда текст
w1. Selection . TypeText ( ' текст на титульной ' ) ;
//переходим в конец документа
vr:=wdStory;
w1. Selection . EndKey ( vr,EmptyParam ) ;
{Всякие if-ы нам не нужны}
//переходим в верхний колонтитул
w1. ActiveWindow . ActivePane . View . SeekView :=wdSeekCurrentPageHeader;
vr:= ' Text Box 5 ' ;
w1. Selection . HeaderFooter . Shapes . Item ( vr ) . Select ( EmptyParam ) ;
w1. Selection . TypeText ( ' текст в колонтитуле ' ) ;
w1. ActiveWindow . ActivePane . View . SeekView :=wdSeekMainDocument;
Итак, мы сформировали титульную страницу и колонтитул... Кстати, кроме объектов "Надпись" можно еще эффективно использовать закладки. Чтобы узнать, как с ними работать, достаточно записать соответствующий макрос, а например, выбрать текст между первой и второй закладками можно так: vr1,vr2,vr3,vr4:OleVariant; vr1:= 1 ; vr2:= 2 ; vr3:=W1. ActiveDocument . Bookmarks . Item ( vr1 ) . End_ ; vr4:=W1. ActiveDocument . Bookmarks . Item ( vr2 ) . End_ ; W1. ActiveDocument . Range ( vr3,vr4 ) . Select ; Кстати, если кто-нибудь найдет красивое решение без использования переменных OleVariant - отпишите мне на мыло. Вывести данные из базы в документ можно либо используя слияние, либо построчно. Для слияния код выглядит примерно так: var vr1,vr2,vr3,vr4,vr5,vr6: OleVariant; begin vr1:= 0 ; vr2:= false ; vr3:= 'Provider=Microsoft.Jet.OLEDB.4.0;Password=""""; User ID=Admin; Data Source=C:\Dataware\Deplhi7\Для статьи\db1.mdb; Mode=Read;Extended Properties=""""; Jet OLEDB:System database=""""; Jet OLEDB:Database Password="""";Jet OLEDB:Engine Type=5;' ; vr4:= 'SELECT Телефон, Корпус FROM QMain ORDER BY ФИО' ; vr5:= GetCurrentDir + ' \db1.mdb ' ; vr6:=- 1 ; winit; try w1. Connect ; w1. Documents . Add ( EmptyParam,EmptyParam,EmptyParam,EmptyParam ) ; w1. Visible := true ; w1. Selection . Range . InsertDatabase ( vr1,vr1,vr2, vr3,vr4,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,vr5,EmptyParam, EmptyParam,EmptyParam ) ; except w1. Disconnect ; end ; Человек, знающий основы баз данных легко сможет преобразовать этот код под свои нужды изменив строку подключения и запрос. Для построчного вывода я пользовался созданной мною процедурой TableExport(DataSet:TDataSet; Title, FlagText:string), которая приведена в примере. В нее передаются датасет, заголовок таблицы и текстовый параметр FlagText. Если он равен ‘’ то экспортируется вся таблица. В противном случае, экспортируются лишь те записи, у которых значение последнего поля равно FlagText. Это сделано для того, чтобы получив выборку, которая долго вычислялась, можно было ее разнести по нескольким таблицам в отчете. // заголовок ADODataSet1. fields [ 1 ] . DisplayLabel := 'ФИО' ; // ширина столбца ADODataSet1. fields [ 1 ] . tag := round ( w1. CentimetersToPoints ( 10 )) ; // отображать поле ADODataSet1. fields [ 1 ] . Visible := true ; Кстати, в приведенном примере, данные сначала выбрасываются в Word, а потом одной командой преобразуются в таблицу. // проведение границы с левой стороны выбранной области // Используется при формировании таблиц. // Изменяя параметры, можно создавать произвольные таблицы w1. Selection . Cells . Borders . Item ( wdBorderLeft ) . LineStyle :=wdLineStyleSingle; //изменение параметров шрифта W1. Selection . Font . Size := 15 ; //изменение параметров шрифта W1. Selection . Font . bold := 1 ; // получить номер последнего символа выбранной // области I:= W1. Selection . End ; Если Вы работаете через OleContainer, то нелишними будут команды типа: OleContainer1. OleObject . CommandBars . Item [ 'Standard' ] . Visible := false ; OleContainer1. OleObject . CommandBars . Item [ 'Formatting' ] . Visible := false ; OleContainer1. OleObject . CommandBars . Item [ 'Drawing' ] . Visible := false ; которые убирают соответствующие менюшки, которые обычно разлетаются по всей форме. unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls,
DB, ADODB, OleServer, W ord2000;
type
TForm1 = class ( TForm )
ADOConnection1: TADOConnection;
ADODataSet1: TADODataSet;
Button1: TButton;
W1: TWordApplication;
// Здесь удалено несколько строчек
StatusLabel: TLabel;
Button2: TButton;
Button3: TButton;
procedure Button1Click ( Sender: TObject ) ;
procedure TableExport ( DataSet:TDataSet;
Title, FlagText: string ) ;
Procedure TableLineSet;
procedure WInit;
procedure Button2Click ( Sender: TObject ) ;
private
{ Private declarations }
public
{ Public declarations }
end ;
var
Form1: TForm1;
implementation
uses ComObj;
{$R *.dfm}
procedure TForm1. Button1Click ( Sender: TObject ) ;
var
vr: olevariant;
begin
try
statusLabel. Caption := 'Формирую отчет ждите' ;
winit;
w1. Connect ;
//w1.Visible:=true;
vr:= GetCurrentDir + ' \Shablon.doc' ;
W1. Documents . Open ( vr,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,EmptyParam ) ;
//выбираем первую надпись
vr:= 'Text Box 8' ;
w1. ActiveDocument . Shapes . Item ( vr ) . Select ( EmptyParam ) ;
//пишем туда текст
w1. Selection . TypeText ( 'текст на титульной' ) ;
//переходим в конец документа
vr:=wdStory;
w1. Selection . EndKey ( vr,EmptyParam ) ;
{Всякие if-ы нам не нужны}
//переходим в верхний колонтитул
w1. ActiveWindow . ActivePane . View . SeekView :=
wdSeekCurrentPageHeader;
vr:= 'Text Box 5' ;
w1. Selection . HeaderFooter . Shapes . Item ( vr ) . Select ( EmptyParam ) ;
w1. Selection . TypeText ( 'текст в колонтитуле' ) ;
w1. ActiveWindow . ActivePane . View . SeekView :=wdSeekMainDocument;
ADODataSet1. Close ;
ADODataSet1. CommandText :=
'SELECT [Main].[Tel], ' +
'[Main].[name], ' +
'[Main].[House], ' +
'[Korp].[NameKorp], ' +
'[Flat].[NameFlat], ' +
'[Ul].[NameUl] ' +
' FROM Main, Ul, Korp, Flat ' +
' WHERE ([Main].[IdUl]=[Ul].[IdUl])
And ([Main].[IdKorp]=[Korp].[IdKorp])
And ([Main].[IdFlat]=[Flat].[IdFlat]) ' ;
ADODataSet1. Open ;
// несколько строчек убрано
// настраиваем параметры экспорта
// заголовок
ADODataSet1. fields [ 0 ] . DisplayLabel := 'Телефон' ;
//ширина столбца
ADODataSet1. fields [ 0 ] . tag := round ( w1. CentimetersToPoints ( 2 )) ;
//отображать поле
ADODataSet1. fields [ 0 ] . Visible := true ;
//заголовок
ADODataSet1. fields [ 1 ] . DisplayLabel := 'ФИО' ;
//ширина столбца
ADODataSet1. fields [ 1 ] . tag := round ( w1. CentimetersToPoints ( 10 )) ;
ADODataSet1. fields [ 1 ] . Visible := true ;
ADODataSet1. fields [ 2 ] . DisplayLabel := 'Дом' ;
//ширина столбца
ADODataSet1. fields [ 2 ] . tag := round ( w1. CentimetersToPoints ( 1 . 5 )) ;
ADODataSet1. fields [ 2 ] . Visible := true ;
ADODataSet1. fields [ 3 ] . DisplayLabel := 'Корпус' ;
//ширина столбца
ADODataSet1. fields [ 3 ] . tag := round ( w1. CentimetersToPoints ( 1 . 5 )) ;
ADODataSet1. fields [ 3 ] . Visible := true ;
ADODataSet1. fields [ 4 ] . DisplayLabel := 'Квартира' ;
//ширина столбца
ADODataSet1. fields [ 4 ] . tag := ound ( w1. CentimetersToPoints ( 1 . 5 )) ;
//отображать поле
ADODataSet1. fields [ 4 ] . Visible := true ;
//не отображать поле
ADODataSet1. fields [ 5 ] . Visible := false ;
//вызываем процедуру экспорта
TableExport ( ADODataSet1, ' Живущие на улице
Ахметова' , 'АХМЕТОВА' ) ;
TableExport ( ADODataSet1, 'Живущие на улице
Летчиков' , 'ЛЕТЧИКОВ' ) ;
//отображем Word. Это можно было сделать и
// вначале, но тогда вывод данных был бы
// значительно медленнее
w1. Visible := true ;
w1. Disconnect ;
statusLabel. Caption := '' ;
except
on e:exception do begin
w1. Visible := true ;
statusLabel. Caption := 'Отчет был сформирован неверно' ;
w1. Disconnect ;
raise Exception. Create ( 'Ошибка формирования
отчета.' + #13 +e. Message ) ;
end ;
end ;
end ;
procedure TForm1. TableExport ( DataSet:TDataSet;
Title, FlagText: string ) ;
var
i,ColCount, // количество колонок в таблице
TableBeg, // Номер символа в начале таблицы
TableBeg2: integer ; // Номер символа в начале данных таблицы
vr1,vr2:OleVariant;
f: boolean ;
st: string ;
Function ConvertString ( S: string ) : string ;
{это, казалось бы глупая функция, делает очень
важное дело.
При формировании таблицы в качестве разделителя по
умолчанию
используется "-", который может встречаться в
экспортируемых
записях. В этом случае в таблицу преобразуется
абсолютно
неверно. Чтобы избежать этого, мы меняем обычный
"-" на символ
с кодом #173, который отображается точно так-же}
Begin
Result := StringReplace ( S, '-' , #173 , []) ;
End ;
|