Экспорт текста в Word из Delphi в текстовый шаблон Word, с элементом построения таблиц, используя маркеры

Экспорт текста в Word из Delphi в текстовый шаблон Word, с элементом построения таблиц, используя маркеры

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

И тут я впервые задумался о конструкторе таблиц. Точнее о выводе таблиц и простого текста в WORD по маркерам в редактируемом шаблоне.

В общем хочу поделиться крохотным примером на эту тему. Показать как можно выводить столбцы таблицы по маркерам, выводить текст в основном документе и колонитулах.

Чем хорош этот метод – в шаблоне (рыбе) документа можно сразу указать размер, цвет, стиль текста и таблицы, и не париться программно. При этом шаблон может поправить сам пользователь, зная назначение маркера.

Накидываю в WORD`e шаблон следующего содержания

Маркеры я заключил в сиволы '%', программа будет их искать и заменять на нужный нам текст. Сохраняю его и ложу рядом с будущим исполняемым фалом программы. Что то типа «отчета за смену», идиотично, но что в голову ни чего не лезет, но суть не в этом.

В итоге должно получиться как то так:

Всю работу с WORD я использовал с помощью WordDocument и WordApplication со вкладки Server. Я использовал бородатый Delphi 7. В XE, если кто не найдет, это вкладка по умолчанию отключена, ей просто надо включить.

Для наглядности покажу, что я накидал на форму Скрин формы:

StrinGrid, Edit`ы, Label`ы к ним, и SpinEdit для регулирования количества строк в таблицы В общем то все эти заполненные поля будем передавать в Word.

Приведу код кнопки, которая соединяет с Word и производит с ним работу.

Спойлер procedure TForm1.BitBtn1Click(Sender: TObject); const // массивные маркеры marker: array[1..5] of string[15] = ('%п/п%', '%Опер%', '%затрат%', '%слж%', '%прбл%'); var FTrue, Ffalse, Template, NewTemplate, ItemIndex, T, D, DD, Replese_T, Find_T: OleVariant; Tbl, L, col, Tablwdline, con: OleVariant; i, ii, j: integer; TExtend: OleVariant;

begin Memo1.Clear; Memo1.Lines.Add('Соеденение с Word. '); Application.ProcessMessages; try WA1.Connect; WA1.Visible := False; Template := EmptyParam; NewTemplate := False; FTrue := true; Ffalse := false;

except ShowMessage('Не удалось соедениться с WORD!'); Memo1.Lines.Add('Не удалось соедениться с WORD!' ); Application.ProcessMessages; exit; end;

Memo1.Lines.Add('Соеденение с документом Word. '); Application.ProcessMessages;

// Добовляем документ из имеющего со статусом несохраненого нового документа

try T := ExtractFilePath(Application.ExeName)+'\document.docx';;

ItemIndex := WA1.Documents.Add(T, NewTemplate, NewTemplate, Template);

Memo1.Lines.Add('Не удалось соедениться с шаблоном WORD! (document.docx) Проверьте путь расположения файла.' ); Application.ProcessMessages; ShowMessage('Не удалось соедениться с шаблоном WORD! (document.docx) Проверьте путь расположения файла.'); WD1.Application.Selection.EndOf(Template, Template); WA1.Application.WindowState := wdWindowStateMaximize; WA1.Application.ScreenUpdating := true; WA1.Application.ScreenRefresh; WA1.Visible := true; WA1.Disconnect; WD1.Disconnect; exit; end;

try //Количество таблиц в документе и количество сток и слобцов col := WA1.ActiveDocument.Tables.Count;

for j := 1 to col do begin Memo1.Lines.Add('Присоеденяемся к таблице '+Inttostr( ); Application.ProcessMessages;

tbl := WA1.ActiveDocument.Tables.Item(j); //присоеденяемся к таблице

//ДОБАВЛЕНИЕ СТРОК Tbl.Select;

for ii := 1 to Length(marker) do begin Find_T := marker[ii]; // текст который меняем D := wdFindStop; // найти один раз DD := wdReplaceNone; // замена один раз Replese_T := ''; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam); if WA1.Selection.Text = marker[ii] then begin L := Stringgrid1.RowCount - 2; // количество вставляемых строк; try WD1.Application.Selection.SelectRow; WD1.Application.Selection.Borders.OutsideLineWidth := wdLineWidth050pt; except Memo1.Lines.Add('Конфликт оформления линий рамки!'); Application.ProcessMessages; // ShowMessage('Конфликт оформления линий рамки!'); end; if L > 1 then WA1.Selection.InsertRowsBelow(L) else L := 0;

try Tablwdline := wdRow; con := L; TExtend := wdMove; WD1.Application.Selection.EndOf(Tablwdline, TExtend); Tablwdline := wdline; con := L; TExtend := wdMove; WD1.Application.Selection.MoveUP(Tablwdline, con, TExtend); WD1.Application.Selection.SelectRow; TExtend := wdExtend; WD1.Application.Selection.MoveDown(Tablwdline, con, TExtend); // на строки ниже;> WD1.Application.Selection.Borders.OutsideLineWidth := wdLineWidth150pt; except Memo1.Lines.Add('Конфликт оформления линий рамки!'); Application.ProcessMessages; ShowMessage('Конфликт оформления линий рамки!'); end;

//ЗАМЕНА И ЗАПОНЕНИЕ

for ii := 1 to Length(marker) do begin Memo1.Lines.Add('Ищем маркеры '+ marker[ii]+' в таблице ' ); Application.ProcessMessages;

Tbl.Select; Find_T := marker[ii]; // текст который меняем D := wdFindStop; // найти один раз DD := wdReplaceNone; // замена один раз Replese_T := ''; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam); if WA1.Selection.Text = '%Опер%' then begin for i := 1 to Stringgrid1.RowCount - 1 do begin WD1.Application.Selection.TypeText(Stringgrid1.Cells[0, i]); Tablwdline := wdline; con := 1; WD1.Application.Selection.MoveDown(Tablwdline, con, EmptyParam); // на строку ниже; end; end;

if WA1.Selection.Text = '%затрат%' then begin for i := 1 to Stringgrid1.RowCount - 1 do begin WD1.Application.Selection.TypeText(Stringgrid1.Cells[1, i]); Tablwdline := wdline; con := 1; WD1.Application.Selection.MoveDown(Tablwdline, con, EmptyParam); // на строку ниже; end; end;

if WA1.Selection.Text = '%слж%' then begin for i := 1 to Stringgrid1.RowCount - 1 do begin WD1.Application.Selection.TypeText(Stringgrid1.Cells[2, i]); Tablwdline := wdline; con := 1; WD1.Application.Selection.MoveDown(Tablwdline, con, EmptyParam); // на строку ниже; end; end;

if WA1.Selection.Text = '%п/п%' then begin for i := 1 to Stringgrid1.RowCount - 1 do begin WD1.Application.Selection.TypeText(IntToStr(i)); Tablwdline := wdline; con := 1; WD1.Application.Selection.MoveDown(Tablwdline, con, EmptyParam); // на строку ниже; end; end;

if WA1.Selection.Text = '%прбл%' then begin for i := 1 to Stringgrid1.RowCount - 1 do begin WD1.Application.Selection.TypeText(Stringgrid1.Cells[3, i]); Tablwdline := wdline; con := 1; WD1.Application.Selection.MoveDown(Tablwdline, con, EmptyParam); // на строку ниже; end; end;

Tbl.Select; Find_T := '%объед%'; // текст который меняем D := wdFindStop; // найти один раз DD := wdReplaceNone; // замена один раз Replese_T := ''; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

if StringGrid1.RowCount > 2 then begin if WA1.Selection.Text = '%объед%' then begin Memo1.Lines.Add('Объеденяем строки в таблице. ' ); Application.ProcessMessages; Tablwdline := wdCharacter; Con := 1; WD1.Application.Selection.Delete(Tablwdline, con); //удалить выделенное con := StringGrid1.RowCount - 2; // tbl.Rows.Count - WD1.Application.Selection.Information[wdEndOfRangeRowNumber]; //количество строк в таблице - номер строки в которой находится курсор Tablwdline := wdline; WD1.Application.Selection.MoveDown(Tablwdline, con, TExtend); WD1.Application.Selection.Cells.Merge; WD1.Application.Selection.EndOf(Template, Template); end end else begin Con := 8; Tablwdline := wdWord; WD1.Application.Selection.Delete(Tablwdline, con); //удалить выделенное WD1.Application.Selection.EndOf(Template, Template); end;

L := wdStory; // в начало документа WA1.Selection.HomeKey(L, EmptyParam);

Memo1.Lines.Add('Меняем маркеры в тексте. ' ); Application.ProcessMessages;

Find_T := '%Фамилия%'; // текст который меняем D := wdFindStop; // найти один раз DD := wdReplaceAll; // замена все Replese_T := Edit1.Text; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

Find_T := '%Имя%'; // текст который меняем Replese_T := Edit2.Text; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

Find_T := '%Отчество%'; // текст который меняем Replese_T := Edit3.Text; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

Find_T := '%время.прихода%'; // текст который меняем Replese_T := Edit4.Text; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

Find_T := '%время.ухода%'; // текст который меняем Replese_T := Edit5.Text; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

Find_T := '%Дата%'; // текст который меняем Replese_T := DateTostr(Now); // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

Find_T := '%опер.кол%'; // текст который меняем Replese_T := Stringgrid1.RowCount - 1; // заменить на

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

//Внос данных в колонтитул

L := wdStory; // в начало документа WA1.Selection.HomeKey(L, EmptyParam);

Memo1.Lines.Add('Меняем маркер в колонтитуле..' ); Application.ProcessMessages;

WA1.ActiveDocument.ActiveWindow.View.SeekView := wdSeekCurrentPageHeader; Find_T := '%Фирма%'; // текст который меняем D := wdFindStop; // найти один раз Replese_T := Edit6.Text; // замена

WA1.Selection.Find.Execute(Find_T, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, D, EmptyParam, Replese_T, DD, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

except WA1.Visible := true; WA1.Disconnect; WD1.Disconnect; Memo1.Lines.Add('Ошибка..' ); Application.ProcessMessages; exit; end; Memo1.Lines.Add('Экспорт завершен.' ); Application.ProcessMessages; WA1.Visible := True; WD1.Disconnect; WA1.Disconnect; end;

Прилогаяю исходники: ]]> Исходник ]]>

Вложение Размер JOB_TABLE2.zip 319.52 КБ
  • Добавить комментарий
  • 22562 просмотра

Тебе надо было не на форум это писать, а написать и оформить в блог. Думаю будет полезно. На форуме это не то.

P.S. Кстати в свое время делал с помощью закладок. Но неудобно, т.к. визуально не видишь где эта закладка находится в тексте. Но с другой стороны из под делфи с закладками было удобнее работать.

Используя трассировку кода, можно избежать ламерских вопросов!

  • Добавить комментарий

Сейчас утром прочитал и понял , что как то бредово написал, поторопился, спать хотелось, надо было более подробно остановиться на коде, расписать назначение функций. Мой первый опыт и не совсем удачный. Исправлюсь.

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

  • Добавить комментарий

Напиши в блог, либо статью. Чтобы потом в закладки можно было запомнить. Да и легче найти будет статью/блог по поиску. В общем вот так

Используя трассировку кода, можно избежать ламерских вопросов!

  • Добавить комментарий

Отличный код, только вот не подскажете, как можно сделать его покороче, мне нужно использовать только стрингрид, а в word счет на 6 столбцов. грубо говоря грид должен вставиться в таблицу между шапкой и футером

  • Добавить комментарий

если коротко то попробуй так: в шаблоне между шапкой и и футером поставь одну пустую строчку

. подключаемся к WORD . подключаемся к документу .

Tbl := WA1.ActiveDocument.Tables.Item(1); // присоединяемся к первой таблице документа или к той которой надо (сверху вниз)

L := Stringgrid1.RowCount - 2; // определяем количество вставляемых строк с учетом шапки в Stringgrid;

Tbl.cell(2,2).range.select; WD1.Application.Selection.SelectRow; //если шапка в таблице WORD из одной строки то выделяем вторую строку

if L >= 1 then WA1.Selection.InsertRowsBelow(L); //вставляем строки

// вставляем значения из StringGtrid в таблицу for i:=1 to StringGrid1.RowCount-1 do for j:=0 to StringGrid1.ColCount-1 do begin Tbl.Cell(i+1,j+1).Range.InsertAfter(StringGrid1.Cells[j,i]); end;

. Закрываем все подключения .

Вложение Размер FootHeatTable.zip 22.19 КБ

  • Добавить комментарий

Поправьте пожалуйста в коде комментарий: L := Stringgrid1.RowCount - 2; // оперделаяем - или определяем . ))))

  • Добавить комментарий

Данный вариант действительно хорош при построении таблиц. А вот. посоветуй, поделись опытом, как быть в случае, если.

Что имеем Есть шаблон: Invoice.dot (Шаблон квитанции). В этом шаблоне, в нужных местах расставлены закладки, для получения данных из набора данных Есть полученные селектом данные из БД

Задача: Нужно полученные данные из БД заполнить в шаблоне, используя закладки. После заполнения одной квитанции, нужно ниже скопировать квитанцию и вставить следующую апись и т.д.

Что у меня получилось:

procedure TfrmWork_Invoice.btn_ProbnikClick( Sender : TObject ); var pWord : Variant; xFileName : String; i : integer; begin xFileName := 'C:\хххххх\ххххх\InvoiceNew.dot'; // Проверяем есть ли файл if not FileExists( xFileName ) then begin Application.MessageBox( PChar( 'Файл шаблона не найден:' + #13 + xFileName + #13 + 'Экспорт данных не возможен.' ), 'Ошибка', MB_ICONERROR ); Exit; end; // Открываем try pWord := CreateOleObject( 'Word.Application' ); except end;

// Открываем приложение pWord.Documents.Add( '', false, 0 );

// Открываем файл шаблона (но он уже будет .doc) pWord.Documents.Add( xFileName, false, 0 ); // заполняем закладку (метку) pWord.ActiveDocument.Bookmarks.Item( 'data1' ).Range.Text :='с ' + DateToStr(DateTimePicker1.Date) + ' по ' + DateToStr( DateTimePicker2.Date ); pWord.ActiveDocument.Bookmarks.Item( 'data2' ).Range.Text :='с ' + DateToStr(DateTimePicker1.Date) + ' по ' + DateToStr( DateTimePicker2.Date ); pWord.ActiveDocument.Bookmarks.Item( 'FIO1' ).Range.Text := DataModule2.ADQuerySQLSelectDinamic.Fields[ 4 ].AsString; pWord.ActiveDocument.Bookmarks.Item( 'FIO2' ).Range.Text := DataModule2.ADQuerySQLSelectDinamic.Fields[ 4 ].AsString; pWord.ActiveDocument.Bookmarks.Item( 'SUM1' ).Range.Text := IntToStr(DataModule2.ADQuerySQLSelectDinamic.Fields[3].AsInteger * - 1 ); pWord.ActiveDocument.Bookmarks.Item( 'SUM2' ).Range.Text := IntToStr(DataModule2.ADQuerySQLSelectDinamic.Fields[3].AsInteger * - 1 ); pWord.ActiveDocument.Bookmarks.Item( 'ADRES1' ).Range.Text := DataModule2.ADQuerySQLSelectDinamic.Fields[ 5 ].AsString; pWord.ActiveDocument.Bookmarks.Item( 'ADRES2' ).Range.Text := DataModule2.ADQuerySQLSelectDinamic.Fields[ 5 ].AsString; pWord.ActiveDocument.Bookmarks.Item( 'Num1' ).Range.Text := DataModule2.ADQuerySQLSelectDinamic.Fields[ 1 ].AsString; pWord.ActiveDocument.Bookmarks.Item( 'Num2' ).Range.Text := DataModule2.ADQuerySQLSelectDinamic.Fields[ 1 ].AsString;

// и копируем его в новый документ pWord.Selection.WholeStory; pWord.Selection.Copy; pWord.ActiveDocument.close( SaveChanges := wdDoNotSaveChanges ); pWord.Selection.EndKey( Unit := wdStory ); pWord.Selection.Paste;

// показывем что наделали pWord.Visible := true; end;

Как сделать так, чтоб автоматически множился шаблон с первой страницы на следующую (в идеале - две квитанции на один лист А4) Ровно столько сколько есть записей, полученные селектом из БД

📎📎📎📎📎📎📎📎📎📎