Delphi вывод отчета в word

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
unit Unit2;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DB, Grids, DBGrids, DBTables, Menus,
  ExtCtrls, DBCtrls, BDE,   Mask,ComObj,
 Buttons, ExtDlgs, JPEG,  Consts,ShellAPI;
 
type
  TForm2 = class(TForm)
    Table1: TTable;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    MainMenu1: TMainMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;
    N4: TMenuItem;
    N5: TMenuItem;
    N6: TMenuItem;
    N7: TMenuItem;
    N8: TMenuItem;
    N9: TMenuItem;
    N10: TMenuItem;
    N11: TMenuItem;
    N12: TMenuItem;
    Edit1: TEdit;
    ComboBox1: TComboBox;
    N13: TMenuItem;
    N14: TMenuItem;
    N15: TMenuItem;
    N16: TMenuItem;
    GroupBox1: TGroupBox;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    CheckBox3: TCheckBox;
    CheckBox4: TCheckBox;
    CheckBox5: TCheckBox;
    CheckBox6: TCheckBox;
    DBImage1: TDBImage;
    DBMemo1: TDBMemo;
    Image1: TImage;
    Label1: TLabel;
    Label2: TLabel;
    N17: TMenuItem;
    Excel1: TMenuItem;
    Excel2: TMenuItem;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button2Click(Sender: TObject);
    procedure N7Click(Sender: TObject);
    procedure N8Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Table1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
    procedure Edit1Change(Sender: TObject);
    procedure N15Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure N12Click(Sender: TObject);
    procedure N16Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure CheckBox2Click(Sender: TObject);
    procedure CheckBox3Click(Sender: TObject);
    procedure CheckBox4Click(Sender: TObject);
    procedure CheckBox5Click(Sender: TObject);
    procedure CheckBox6Click(Sender: TObject);
    procedure N13Click(Sender: TObject);
    procedure N11Click(Sender: TObject);
    procedure N9Click(Sender: TObject);
    procedure N5Click(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure Excel2Click(Sender: TObject);
    procedure Excel1Click(Sender: TObject);
    procedure N17Click(Sender: TObject);
 
  private
    { Private declarations }
    procedure NewGridWinProc(var Msg: TMessage);
  public
    { Public declarations }
  end;
Type MyRec=Record
           Name:string[255];
           Age:byte;
           Ar:array[1..10] of integer;
        End;
var
  Form2: TForm2;
  MyVar:MyRec;
 f:File of MyRec;
 
implementation
 
uses Unit1, Unit3, Unit4, Unit5;
{$R *.dfm}
 
var OldGridWinProc: TWndMethod;
 
 
 
 
procedure TForm2.Button1Click(Sender: TObject);
begin
form5.show;
form2.hide;
end;
 
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Table1.Active then // ???? ??????? ???????, ??
    begin
      Table1.FlushBuffers; // ????? ????????? ?? ??????? ? ???? biolifes.db
      Table1.Close; // ???????? ???????
      form1.Close;
    end;
 
end;
 
procedure TForm2.Button2Click(Sender: TObject);
begin
form3.show;
table1.insert;
end;
              
procedure TForm2.N7Click(Sender: TObject);
begin
Close;
end;
 
procedure TForm2.N8Click(Sender: TObject);
begin
form3.show;
table1.insert;
end;
 
procedure TForm2.Button3Click(Sender: TObject);
begin
form3.Show;
form2.Table1.Edit
 
end;
 
procedure TForm2.Button4Click(Sender: TObject);
begin
table1.Delete;
application.Title:=('Îïîâåùåíèå');
showmessage('Çàïèñü óäàëåíà!');
end;
 
procedure TForm2.Table1FilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
var
    a,f:string;
begin
  case ComboBox1.ItemIndex of
  0:  f:='Name';
  1:  f:='Year';
  2:  f:='Country';
  3:  f:='Mass';
  4:  f:='Len';
  5:  f:='Capacity';
  end;
  if VarIsNull(DataSet[f]) then
  begin
 
  end
  else
  begin
    a := Copy(DataSet[f],1, Length(Edit1.Text));
    Accept := a = Edit1.text;
  end;
end;
 
 
 
procedure TForm2.Edit1Change(Sender: TObject);
begin
if Edit1.Text = ('') then begin
  Table1.Filtered:=False end
  else Table1.Filtered:=True;
end;
 
procedure TForm2.N15Click(Sender: TObject);
begin
if N15.Checked=true then begin
label1.Show;
label2.Show;
combobox1.Show;
edit1.Show;
end
else
begin
label1.hide;
label2.hide;
combobox1.hide;
edit1.hide;
end;
end;
 
procedure TForm2.FormCreate(Sender: TObject);
begin
label1.hide;
label2.hide;
combobox1.hide;
edit1.hide;
groupbox1.Hide;
checkbox1.Hide;
checkbox2.Hide;
checkbox3.Hide;
checkbox4.Hide;
checkbox5.Hide;
checkbox6.Hide;
 
 
 OldGridWinProc := DBGrid1.WindowProc;
 DBGrid1.WindowProc := NewGridWinProc;
end;
 
procedure TForm2.N12Click(Sender: TObject);
begin
Form4.Show;
end;
 
procedure TForm2.N16Click(Sender: TObject);
begin
Close;
end;
 
procedure TForm2.CheckBox1Click(Sender: TObject);
begin
if checkbox1.Checked=true then
begin
table1.IndexName:='N';
end;
 end;
procedure TForm2.CheckBox2Click(Sender: TObject);
begin
if checkbox2.Checked=true then
begin
table1.IndexName:='G';
end;
end;
 
procedure TForm2.CheckBox3Click(Sender: TObject);
begin
if checkbox3.Checked=true then
begin
table1.IndexName:='S';
end;
end;
 
procedure TForm2.CheckBox4Click(Sender: TObject);
begin
if checkbox4.Checked=true then
begin
table1.IndexName:='M';
end;
end;
 
procedure TForm2.CheckBox5Click(Sender: TObject);
begin
if checkbox5.Checked=true then
begin
table1.IndexName:='D';
end;
end;
 
procedure TForm2.CheckBox6Click(Sender: TObject);
begin
if checkbox6.Checked=true then
begin
table1.IndexName:='V';
end;
end;
 
procedure TForm2.N13Click(Sender: TObject);
begin
if N13.Checked=true then begin
groupbox1.Show;
checkbox1.Show;
checkbox2.Show;
checkbox3.Show;
checkbox4.Show;
checkbox5.Show;
checkbox6.Show;
end
else
begin
groupbox1.hide;
checkbox1.Hide;
checkbox2.Hide;
checkbox3.Hide;
checkbox4.Hide;
checkbox5.Hide;
checkbox6.Hide;
 
end;
   end;
   
procedure TForm2.N11Click(Sender: TObject);
begin
table1.Delete;
application.Title:=('Îïîâåùåíèå..');
showmessage('Çàïèñü óäàëåíà!');
end;
 
procedure TForm2.N9Click(Sender: TObject);
begin
form3.Show;
end;
 
procedure TForm2.N5Click(Sender: TObject);
begin
form5.Show;
form2.Hide;
end;
 
function BDEInstalled: Boolean;
begin
  Result:= (dbiInit(nil) = 0);
  if not Result then ShowMessage('BDE íå óñòàíîâëåí.');
end;
 
function TableActivate: boolean;
 var ExeDir, DbDir, DbFile: String;
begin
  Result:= false;
  if  BDEInstalled then
     begin
        ExeDir:= ExtractFilePath(Application.ExeName);
        DbDir:= ExeDir+'db';
        DbFile:= DbDir+Form2.Table1.TableName;
        if FileExists(DbFile) then
           begin
              Form2.Table1.DataBaseName:= DbDir;
              Form2.Table1.Open;
              Result:= true;
          end;
    end;
end;
procedure TForm2.FormActivate(Sender: TObject);
begin
   if not TableActivate then ShowMessage('Íå óäàëîñü îòêðûòü òàáëèöó ÁÄ.');
 
end;
 
 
 
procedure TForm2.NewGridWinProc(var Msg: TMessage);
begin
   if Msg.Msg = WM_MOUSEWHEEL then 
     begin
       if SmallInt(HiWord(Msg.wParam)) > 0 then DBGrid1.DataSource.DataSet.Prior
        else DBGrid1.DataSource.DataSet.Next;
       Msg.Result := 1;
     end else OldGridWinProc(Msg);
end;
procedure TForm2.Excel2Click(Sender: TObject);
var
 XL, XArr: Variant;
 i: Integer;
 j: Integer;
begin
 
 XArr:=VarArrayCreate([1,form2.Table1.FieldCount],varVariant);
 XL:=CreateOLEObject('Excel.Application');     
 XL.WorkBooks.add;
 XL.visible:=true;
 
 j := 1;
 form2.Table1.First;
 while not form2.Table1.Eof do
  begin
   i:=1;
   while i<=form2.Table1.FieldCount do
    begin
     XArr[i] := form2.Table1.Fields[i-1].Value;
     i := i+1;
    end;
   XL.Range['A'+IntToStr(j),
   CHR(64+form2.Table1.FieldCount)+IntToStr(j)].Value := XArr;
   form2.Table1.Next;
   j:=j+1;
  end;
 XL.Range['A1',CHR(64+form2.Table1.FieldCount)+IntToStr(j)].select;
 // XL.cells.select;                     
 XL.Selection.Font.Name:='Arial cur';
 XL.Selection.Font.Size:=10;
 XL.selection.Columns.AutoFit;
 XL.Range['A1','A1'].select;
end;
 
procedure TForm2.Excel1Click(Sender: TObject);
begin
AssignFile(f,'MyFile.rec');
Rewrite(f);
MyVar.Name:='Yaroslav';
MyVar.Age:=17;
seek(f,100);
Write(f,MyVar);
Closefile(f);
end;
procedure TForm2.N17Click(Sender: TObject);
begin
  ShellExecute(Handle, 'open', 'Helpdrexplain.chm',nil, nil, SW_SHOW);
end;
 
end.

Собранные версии компонента с примерами использования

Архив на Яндекс.Диск

Файлы в архиве

Файлы для Delphi 7

  • ОписаниеРазработки.txt — этот файл.
  • WordReport70.zip — исполняемые файлы и ресурсы компонента.
  • WR_ExampleD7.zip — пример применения компонента.
  • WordReport70src.zip — исходные коды компонента.

Файлы для Delphi XE3

  • ОписаниеРазработки.txt — этот файл.
  • WordReport170.zip — исполняемые файлы и ресурсы компонента.
  • WordReport170src.zip — исходные коды компонента.
  • WR_ExampleDXE3.zip — пример применения компонента.

Назначение

Компонент предназначен для автоматизации создания отчетов через MS Word.
Как исходный шаблон, так и готовый отчет представляют собой обычные документы Word, что обеспечивает пользователю самыме богатые возможности редактирования, предпросмотр и печать без каких-либо дополнительных средств.

Программные требования

  • Borland Delphi или Embarcadero RAD Studio XE3.
  • Microsoft Word 2000 и выше.

Установка

  1. Извлечь файлы из WordReport(Version).zip в директорию с установленной Delphi (например, WordReport70.zip в «C:Program Files (x86)BorlandDelphi7»).
  2. Запустить Delphi.
  3. Выбрать пункт меню Component >> Install Packages…
  4. Нажать кнопку Add… и выбрать файл WordReport(Version).bpl в DelphiBin (например, WordReport70.bpl в «C:Program Files (x86)BorlandDelphi7BinWordReport70.bpl»)
  5. Компонент готов к работе. Его можно найти на вкладке WordReport.

Инструкция

Правила создания шаблонов

Шаблон в нашем случае — это документ MS Word (именно документ — т.е. файл *.doc, а не *.dot !), составленный по определенным правилам.

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

  • Каждая секция должна быть отмечена закладкой с именем DataN, где N — целое число от 1 до 8.
    Повторяться будет ТОЛЬКО то, что в диапазоне закладки, поэтому закладкой лучше отмечать всю строку документа целиком. Если секция используется
    для повторения строки таблицы, то отмечать закладкой следует также всю строку документа, в которой находится эта строка таблицы.

  • Существует три категории переменных шаблона:

  1. переменные вне секций

    Синтаксис объявления: #(ИмяСвободнойПеременной)
    Способ определения значения: напрямую, методом SetValue

  2. переменные секций

    Синтаксис объявления: #(ИмяСекции(ИмяСчетчика).ИмяПеременной)
    Способ определения значения: из текущей записи привязанного поля набора данных.

  3. счетчики записей секций

    Синтаксис объявления: #(ИмяСчетчика)
    Объявление действительно только внутри секции.
    Заменяется на текущий номер записи привязанного набора данных при отсутствии групп секций
    или на номер записи в неразвывной последовательности при группировке секций.

  • ИмяСвободнойПеременной — ненулевая последовательность латинских букв, цифр и точек (только букв, цифр и точек, никаких других знаков!). Регистр букв не важен.

  • ИмяПеременной — ненулевая последовательность латинских букв и цифр. Регистр букв не важен.

  • ИмяСчетчика — ненулевая последовательность латинских букв и цифр. Регистр букв не важен.

  • ИмяСекции — ненулевая последовательность латинских букв и цифр. Регистр букв не важен.

  • Максимальное количество переменных в секции — 16.

  • Максимальное количество секций в документе — 8.

  • Максимальное количество переменных вне секций — 2^31 — 1, то есть верхняя граница 32-битного целого типа.

Описание функционала компонента

Свойства времени разработки

Имя документа, содержащего шаблон

TemplateDocFileName: string

Имя документа, в котором следует сохранить готовый отчет

ResultDocFileName: string

Показать MS Word c готовым отчетом при вызове Quit

ShowResult: boolean

Свойства времени выполнения

Массив секций документа.
Доступ по имени секции.
Если секция не найдена — возвращает nil.

Bands[Name:string]: TDataBand

Количество секций документа.

BandCount: integer

Методы

Существует ли в шаблоне секция с именем BandName.

function BandExists(BandName: string): boolean;

Сохранить документ в файле FileName.

procedure SaveToFile(FileName: string);

Выйти. Завершает процесс MS Word или показывает готовый документ.

procedure Quit;

Сформировать отчет. Выполняет все операции для формирования одного отчета, сохраняет и закрывает полученный документ.
Однако, сам Word остается запущенным для формирования следующих отчетов.

procedure Build;

Установить значение свободной переменной.

procedure SetValue(VariableName:string; Value:Variant);

Связать две соседние секции вместе
так чтобы они чередуясь выводили записи из одного и того же набора данных (НД).
Однако, для этого требуется указать целочисленное поле в НД,
значение которого и будет определять, какую именно секцию использовать для вывода текущей записи.

procedure JoinBands(BandKeyField:string; BandName1:string; KeyValue1:integer; BandName2:string; KeyValue2:integer);

События

Событие наступает после прочтения структуры документа-шаблона,
т.е. тогда, когда имена всех секций (bands) и переменных уже определены, но
этим переменным еще не установлены значения или поля набора данных (НД).

OnReadMaket:TNotifyEvent;

Объект секции документа (TDataBand)

Свойства времени выполнения

Имя секции документа.

Name: string

Имя поля для переменной VarName.

Field[VarName:string]: string

Формат вывода переменной.

Format[VarName:string]: string

Методы

Подключить набор данных к секции.
Параметром служит указатель на TDataset, т.е. вызов производится так:
AssignDataSet(@IBQuery1) или AssignDataSet(@ADOTable1).

procedure AssignDataSet(aDataSet: PDataSet);

Установить переменной VariableName из секции поле FieldName набора данных, подключенного к этой секции.
Если поле содержит действительные числа, то лучше использовать маску формата для их отображения,
например %10.2f (полный список форматов см. в описании функции SysUtils.Format).
Во всех других случаях значение параметра формат можно оставить пустой строкой, т.к. это ни на что не повлияет

procedure SetField(VariableName,FieldName,Format:string);

Обработка события OnReadMaket

Так как каждый шаблон уникален и содержит свой неповторимый набор секций и переменных, то и связывание этих секций и переменных со своими
значениями также процесс уникальный, а потому не может быть до конца автоматизирован внутри самого компонента. Эту задачу предстоит решить пользователю компонента.

Специально для этой цели и было создано данное событие.
В обработчике этого события НЕОБХОДИМО произвести связывание:

  • имен переменных с их значениями (TWordReport.SetValue),
  • секций с их заполненными наборами данных (TDataBand.AssignDataset),
  • переменных в секции с их полями (TDataBand.SetField),
  • а также, если это нужно — формирование групп секций (TWordReport.JoinBands)

Подробнее об этом в примере приложения.

Иные версии Delphi

Если нужен компонент для другой версии Delphi — используйте исходные коды, чтобы собрать пакет на своей Delphi, а затем скопируйте его:

  1. 32-битную release версию bpl — в поддиректорию bin
  2. 64-битную release bpl — в bin64
  3. 32-битную release версию dcu вместе с файлами WordReport.dcr и wrtprogress.dfm в libwin32release.
  4. 64-битную release версию dcu вместе с файлами WordReport.dcr и wrtprogress.dfm в libwin64release.
  5. Отладочные DCU, если они нужны, копируются в libwin32debug и libwin64debug уже без файлов ресурсов.

Создание отчета в MS Word

(Пример для Delphi 1.0 поскольку в Delphi 2-3 лучше использовать:

var MsWord : variant;
MsWord := CreateOleObject('Word.Basic'); // Для Delphi 3, пример ниже

Создавать отчет в программе Word удобно если отчет имеет сложную структуру (тогда его быстрее создать в Word, чем в Qreport от Delphi, кроме того, этот QReport имеет «глюки»), либо, если после создания отчета его нужно будет изменять. Итак, первым делом в Word создается шаблон будущего отчета, это самый обыкновенный не заполненный отчет. А в места куда будет записываться информация нужно поставить метки. Например (для наглядности метки показаны зеленым цветом, реально они конечно не видны):

Накладная

No Поставщик Наименование товара Код товара Кол-во Цена Сумма
1            
Сдал_______________________          Принял________________________
             М.П.                                    М.П.

Далее в форму, откуда будут выводиться данные, вставляете компоненту DdeClientConv из палитры System. Назовем ее DDE1. Эта компонента позволяет передавать информацию между программами методом DDE. Свойства:

ConnectMode : ddeManual — связь устанавливаем вручную

DdeService : (winword) — с кем устанавливается связь

ServiceApplication : C:MSOfficeWinwordWINWORD.EXE — полный путь доступа к программе. (Вот здесь можно наступить на грабли. Ведь Word может лежать в любой папке! Поэтому путь доступа к нему лучше взять из реестра, а еще лучше использовать OLE см.начало раздела)

Теперь пишем процедуру передачи данных:

{ Печать накладной }
procedure Form1.PrintN;
Var
    S          : string;
    i          : integer;
    Sum        : double;  {итоговая сумма, кстати,совет: не пользуйтесь типом real!}
    Tv, Ss     : PChar;
begin
 S:=GetCurrentDir+'Накладная.doc'; { имя открываемого документа }
 DDE1.OpenLink; { устанавливаем связь }
 Tv:=StrAlloc(20000); Ss:=StrAlloc(300); { выделяем память }
  { даем команду открыть документ и установить курсор в начало документа }
 StrPCopy(Tv, '[FileOpen "'+S+'"][StartOfDocument]');
 S:=NNakl.Text; { номер накладной }
  { записываем в позицию Num номер накладной }
 StrCat(Tv, StrPCopy(SS, '[EditBookmark .Name = "Num", .Goto][Insert "'+S+'"]'+
 '[EditBookmark .Name = "Table", .Goto]'); { и переходим к заполнению таблицы }
  { передаем данные в Word }
 if not DDE1.ExecuteMacro(Tv, false) then
   begin { сообщаем об ошибке и выход }
    MessageDlg('Ошибка связи с Microsoft Word.', mtError, [mbOk], 0);
    StrDispose(Tv); StrDispose(Ss);
    exit;
   end;

  { Заполняем таблицу }
 Sum:=0; Nn:=0;
 for i:=0 to TCount do
 begin
  inc(Nn);
  { предполагаем, что данные находятся в массиве T }
  StrPCopy(Tv, '[Insert "'+IntToStr(Nn)+'"][NextCell][Insert "'+T[i].Company+'"]'+
   '[NextCell][Insert "'+T.TName+'"][NextCell][Insert "'+T.Cod+'"][NextCell]'+
   '[Insert "'+IntToStr(T.Count)+'"][NextCell]'+
   '[Insert "'+FloatToStr(T.Cena)+'"][NextCell]'+
   '[Insert "'+FloatToStr(T.Count*T.Cena)*+'"][NextCell]'));
  inc(Nn);
  Sum:=Sum+(T.Count*T.Cena); { итоговая сумма }
  if not DDE1.ExecuteMacro(Tv, false)
   then begin
    MessageDlg('Ошибка связи с Microsoft Word.', mtError, [mbOk], 0);
    exit;
   end;
 end;

 { Записываем итоговую сумму }
 StrPCopy(Tv,
  '[NextCell][Insert "Итого"][NextCell][NextCell][NextCell]'+
  '[Insert "'+FloatToStr(Sum)+'"]'));
 if not DDE1.ExecuteMacro(Tv, false)
  then MessageDlg('Ошибка связи с Microsoft Word.', mtError, [mbOk], 0)
  else MessageDlg('Акт удачно создан. Перейдите в Microsoft Word.',
        mtInformation, [mbOk], 0);

 StrDispose(Tv); StrDispose(Ss);
end;

Для Delphi 2 и выше

Пример проверен только на русском Word 7.0! Может, поможет…

unit InWord;
interface
uses
  ... ComCtrls; // Delphi3
  ... OLEAuto;  // Delphi2
[skip]
procedure TPrintForm.MPrintClick(Sender: TObject);
var W: Variant;
    S: String;
begin
  S:=IntToStr(Num);
  try // А вдруг где ошибка :)
    W:=CreateOleObject('Word.Basic');
    // Создаем документ по шаблону MyWordDot
    // с указанием пути если он не в папке шаблонов Word
    W.FileNew(Template:='C:MyPathDBMyWordDot',NewTemplate:=0);
    // Отключение фоновой печати (на LJ5L без этого был пустой лист)
    W.ToolsOptionsPrint(Background:=0);

    // Переходим к закладке Word'a 'Num'
    W.EditGoto('Num'); W.Insert(S);
    //Сохранение
    W.FileSaveAs('C:MayPathReportsMyReport')
    W.FilePrint(NumCopies:='2'); // Печать 2-х копий
  finally
    W.ToolsOptionsPrint(Background:=1);
    W:=UnAssigned;
  end;
end;
{.....}

И еще, как определить установлен ли на компьютере Word, запустить его и загрузить в него текст из программы?

var
 MsWord: Variant;
...
try
 // Если Word уже запущен
 MsWord := GetActiveOleObject('Word.Application');
 // Взять ссылку на запущенный OLE объект
 except
  try
  // Word не запущен, запустить
  MsWord := CreateOleObject('Word.Application');
  // Создать ссылку на зарегистрированный OLE объект
  MsWord.Visible := True;
   except
    ShowMessage('Не могу запустить Microsoft Word');
    Exit;
   end;
  end;
 end;
...
MSWord.Documents.Add; // Создать новый документ
MsWord.Selection.Font.Bold := True; // Установить жирный шрифт
MsWord.Selection.Font.Size := 12; // установить 12 кегль
MsWord.Selection.TypeText('Текст');

По командам OLE Automation сервера см. help по Microsoft Word Visual Basic.

Остальные Вопросы

   В этой статье я хочу поговорить об очередной интересной полезности, такой как создание отчетности в Microsoft Word. Недавно передо мной стала задача, автоматизировать процесс заключения договоров у меня на работе, и последующего их хранения и учета в электронном виде.
   Пришлось перелопатить конечно много информации, но на выходе неожиданно для самого себя получилось очень простое,
компактное и я бы даже сказал универсальное решение, которым бы я хотел поделиться.
   Многие кто по долгу работы сталкивается с заключением договоров, знает, что даже работая с типовыми их формами, все равно возникают сложности, на предмет, то пропустишь, и забудешь вбить какие то данные, то вобьешь их не туда куда надо, бывает даже и такое, когда у тебя очередь и буквально разрываешься на части, и о возможности сконцентрироваться, чтобы качественно обслужить текущего клиента, можно только мечтать. Ошибся, переписываешь все заново, опять куча потерянного времени, а особенно под конец дня вообще, жесть. В общем решил я положить этому всему край, и в очередной выходной занялся реализацией задуманного.
Задача стояла следующая, необходимо было создать некую форму документа с полями, куда бы я мог вбивать данные по договору, а потом, по нажатию клавиши смог бы либо сохранить, либо распечатать, уже готовый договор.
   Полный листинг получившегося приложения я приводить, здесь не буду по скольку всего очень много, а небольшой примерчик доступный для понимания мы рассмотрим, тем паче, что подробно рассмотрев его, любой из вас самостоятельно сможет создавать фишки с отчетностью даже еще круче чем получилось у меня.

   Итак приступим:

  Работать мы будем с шаблоном документа MS Word, для этого создаем вордовский документ,
набираем в нем форму нашего договора, только вместо данных, которые мы будем перемещать в документ из нашей формы, впишем имена произвольных переменных. Допустим вместо даты, напишем слово date, Ф.И.О исполнителя — «FIO1«, Ф.И.О заказчика — «FIO2» и.т.д.
   Чтобы легче было ориентироваться, ниже привожу скринчик, ориентировочного шаблона:

   Почему именно так и для чего все это надо, забегая немного вперед поясню. В дальнейшем, 

мы с вами, напишем функцию, которая будет искать эти значения и заменять на то что мы будем вводить в поля нашей формы. Вот так все просто). Записываем в Edit1, Ф.И.О. клиента , функция находит абзац, находит слово FIO2, и заменяет его на содержимое из нашего Edit1.   

   Далее, когда шаблон готов, сохраняем документ в формате .dot присваиваем ему какое нибудь имя, (я например свой назвал просто — «Договор«) и для удобства работы сохранил его на диск D:\ 

   Все с шаблоном разобрались, теперь переходим непосредственно к программированию.

