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 и выше.
Установка
- Извлечь файлы из WordReport(Version).zip в директорию с установленной Delphi (например, WordReport70.zip в «C:Program Files (x86)BorlandDelphi7»).
- Запустить Delphi.
- Выбрать пункт меню Component >> Install Packages…
- Нажать кнопку Add… и выбрать файл WordReport(Version).bpl в DelphiBin (например, WordReport70.bpl в «C:Program Files (x86)BorlandDelphi7BinWordReport70.bpl»)
- Компонент готов к работе. Его можно найти на вкладке WordReport.
Инструкция
Правила создания шаблонов
Шаблон в нашем случае — это документ MS Word (именно документ — т.е. файл *.doc, а не *.dot !), составленный по определенным правилам.
-
Секция — это диапазон шаблона, который должен повторяться в результирующем документе столько раз, сколько требуется для вывода всех записей привязанного к секции набора данных.
-
Каждая секция должна быть отмечена закладкой с именем DataN, где N — целое число от 1 до 8.
Повторяться будет ТОЛЬКО то, что в диапазоне закладки, поэтому закладкой лучше отмечать всю строку документа целиком. Если секция используется
для повторения строки таблицы, то отмечать закладкой следует также всю строку документа, в которой находится эта строка таблицы. -
Существует три категории переменных шаблона:
-
переменные вне секций
Синтаксис объявления: #(ИмяСвободнойПеременной)
Способ определения значения: напрямую, методом SetValue -
переменные секций
Синтаксис объявления: #(ИмяСекции(ИмяСчетчика).ИмяПеременной)
Способ определения значения: из текущей записи привязанного поля набора данных. -
счетчики записей секций
Синтаксис объявления: #(ИмяСчетчика)
Объявление действительно только внутри секции.
Заменяется на текущий номер записи привязанного набора данных при отсутствии групп секций
или на номер записи в неразвывной последовательности при группировке секций.
-
ИмяСвободнойПеременной — ненулевая последовательность латинских букв, цифр и точек (только букв, цифр и точек, никаких других знаков!). Регистр букв не важен.
-
ИмяПеременной — ненулевая последовательность латинских букв и цифр. Регистр букв не важен.
-
ИмяСчетчика — ненулевая последовательность латинских букв и цифр. Регистр букв не важен.
-
ИмяСекции — ненулевая последовательность латинских букв и цифр. Регистр букв не важен.
-
Максимальное количество переменных в секции — 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, а затем скопируйте его:
- 32-битную release версию bpl — в поддиректорию bin
- 64-битную release bpl — в bin64
- 32-битную release версию dcu вместе с файлами WordReport.dcr и wrtprogress.dfm в libwin32release.
- 64-битную release версию dcu вместе с файлами WordReport.dcr и wrtprogress.dfm в libwin64release.
- Отладочные 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___ г.
Зав.
кафедрой________________ А.Н. Иноземцев
-
Что такое сом-технология
Все
поломки случаются в тот день, когда у
техника выходной.
Из
законов Мэрфи
При
решении многих задач часто можно
обнаружить, что «все уже сделано за
нас»: существуют программы, выполняющие
необходимые нам функции. Например, если
вашей программе надо выводить данные
«в красивом виде» и с возможностью
печати, то с такой задачей прекрасно
справляется Microsoft Word.
Если необходимо выводить график или
векторное изображение, то для этого
прекрасно подходит AutoCAD
и т.д. Программирование в своей задаче
вывода текста и графики на печать или
своего векторного редактора – дело
трудоемкое и неблагодарное, а главное,
бессмысленное: все равно лучше, чем в
Word и AutoCAD,
вряд ли получится.
Для
использования мощных возможностей
«чужих» программ в собственных
разработках в ОС Windows
применяется так называемая COM-технология.
COM означает Common
Object Interface
(общий интерфейс объектов). Идея
COM-технологии состоит в
том, что ваша программа может импортировать
так называемую библиотеку типов внешнего
приложения и получить доступ к используемым
в этом приложении объектам, их свойствам
и методам. Вызывая на выполнение из
своей программы (СОМ-клиента) методы
объектов другой программы (СОМ-сервера),
можно выполнять самые разнообразные
функции. При этом сам СОМ-сервер в явном
виде не запускается: можно сформировать
сложный документ в Word и
сохранить его в файле совершенно
незаметно для пользователя.
Рис. 1 – Создание
приложения по СОМ-технологии.
СОМ-серверами
являются большинство распространенных
программ: все компоненты пакета Microsoft
Office всех версий, AutoCAD,
Компас, Visio, SolidWorks…
Таким образом, создатели этих программ
предусматривают использование
функциональности, заложенной в их
творения, сторонними разработчиками.
Осталось научиться это делать.
-
Вывод отчета при помощи Microsoft Word
У
бюрократов поток бумаг тем обильнее,
чем больше времени
тратится
на отчеты о все меньшем объеме работы.
Из
законов Мэрфи
Рассмотрим
использование СОМ-технологии на примере.
Пусть в результате работы нашей программы
вычисляется среднее арифметическое
матрицы вещественных чисел 5×6 элементов.
Мы хотим получить результаты работы в
виде красивого отчета (doc-файла)
примерно следующего вида (Рис. 2).
Результаты Исходная
Среднее |
Рис. 2 – Ожидаемый
вид отчета.
Стандартными
средствами Delphi получить
такой документ, пригодный к распечатке,
весьма затруднительно. Пришлось бы
отрисовывать его вручную, по клеточке,
на объекте Tcanvas
и потом развлекаться весьма трудоемким
программированием взаимодействия вашей
программы с очередью печати Windows.
Мы пойдем другим путем – переложим всю
трудоемкую работу по форматированию
на Microsoft Word.
В результате совместных усилий нашей
программы и Word мы получим
doc-файл на диске, который
счастливый пользователь сможет загрузить
в тот же Word, просмотреть,
распечатать и т.д.
Соседние файлы в папке 2 семестр
- #
- #
- #
- #
К статье прилагаются исходный код примера и база данных. Выполнены в
Delphi 7 и Access 2003 соответственно.
Статья написана на основе собственного опыта, и опыта других людей, полученного при общении в конференциях. И надеюсь, кому-нибудь хоть сколько поможет.
В статье приведены примеры работы с:
— таблицами;
— закладками;
— колонтитулами;
— надписями;
— шрифтами;
— курсором.
Не буду углубляться в особенности позднего и раннего связывания, т.к. информации на эту тему уже достаточно.
Итак, стоит задача экспортировать некую таблицу в 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.