1) Создаем новый проект и помещаем на форму ряд компонентов (как на скрине ниже),

подключаем в Uses библиотеку ComObj, объявляем глобальные переменные:

var 
Dogovor,W:Variant;
Text:String; 

и приступим к настройке компонентов на форме:

а) Кликаем компонент SaveDialog1, переходим к инспектору объектов, где в его свойстве
Filter — прописываем Документ Microsoft Word|*.doc ;
б) В список Items компонента Combobox1, записываем какие нибудь города, например тот где вы живете или работаете). Свойство ItemIndex устанавливаете в 0;

2) Теперь создадим функцию, о которой мы говорили выше, она будет искать и осуществлять замену, для этого в разделе private пропишем:
function Repl(Atx, B, C:String):String;

нажимаем Ctrl+Shift+C и описываем ее:

function TForm3.Repl(Atx, B, C: String):String; 
var 
F1,F2,F3:String; 
begin 
F1:=»; 
F2:=Atx; 
F3:=Atx; 
while 
Pos(B, F2)>0 do 
begin 
F2:=Copy(F2, Pos (B, F2), (Length(F2)- Pos(B, F2))+1); 
F1:=Copy(F3, 1, Length(F3) — Length(F2))+C; 
delete(F2, Pos (B, F2), Length(B)); 
F3:=F1+F2; 
end; 
result:=F3; 
end; 

 3) В событии OnCreate на форме, внесем настройки в DateTimePicker1 и DateTimePicker2, чтобы при запуске они показывали текущую дату:

procedure TForm1.FormCreate(Sender: TObject); 
begin 
DateTimePicker1.Date:=Now; 
DateTimePicker2.Date:=Now; 
end;


4) В событии OnClick, на кнопке «Печать» пишем:

procedure TForm1.Button1Click(Sender: TObject);
var
Text :String;
begin
D:=CreateOleObject(‘Word.Application’); //Создаем OLE объект;
D.Documents.Open(‘D:Договор.dot’);  //Загружаем для работы наш шаблон договора;
D.Options.CheckSpellingAsYouType:=false; //Отключение правописания и
D.Options.CheckGrammarAsYouType:=false;  //грамматики;



W:=D.Documents.Item(1);


text:=W.Paragraphs.Item(1).Range.Text; //Замена Num, на содержимое Edit1;
text:=repl(text,’Num’,Edit1.Text);
W.Paragraphs.Item(1).Range.Text:=text;


text:=W.Paragraphs.Item(3).Range.Text;  //Замена Gorod в договоре на содержимое  
text:=repl(text,’Gorod’,Combobox1.Text);  //Combobox1;
W.Paragraphs.Item(3).Range.Text:=text;


text:=W.Paragraphs.Item(3).Range.Text; //Замена Date1 в договоре на содержимое Q;
text:=repl(text,’Date1′,DateToStr(DateTimePicker1.Date));
W.Paragraphs.Item(3).Range.Text:=text;


text:=W.Paragraphs.Item(5).Range.Text; //Замена FIO1, на содержимое Edit2;
text:=repl(text,’FIO1′,Edit2.Text);
W.Paragraphs.Item(5).Range.Text:=text;


text:=W.Paragraphs.Item(7).Range.Text; //Замена FIO2, на содержимое Edit5;
text:=repl(text,’FIO2′,Edit5.Text);
W.Paragraphs.Item(7).Range.Text:=text;


text:=W.Paragraphs.Item(12).Range.Text; //Замена Tovar, на содержимое Edit6;
text:=repl(text,’Tovar’,Edit6.Text);
W.Paragraphs.Item(12).Range.Text:=text;


text:=W.Paragraphs.Item(14).Range.Text; //Замена Addr, на содержимое Edit3;
text:=repl(text,’Addr’,Edit3.Text);
W.Paragraphs.Item(14).Range.Text:=text;


text:=W.Paragraphs.Item(15).Range.Text; //Замена Tel, на содержимое Edit4;
text:=repl(text,’Tel’,Edit4.Text);
W.Paragraphs.Item(15).Range.Text:=text;


text:=W.Paragraphs.Item(18).Range.Text; //Замена Price, на содержимое Edit7;
text:=repl(text,’Price’,Edit7.Text);
W.Paragraphs.Item(18).Range.Text:=text;


text:=W.Paragraphs.Item(23).Range.Text; //Замена Date2, на содержимое DateTimePicker2;
text:=repl(text,’Date2′,DateToStr(DateTimePicker2.Date));
W.Paragraphs.Item(23).Range.Text:=text;


text:=W.Paragraphs.Item(27).Range.Text; //Замена FIO3, на содержимое Edit5;
text:=repl(text,’FIO3′,Edit5.Text);
W.Paragraphs.Item(27).Range.Text:=text;


text:=W.Paragraphs.Item(27).Range.Text; //Замена FIO4, на содержимое Edit2;
text:=repl(text,’FIO4′,Edit2.Text);
W.Paragraphs.Item(27).Range.Text:=text;


if PrintDialog1.Execute then  //Печать документа;
D.ActiveDocument.PrintOut;


D.Application.ActiveDocument.Close(false); //Закрываем документ, не сохраняем изменения;
D.Quit; //Освобождаем переменную;
end;

5) В событии OnClick, на кнопке «Сохранить» пишем все тоже самое, только в самом начале, после begin, зададим параметр FileName для компонента SaveDialog1, чтобы при сохранении договоров, в имя файла автоматом вбивался номер и дата документа). А в конце опишем опции сохранения, вместо опций печати:

procedure TForm1.Button1Click(Sender: TObject);
var
Text :String;
begin


SaveDialog1.FileName:=(‘Договор ‘+’№ ‘+Edit1.Text+’ от  ‘+DateToStr(DateTimePicker1.Date)+’.doc’);


D:=CreateOleObject(‘Word.Application’); //Создаем OLE объект;
D.Documents.Open(‘D:Договор.dot’);  //Загружаем для работы наш шаблон договора;
D.Options.CheckSpellingAsYouType:=false; //Отключение правописания и
D.Options.CheckGrammarAsYouType:=false;  //грамматики;


W:=D.Documents.Item(1);


text:=W.Paragraphs.Item(1).Range.Text; //Замена Num, на содержимое Edit1;
text:=repl(text,’Num’,Edit1.Text);
W.Paragraphs.Item(1).Range.Text:=text;


text:=W.Paragraphs.Item(3).Range.Text; //Замена Gorod в договоре на содержимое    
text:=repl(text,’Gorod’,Combobox1.Text); //Combobox1;
W.Paragraphs.Item(3).Range.Text:=text;


text:=W.Paragraphs.Item(3).Range.Text; //Замена Date1 в договоре на содержимое Q;
text:=repl(text,’Date1′,DateToStr(DateTimePicker1.Date));
W.Paragraphs.Item(3).Range.Text:=text;


text:=W.Paragraphs.Item(5).Range.Text; //Замена FIO1, на содержимое Edit2;
text:=repl(text,’FIO1′,Edit2.Text);
W.Paragraphs.Item(5).Range.Text:=text;


text:=W.Paragraphs.Item(7).Range.Text; //Замена FIO2, на содержимое Edit5;
text:=repl(text,’FIO2′,Edit5.Text);
W.Paragraphs.Item(7).Range.Text:=text;


text:=W.Paragraphs.Item(12).Range.Text; //Замена Tovar, на содержимое Edit6;
text:=repl(text,’Tovar’,Edit6.Text);
W.Paragraphs.Item(12).Range.Text:=text;


text:=W.Paragraphs.Item(14).Range.Text; //Замена Addr, на содержимое Edit3;
text:=repl(text,’Addr’,Edit3.Text);
W.Paragraphs.Item(14).Range.Text:=text;


text:=W.Paragraphs.Item(15).Range.Text; //Замена Tel, на содержимое Edit4;
text:=repl(text,’Tel’,Edit4.Text);
W.Paragraphs.Item(15).Range.Text:=text;


text:=W.Paragraphs.Item(18).Range.Text; //Замена Price, на содержимое Edit7;
text:=repl(text,’Price’,Edit7.Text);
W.Paragraphs.Item(18).Range.Text:=text;


text:=W.Paragraphs.Item(23).Range.Text; //Замена Date2, на содержимое DateTimePicker2;
text:=repl(text,’Date2′,DateToStr(DateTimePicker2.Date));
W.Paragraphs.Item(23).Range.Text:=text;


text:=W.Paragraphs.Item(27).Range.Text; //Замена FIO3, на содержимое Edit5;
text:=repl(text,’FIO3′,Edit5.Text);
W.Paragraphs.Item(27).Range.Text:=text;


text:=W.Paragraphs.Item(27).Range.Text; //Замена FIO4, на содержимое Edit2;
text:=repl(text,’FIO4′,Edit2.Text);
W.Paragraphs.Item(27).Range.Text:=text;


if SaveDialog1.Execute then  //Печать документа;
begin
D.ActiveDocument.SaveAs(SaveDialog1.FileName, $00000000);
ShowMessage(‘Файл сохранен в следующей директории: ‘+SaveDialog1.FileName);
end;


D.Application.ActiveDocument.Close(false); //Закрываем документ, не сохраняем изменения;
D.Quit; //Освобождаем переменную D;
end;

   Теперь можно запускать проект и любоваться результатом). Если все делалось в той последовательности, как описано в статье, то все должно работать как часы, ибо пример рабочий на все 100%, а код копировался прямо из листинга.

 
   Великовата получилась статейка, но из песни слов не выкинешь, а дорогу осилит идущий).

Кстати:


Если необходимо, можно установить ориентацию листа:

D.Application.Selection.PageSetup.Orientation:= $00000000; //Ориентацияпортрет; если нужен ландшафт — $00000001;

и размеры полей:

D.Application.Selection.PageSetup.LeftMargin:=56; //отступ слева «2,5 сантиметра«;
D.Application.Selection.PageSetup.RightMargin:=28; //отступ справа «1,0 сантиметр«;
D.Application.Selection.PageSetup.TopMargin:=28;  //отступ сверху «1.0 сантиметр«;
D.Application.Selection.PageSetup.BottomMargin:=42; //отступ снизу «1,5 сантиметра«;




Единицы измерения — пункты (1 см = 28,35 п).

Вот так, например можно установить жирность шрифта, размер и выравнивание:

W.Paragraphs.Item(?).Range.Font.Bold:=True;

W.Paragraphs.Item(?).Range.Font.Size:=13;
W.Paragraphs.Item(?).Alignment:=$00000000; //Выравнивание по левому краю; если нужно
по центру — $00000001; и т.д. значения некоторых констант приведены ниже:

wdAlignParagraphLeft = 000000;
wdAlignParagraphCenter = 000001;
wdAlignParagraphRight = 000002;
wdAlignParagraphJustify = 000003;
wdAlignParagraphDistribute = 000004;
wdAlignParagraphJustifyMed = 000005;
wdAlignParagraphJustifyHi = 000007;
wdAlignParagraphJustifyLow = 000008;
wdAlignParagraphThaiJustify = 000009;



Чтобы было удобней пользоваться программой в последующем, файл шаблона можно поместить в папке с экзешником проекта. А для обращения к нему воспользоваться следующим кодом:

var

FName, Path:String

begin

FName:=’Договор.dot’;

Path:=(ExtractFilePath(Application.ExeName)+»+FName);


D.Documents.Open(Path);






end;


Теперь куда бы ни был перемещен экзешник с программой, если вордовский шаблон находится с ним в одной папке, он будет без проблем открыт и использован. И не нужно никаких перезаписей путей в листинге.


16

Министерство
образования и науки РФ

Федеральное
государственное бюджетное образовательное
учреждение

высшего
профессионального образования

«Тульский
государственный университет»

Политехнический
институт

Кафедра
«Автоматизированные станочные системы»

Методические указания к

лабораторной
работе №3

Вывод
в
MS
Word

по дисциплине

ПРОГРАММИРОВАНИЕ

Направление
подготовки: 230100 Информатика и вычислительная
техника

Профиль
подготовки: Системы
автоматизированного проектирования

Формы
обучения очная,
очно-заочная, заочная

Тула
2011 г.

Методические указания к
лабораторным работам составлены доц.
А.В.Анцевым
и обсуждены на заседании
кафедры «Автоматизированные станочные
системы»
механико-технологического
факультета

протокол
 1 
от « 31 »
  августа  
2011 
г.

Зав.
кафедрой________________А.Н. Иноземцев

Методические
указания к лабораторным работам
пересмотрены и утверждены на заседании
кафедры «Автоматизированные станочные
системы»
механико-технологического
факультета

протокол №___
от «___»____________ 20___ г.

Зав.
кафедрой________________ А.Н. Иноземцев

  1. Что такое сом-технология

Все
поломки случаются в тот день, когда у
техника выходной.

Из
законов Мэрфи

При
решении многих задач часто можно
обнаружить, что «все уже сделано за
нас»: существуют программы, выполняющие
необходимые нам функции. Например, если
вашей программе надо выводить данные
«в красивом виде» и с возможностью
печати, то с такой задачей прекрасно
справляется Microsoft Word.
Если необходимо выводить график или
векторное изображение, то для этого
прекрасно подходит AutoCAD
и т.д. Программирование в своей задаче
вывода текста и графики на печать или
своего векторного редактора – дело
трудоемкое и неблагодарное, а главное,
бессмысленное: все равно лучше, чем в
Word и AutoCAD,
вряд ли получится.

Для
использования мощных возможностей
«чужих» программ в собственных
разработках в ОС Windows
применяется так называемая COM-технология.
COM означает Common
Object Interface
(общий интерфейс объектов). Идея
COM-технологии состоит в
том, что ваша программа может импортировать
так называемую библиотеку типов внешнего
приложения и получить доступ к используемым
в этом приложении объектам, их свойствам
и методам. Вызывая на выполнение из
своей программы (СОМ-клиента) методы
объектов другой программы (СОМ-сервера),
можно выполнять самые разнообразные
функции. При этом сам СОМ-сервер в явном
виде не запускается: можно сформировать
сложный документ в Word и
сохранить его в файле совершенно
незаметно для пользователя.

Рис. 1 – Создание
приложения по СОМ-технологии.

СОМ-серверами
являются большинство распространенных
программ: все компоненты пакета Microsoft
Office всех версий, AutoCAD,
Компас, Visio, SolidWorks…
Таким образом, создатели этих программ
предусматривают использование
функциональности, заложенной в их
творения, сторонними разработчиками.
Осталось научиться это делать.

  1. Вывод отчета при помощи Microsoft Word

У
бюрократов поток бумаг тем обильнее,
чем больше времени

тратится
на отчеты о все меньшем объеме работы.

Из
законов Мэрфи

Рассмотрим
использование СОМ-технологии на примере.
Пусть в результате работы нашей программы
вычисляется среднее арифметическое
матрицы вещественных чисел 5×6 элементов.
Мы хотим получить результаты работы в
виде красивого отчета (doc-файла)
примерно следующего вида (Рис. 2).

Результаты
работы гениальной программы студента
Б. Гейтса

Исходная
матрица:

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

Среднее
арифметическое элементов матрицы
S=1.00

Рис. 2 – Ожидаемый
вид отчета.

Стандартными
средствами Delphi получить
такой документ, пригодный к распечатке,
весьма затруднительно. Пришлось бы
отрисовывать его вручную, по клеточке,
на объекте Tcanvas
и потом развлекаться весьма трудоемким
программированием взаимодействия вашей
программы с очередью печати Windows.
Мы пойдем другим путем – переложим всю
трудоемкую работу по форматированию
на Microsoft Word.
В результате совместных усилий нашей
программы и Word мы получим
doc-файл на диске, который
счастливый пользователь сможет загрузить
в тот же Word, просмотреть,
распечатать и т.д.

Соседние файлы в папке 2 семестр

  • #
  • #
  • #
  • #

  
К статье прилагаются исходный
код примера и база данных. Выполнены в
Delphi 7 и Access 2003 соответственно.
 


Экспорт из БД в Word

 
   Статья написана на основе собственного опыта, и опыта других людей, полученного при общении в конференциях. И надеюсь, кому-нибудь хоть сколько поможет.
   В статье приведены примеры работы с:
   — таблицами;

  
— закладками;

   — колонтитулами;

   — надписями;

   — шрифтами;

   — курсором.

 
   Не буду углубляться в особенности позднего и раннего связывания, т.к. информации на эту тему уже достаточно.
   Итак, стоит задача экспортировать некую таблицу в Word. Даже не так, стоит задача сформировать сложный документ с колонтитулами таблицами заголовками и т.д., и т.п.
   Есть два замечательных способа добывать информацию об интерфейсе Word:
   — для того чтобы узнать, как что-то сделать из Delphi в Word-e надо в Word-е зайти в меню Сервис/Макрос/Начать запись… Потом сделать в Word-e то, что надо сделать из Delphi и закончить запись макроса. И наконец Сервис/Макрос/Макросы… выбираем записанный… Изменить и смотрим, как он устроен. После этого
перевод синтаксиса VBA в синтаксис Delphi
осуществляется просто и непринужденно;
  — еще одним хорошим инструментом получения знаний являются компоненты типа TWordApplication. Кидаем его на форму, в любом операторе набираем WordApplication1., нажимаем
«Сtrl + Пробел» и внимательно читаем. Смысл доступных функций и свойств обычно понятен интуитивно.
   Есть, еще один, как мне сказали, наиболее логичный способ – справка VBA, но что-то не довелось мне ею пользоваться…
   Теперь немного теории, добытой этими путями…


  

W1: TWordApplication;
Vr: OleVariant;


   У Wod есть коллекция документов:
  

Vr := номер нужного документа;
//так можно обратиться к нужному документу
w1.Documents.Item(vr);


   У всякой коллекции есть свойство count – количество таким образом
w1.Documents.count – количество документов.
   Использование переменной типа olevariant (в данном случае vr) иногда требуется, иногда нет.
   У документа есть свои коллекции:

  
   1. буквы (characters):

// так можно получить доступ
// к коллекции букв активного документа
w1.ActiveDocument.Characters


   Теперь можно получить доступ к:
 

w1.ActiveDocument.Characters.Items(vr) // конкретной букве
w1.ActiveDocument.Characters.Count // количеству букв


   2. таблицы:

 

W1.activedocument.tables


   А вот так:
 

W1.ActiveDocument.Tables.Item(
  W1.ActiveDocument.Tables.Count).Columns.Item(2).Select;


   можно выбрать вторую колонку последней таблицы.

   3. абзацы:


  

w1.ActiveDocument.Paragraphs


   4. фигуры:

  

W1.ActiveDocument.Shapes


   Ну и т.д….
   Еще есть полезные объекты selection – выбранная область и range – диапазон, а так-же функция select – выбрать. Выбрать можно таблицу, колонку (опять же W1.ActiveDocument.Tables.Item(W1.ActiveDocument.Tables.Count).Columns.Item(2).Select), букву, диапазон, абзац и т.д

   Кстати, очень часто приходится работать с объектом 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. Без использования шаблона.
   Создается новый документ, где создаются колонтитулы надписи и т.д.
  
Этот путь использовать не стоит т.к. на разных компьютерах могут изначально стоять разные настройки страницы, шрифтов, абзацев и красиво сформированный документ у Вас на компьютере может ужасно выглядеть на другом. Можно, конечно, программно выставлять все необходимые настройки, но, во-первых, всего не учтешь, во-вторых это – большие тормоза и много кода.
   2. С использованием шаблона.
   Суть состоит в том, что заранее создается вордовский документ, который используется как шаблон. В нашем случае в шаблон в нужном месте в центре первой страницы ставим объект типа надпись, в котом выставляем нужный нам шрифт и выравнивание. Также делаем разрыв раздела. На второй странице в верхний колонтитул тоже вставляем объект надпись. В него мы будем вставлять название документа. Далее делаем макрос, в котором повторяем действия, которые нам нужно сделать из
Делфи:
   — в объект надпись на первой странице внести текст;
   — перейти в конец документа;
   — перейти в верхний колонтитул;
   — в объект надпись в колонтитуле внести текст;
   — вернуться на вторую страницу.

   Получаем следующий макрос:


 

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:DatawareDeplhi7Для статьи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;


   которые убирают соответствующие менюшки, которые обычно разлетаются по всей форме.
  
Если работать через CreateOleObject, то чтобы получить значение констант VisualBasic, которые Дельфи вообще говоря не понимает, надо внутри макроса написать MsgBox(нужная_константа). Тогда он покажет ее численное значение, их-то в Дельфи и использовать.
   Теперь привожу код всей программы:
  

unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes,
  Graphics, Controls, Forms, Dialogs, StdCtrls,
  DB, ADODB, OleServer, Word2000;
 
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;

Begin
{Процедура экспортирует лишь те записи датасета,
 у которых
значение последнего поля совпадает с FlagText
Если FlagText='' то экспортируются все записи.
Это связано с тем, что зачастую нужно разнести
в разные таблицы записи, полученные в результате
долго выполняемого запроса}
 
  Application.ProcessMessages;
  vr1:=wdStory;
  //переходим в конец документа
  w1.Selection.EndKey(vr1,EmptyParam); 
  //вставляем заголовок таблицы
  W1.ActiveDocument.Range(EmptyParam,EmptyParam).
                                            InsertAfter(Title);
  // далее идут настройки, что-бы заголовок не
  // отрывался от основной таблицы и все красиво выглядело
  W1.ActiveDocument.Paragraphs.Item(w1.
              ActiveDocument.Paragraphs.Count).Range.Select;
  W1.Selection.ParagraphFormat.KeepWithNext:=-1;
  W1.Selection.ParagraphFormat.SpaceAfter:=14;
  W1.Selection.Font.Size:=15;   //применяем шрифт
  W1.Selection.Font.bold:=1;
  //добавляем строчку
  //выбираем ее
  W1.ActiveDocument.Paragraphs.Add(EmptyParam);
  W1.ActiveDocument.Paragraphs.Item(w1.
              ActiveDocument.Paragraphs.Count).Range.Select;
  W1.Selection.ParagraphFormat.SpaceAfter:=0;
  vr1:=wdStory;
  //переходим в конец документа
  w1.Selection.EndKey(vr1,EmptyParam);
  // запоминаем положение курсора. Это - начало
  // будущей таблицы.
  // потом выберем весь оставшийся текст, чтобы
  // преобразовать его в таблицу
  // В ворде есть такая фунция "Преобразовать в
  // таблицу" ею и воспользуемся
  TableBeg:=W1.Selection.End_;
  DataSet.First;
  //вставляем заголовки для всех видимых полей
  for i:=0 to DataSet.FieldCount-1 do
   if DataSet.Fields[i].Visible then
    W1.ActiveDocument.Range(EmptyParam,EmptyParam).
     InsertAfter(convertstring(DataSet.Fields[i].
                                             DisplayLabel)+#9);
  Application.ProcessMessages;
  w1.Selection.EndKey(vr1,EmptyParam);
  //убираем последний символ табуляции
  {Вообще символ табуляции используется в качестве
  разделителя для столбцов таблиццы}
  w1.Selection.TypeBackspace;
  //применяем шрифт
  W1.ActiveDocument.Paragraphs.Item(w1.
    ActiveDocument.Paragraphs.Count).Range.Select;
  W1.Selection.Font.Size:=14;
  W1.Selection.Font.Italic:=1;
  W1.Selection.Font.bold:=0;
 
  //добавляем строчку
  W1.ActiveDocument.Paragraphs.Add(EmptyParam);
  // флаг для определения, были ли в таблице вообще
  // записи
  // для экспорта
  f:=true;
  // в эту строчку будем экспортировать текст
  // таблицы
  st:='';
  TableBeg2:=W1.Selection.End_; //начало данных в таблице
 
 if dataset.RecordCount>0 then begin
  Repeat
    Application.ProcessMessages;
    if (dataset.fields[DataSet.Fields.Count-1].
              AsString=FlagText) or (FlagText='')
      then begin
        // через табуляцию выводим все видимые поля
        for i:=0 to DataSet.FieldCount-1 do
         if DataSet.Fields[i].Visible
         then st:=st+DataSet.Fields[i].AsString+#9;
        // убираем последний символ табуляции
        SetLength(st,length(st)-1); 
        st:=st+#13;   // перенос строки
        f:=false;
           end;
    dataset.Next;
  until dataset.Eof;
  //уходим в конец текста
  w1.Selection.EndKey(vr1,EmptyParam);
  //вставляем данные таблицы
  W1.Selection.InsertAfter(convertstring(st));
  vr1:=TableBeg2;        //начало данных таблицы
  vr2:=W1.Selection.End_;  //конец таблицы
  W1.ActiveDocument.Range(vr1,vr2).Select;
  W1.Selection.Font.Size:=12;
  W1.Selection.Font.bold:=0;
  W1.Selection.Font.Italic:=0;
  end;
  //в том случае, если не экспортировалось ни одной
  // записи формируем пустую строчку
  if f then begin
    for i:=0 to DataSet.FieldCount-1 do
      if DataSet.Fields[i].Visible then
        W1.ActiveDocument.Range(EmptyParam,
             EmptyParam).InsertAfter(' '+#9);
    w1.Selection.EndKey(vr1,EmptyParam);
    w1.Selection.TypeBackspace;
            end;
   Application.ProcessMessages;
   vr1:=TableBeg; //начало будущей таблицы
   vr2:=W1.Selection.End_; //конец будущей таблицы
   //выбираем этот диапазон
   W1.ActiveDocument.Range(vr1,vr2).Select;
   //и преобразуем его в таблицу
   W1.Selection.ConvertToTable(EmptyParam,
      EmptyParam,
      EmptyParam,EmptyParam,EmptyParam,EmptyParam,
      EmptyParam,EmptyParam,EmptyParam,EmptyParam,
      EmptyParam,EmptyParam,EmptyParam,EmptyParam,
      EmptyParam,EmptyParam);
   colcount:=1;
   //выставляем ширины колонок
    for i:=0 to DataSet.FieldCount-1 do
      if DataSet.Fields[i].Visible then begin
        W1.ActiveDocument.Tables.Item(W1.
                          ActiveDocument.Tables.
          Count).Columns.Item(colcount).Width:=
                            DataSet.Fields[i].Tag;
        inc(colcount);
        Application.ProcessMessages;
     end;
 
  TableLineSet;
  // эта процедура прорисовывает нужные
  // границы таблицы
  W1.ActiveDocument.Paragraphs.Add(EmptyParam);
  W1.ActiveDocument.Paragraphs.Item(w1.
   ActiveDocument.Paragraphs.Count-1).Range.Select;
  W1.Selection.ParagraphFormat.KeepWithNext:=0;
End;
 
// эта процедура прорисовывает нужные границы таблицы
Procedure TForm1.TableLineSet; 
Begin
  w1.Selection.Cells.Borders.Item(wdBorderLeft).
   LineStyle:=wdLineStyleSingle;
  w1.Selection.Cells.Borders.Item(wdBorderRight).
   LineStyle:=wdLineStyleSingle;
  w1.Selection.Cells.Borders.Item
 (wdBorderHorizontal).LineStyle:=wdLineStyleSingle;
  w1.Selection.Cells.Borders.Item(wdBorderTop).
   LineStyle:=wdLineStyleSingle;
  w1.Selection.Cells.Borders.Item(wdBorderBottom).
   LineStyle:=wdLineStyleSingle;
  w1.Selection.Cells.Borders.Item
   (wdBorderVertical).LineStyle:=wdLineStyleSingle;
End;
 
procedure TForm1.WInit;
Begin
 // для избежания глюков полезно убивать
 // используемые компоненты
 // и потом их создавать заново...
 W1.free;
 W1:=TWordApplication.Create(Form1);
 //Чтобы всегда новое приложение запускалось
 w1.connectkind:=ckNewInstance;
End;
 
procedure TForm1.Button2Click(Sender: TObject);
var
  vr1,vr2,vr3,vr4,vr5:OleVariant;
begin
  vr1:=0;
  vr2:=false;
  vr3:='Provider=Microsoft.Jet.OLEDB.4.0;
   Password="""";
   User ID=Admin;Data
   Source=C:DatawareDeplhi7Для статьи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';
  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;
end;
 
end.


   Если кто-то увидит в данной статье свои приемы, которые я здесь описал, пожалуйста, не обижайтесь :).
 

Дата: 12.01.2006,
Автор:
Глущенко
Юрий, aka YurikGL.

Like this post? Please share to your friends:
  • Delphi проверить открыт или нет excel
  • Delphi выбрать весь лист в excel
  • Delphi приложение для excel
  • Delphi все листы excel в один лист
  • Delphi весь текст word