Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Таблицы в Word


Автор: StamSvam 9.7.2012, 18:55
Здравствуйте. Вы мне уже очень помогли. Надеюсь, не оставите меня и в этот раз.
Моя задача записать в файл строки. В документе Word содержится что-то вроде таблицы. Шаблон допуска для студента. Мне необходимо в эту таблицу записать в определенные ячейки данные из программы. Конкретнее - строки. Как это реализовать я ума не приложу.

Прикрепил файл с шаблоном. 

Помогите, пожалуйста.

Автор: artsb 9.7.2012, 22:22
Я в шаблоне в нужных ячейках создавал закладки (bookmarks) и потом просто искал нужную закладку по имени и заменял её на текст.
Завтра поделюсь кодом smile А пока можете поискать в этом направлении.

Автор: StamSvam 9.7.2012, 22:36
Спасибо большое за идею! Попробую сам, но все равно жду кода

Автор: Usper 10.7.2012, 09:25
Я предлагаю сформировать нужный файл средствами билдера и офиса без использования шаблонов. Пример создания таблицы:
Код

#include "ComObj.hpp"

#define OPG OlePropertyGet
#define OPS OlePropertySet
#define OF OleFunction

void exportToWord()
{
    Variant vApp, vDoc, vDocs, vParagraphs, vParagraph, vRange, vTables, vTable;    
    AnsiString S;
    int paragraphItem;
    const int colCount = 10; //количество столбцов в будущей таблице
    const int rowCount = 10; //количество строк в будущей таблице
    try
    {
        vApp = CreateOleObject("Word.Application");
    }
    catch(...)
    {
        Application->MessageBoxA("Не могу запустить сервер MS Word. Отчёт не может быть экспортирован.","Ошибка",MB_ICONERROR);
        return;
    }
    vApp.OPS("Visible", false); //прячем создаваемый документ (так быстрее работает)
    vDocs = vApp.OPG("Documents");
    vDocs.OleProcedure("Add");
    vDoc = vDocs.OF("Item", 1);
    vParagraphs = vDoc.OPG("Paragraphs");
    vParagraphs.OleProcedure("Add"); //добавляем параграф в документ
    vParagraph = vParagraphs.OF("Item", 1);//"указатель" на первый параграф
    vRange = vParagraph.OlePropertyGet("Range");
    vDoc.OlePropertyGet("Tables").OleProcedure("Add", vRange, rowCount, colCount, 1, 1); //вставляем в документ таблицу
    vTable = vDoc.OlePropertyGet("Tables").OleFunction("Item", 1);
    vTable.OF("Cell", 1, 1).OPG("Range").   //пример добавления текста в ячейки
                OPS("Text", "Столбец 1, строка 1");
    vTable.OF("Cell", 2, 1).OPG("Range").
                OPS("Text", "Столбец 1, строка 2");
    vTable.OF("Cell", 3, 3).OF("Merge", vTable.OF("Cell", 3, 5)); //объединяем три ячейки в третьей строке
    vTable.OPG("Borders").OF("Item", 4294967291).OPS("LineStyle", 0); //делаем невидимыми все вертикальные границы между ячейками таблицы...
    vTable.OPG("Borders").OF("Item", 4294967290).OPS("LineStyle", 0); //... и горизонтальные

    vTable.OF("Cell", 3, 3).OPG("Borders").OF("Item", 4294967293).OPS("LineStyle", 1); //делаем нижнюю границу ячейки 3,3 видимой
    vTable.OF("Cell", 3, 3).OPG("Range").
                OPS("Text", "Столбец 3, строка 3");
    vParagraphs.OF("Item", 25).OPG("Range").OPG("Font").OPS("Size", 14); //устанавливаем размер шрифта в 25 параграфе в 14 пунктов. В нашем случае 25 параграф это ячейка 3,3
    vParagraphs.OF("Item", 25).OPG("Range").OPG("Font").OPS("Bold", true); //устанавливаем жирный шрифт там же
    vApp.OPS("Visible", true); //делаем документ видимым
}

А вообще какая задача стоит? Распечатать таблицу или сохранить в файл? Если распечатать, то можно на канве TPrinter создать нужную таблицу и сразу вывести на печать.

Автор: StamSvam 10.7.2012, 10:04
Usper, в том то и дело, что создать этот файл с нуля - лишняя морока, имхо. Если еще и накосячу где-нибудь, то мне потом выскажут все. Эти шаблоны мне выдал "заказчик". 

А что касается дальнейшей судьбы файла, то его надо распечатать.

P.S. Честно говоря, я в непонятках как создать такую табличку даже в самом Ворде. Слишком мудреная она какая-то.
 

Автор: StamSvam 10.7.2012, 10:22
Кстати, если это что-то изменит, то файл изначально есть в формате *.RTF . Но, к стыду признаться я видел такой тип впервые, сохранил его как *.doc, т.к. о работе с таблицами в Ворде я хоть что-то слышал. 

Автор: Usper 10.7.2012, 10:47
Эээ, в чём морока? В моём примере выше я создал файл в ворде и показал как надо работать с таблицами. Нужно только туда запихать таблицу по шаблону. А накосячить можно и с готовым шаблоном.

Автор: StamSvam 10.7.2012, 10:58
Поглядите файл, прикрепленный к теме. И в свойствах таблицы пройдитесь по строкам (Следующая строка/Предыдущая строка). Первые строки куда не шло. А дальше начинается маразм. Я вот просто не пойму, как она так сделана эта таблица. smile 

Автор: deniska 10.7.2012, 11:43
ну на самом деле не так уж и много строк в таблице. можно и тупым перебором определить куда Ворд запишет строку по той или иной "i,j"
Код

Cur_Table->Cell(i, j)->Range->set_Text(WideString(L"Вася Пупкин"));


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

Автор: StamSvam 10.7.2012, 11:59
Такая проблема еще. Открываю файл для работы с ним:

#define OPG OlePropertyGet
#define OPS OlePropertySet
#define OF OleFunction

void exportToWord()
{
    Variant vApp, vDoc, vDocs, vParagraphs, vParagraph, vRange, vTables, vTable;
    AnsiString S;
    int paragraphItem;
 
try
    {
        vApp = CreateOleObject("Word.Application");
    }
    catch(...)
    {
        Application->MessageBoxA("Не могу запустить сервер MS Word. Отчёт не может быть экспортирован.","Ошибка",MB_ICONERROR);
        return;
    }

    vDocs = vApp.OPG("Documents");
    vDocs.OleProcedure("Open","Temp.doc");
......................
}

Вот на строчке с процедурой он выводит такую ошибку: 

Project *.exe raised exeption class EOleSysError with message 'Ошибка'. Process stoped.[U][/U]

и перекидывает на Header "sysvari.h" на строчку  OleProcedure(name, static_cast<TAutoArgsBase*>(&args));

Что ему не нравится? Пробовал перевести имя файла функцией StringToOleStr(), но толку никакого. Та же ошибка.

Автор: Usper 10.7.2012, 12:43
Не такая уж она страшная эта таблица, просто представляет собой кучу сгруппированных ячеек. Кстати, нужна ли эта куча? Очевидно, что половина ячеек в ней никогда не будут использоваться. Задайте этот вопрос "заказчику".
А проблема с открытием файла скорее всего в том, что ворд его не может найти в пути по умолчанию. Задайте нужный путь (например так: vDocs.OleProcedure("Open","d:\\Temp.doc");)

Автор: StamSvam 10.7.2012, 12:54
Тогда возникает еще одна проблема. На другом компьютере ведь заново придется прописывать полный путь к файлу. неизвестно же где пользователь будет хранить файлы. А это не дело, юзеры обычно далеки даже от элементарного указания полного пути к файлу. Да и ленивы для этого..

Автор: Usper 10.7.2012, 14:01
Собственно поэтому я и предложил генерировать файл на месте. Иначе будет нужно распространять шаблон вместе с исполняемым файлом.

Автор: StamSvam 10.7.2012, 14:12
Так. Ну с этим понятно. Надо будет оставить на крайний случай. А про закладки в вставку на их места текста кто что может рассказать? Выручите, плиз. Хоть ссылочкой какой.

Автор: artsb 10.7.2012, 14:29
Вот, как и обещал. Последовательность вызовов. Только не забудь проверять все значения на валидность.
Код

varBookmarks = varActiveDocument.OlePropertyGet("Bookmarks");
varBookmark = varBookmarks.OleFunction("Item", WideString("myBookmark")); // второй параметр - имя закладки
varBookmark.OleProcedure("Select");
varSelection = varWordApplication.OlePropertyGet("Selection");

varSelection.OleProcedure("MoveStart", Word_xp::WdUnits::wdCharacter, 1); // wdCharacter = 1
varSelection.OleProcedure("MoveEnd", Word_xp::WdUnits::wdCharacter, -1);

varSelection.OlePropertySet("Text", WideString("myValue"));

// снять выделение
varRange = varActiveDocument.OleFunction("Range", 0, 0);
varRange.OleProcedure(_T("Select"));

Автор: Usper 10.7.2012, 15:11
Честно говоря, не вижу никакого смысла так делать. Всё равно нужно с собой таскать шаблон. И не проще ли в нужные ячейки вставить нужный текст, вместо поиска-замены?

Автор: artsb 10.7.2012, 15:16
Usper, всё зависит от конкретной задачи. Я не спорю, что можно пробежаться по ячейкам и вставить нужные значения. Но мой способ хорош в том смысле, что можно изменить шаблон и не придётся пересобирать программу заного только из-за того, что столбцов стало на один меньше или больше. В общем, и там и там есть свои плюсы. Пускай ТС сам решает как ему будет удобнее и легче.

Автор: Usper 10.7.2012, 15:34
Хм... действительно это плюс.
Но случайно наткнулся на ещё один минус таскания за собой шаблона, если этот шаблон уже открыт (случайно или намеренно) в момент попытки открытия его программой через OLE, ворд выдаст сообщение, что может его открыть только для чтения. Если пользователь не слишком опытен, это может его сбить с толку.

Автор: artsb 10.7.2012, 15:46
Этот минус устраняется так:
расширение меняется с DOC на DOT.
Теперь при создании документа на основе шаблона, создаётся новый документ, который никак не связан с шаблоном.

Автор: StamSvam 10.7.2012, 18:31
Возможно я туплю..Но выдает две ошибки. Для каждой из строчек

Код

vSelection.OleProcedure("MoveStart", Word_xp::WdUnits::wdCharacter, 1); // wdCharacter = 1
vSelection.OleProcedure("MoveEnd", Word_xp::WdUnits::wdCharacter, -1);


Выдает такую ошибку при компиляции:

"Qualifaer 'WdUnits' is not a class or namespace name.
Function call missing )"



Вот мой код функции.
Код


#include <stdio.h>
#include <iostream.h>
#include <conio.h>
#include <windows.h>
#include <vcl.h>
#include <utilcls.h>  
//#include <sysvari.h>
#include <ComObj.hpp>
#include "word_xp.h"
#pragma hdrstop

#include "Progection.h"

#pragma package(smart_init)
#pragma link "Word_2K_SRVR"
#pragma resource "*.dfm"


void exportToWord()
{
    Variant vApp, vDoc, vDocs, vParagraphs;
    Variant vParagraph, vRange, vTables, vTable;
    Variant vBookmarks,vBookmark,vSelection;
   
    try
    {
        vApp = CreateOleObject("Word.Application");
    }
    catch(...)
    {
        Application->MessageBoxA("Не могу запустить сервер MS Word. Отчёт не может быть экспортирован.","Ошибка",MB_ICONERROR);
        return;
    }
    vApp.OPS("Visible", false); //прячем создаваемый документ (так быстрее работает)
    vDoc = vApp.OPG("Documents");
    vDoc.OleProcedure("Open","d:\\Temp.doc");
    
    vBookmarks = vDoc.OlePropertyGet("Bookmarks");
    vBookmark = vBookmarks.OleFunction("Item", WideString("myBookmark")); // второй параметр - имя закладки
    vBookmark.OleProcedure("Select");
    vSelection = vApp.OlePropertyGet("Selection");
    vSelection.OleProcedure("MoveStart", Word_xp::WdUnits::wdCharacter, 1); // wdCharacter = 1
    vSelection.OleProcedure("MoveEnd", Word_xp::WdUnits::wdCharacter, -1);
    vSelection.OlePropertySet("Text", WideString("myValue"));

  vRange = vDoc.OleFunction("Range", 0, 0);
  vRange.OleProcedure(_T("Select"));
}


В чем я накосячил?

Полазил по инетам и не нашел ни одного упоминания о параметрах "MoveStart" и "MoveEnd". Для чего они? И что передает параметр Word_xp::WdUnits::wdCharacter? Для чего он нужен?

Автор: artsb 10.7.2012, 20:58
Цитата(StamSvam @  10.7.2012,  18:31 Найти цитируемый пост)

В чем я накосячил?

Да, вроде бы, ни в чём. У меня enum WdUnits описан в word_xp.h и выглядит так:
Код

enum class WdUnits
{
  wdCharacter = 1, 
  wdWord = 2, 
  wdSentence = 3, 
  wdParagraph = 4, 
  wdLine = 5, 
  wdStory = 6, 
  wdScreen = 7, 
  wdSection = 8, 
  wdColumn = 9, 
  wdRow = 10, 
  wdWindow = 11, 
  wdCell = 12, 
  wdCharacterFormatting = 13, 
  wdParagraphFormatting = 14, 
  wdTable = 15, 
  wdItem = 16
};

Ну раз у вас ругается, то напишите просто 1:
Код

vSelection.OleProcedure("MoveStart", 1, 1);
vSelection.OleProcedure("MoveEnd", 1, -1);

А потом разберётесь почему не находит enum.

И если вы используете шаблоны, то открывайте документ как шаблон:
Код

varActiveDocument = varWordDocuments.OleFunction("Add", WideString(TemplateFileName)); // второй параметр - путь до файла *.dot

Автор: KaZepKa 10.7.2012, 21:07
Можно пройтись чисто по файлу, зная формат, например, в rtf это не сложно.

Автор: StamSvam 10.7.2012, 21:11
Цитата

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

код C++ Builder
varActiveDocument = varWordDocuments.OleFunction("Add", WideString(TemplateFileName)); // второй параметр - путь до файла *.dot


если я правильно понял, то я создам новый документ, содержащий таблицу из начального файла? Щас протестю

Добавлено через 9 минут и 32 секунды
теперь он (компилятор) не ругается. Зато при вызове функции выводит сообщение 

Project *.exe raised exeption class EOleSysError with message 'Неизвестное имя'. Process stoped.

и показывает строчку

Код

vBookmarks = vDoc.OlePropertyGet("Bookmarks");


Вот это жесть..вообще не понимаю, что ему все время не нравится

вот измененный код:

Код

void exportToWord()
{
    Variant vApp, vDoc, vDocs, vParagraphs;
    Variant vParagraph, vRange, vTables, vTable;
    Variant vBookmarks,vBookmark,vSelection;

    try
    {
        vApp = CreateOleObject("Word.Application");
    }
    catch(...)
    {
        Application->MessageBoxA("Не могу запустить сервер MS Word. Отчёт не может быть экспортирован.","Ошибка",MB_ICONERROR);
        return;
    }
    vApp.OPS("Visible", false); //прячем создаваемый документ (так быстрее работает)
    vDoc = vApp.OPG("Documents");
    vDoc.OleFunction("Add", WideString("d:\\Temp.doc"));
vBookmarks = vDoc.OlePropertyGet("Bookmarks");
vBookmark = vBookmarks.OleFunction("Item", WideString("myB")); // второй параметр - имя закладки
vBookmark.OleProcedure("Select");
vSelection = vApp.OlePropertyGet("Selection");
vSelection.OleProcedure("MoveStart", 1, 1);
vSelection.OleProcedure("MoveEnd", 1, -1);
vSelection.OlePropertySet("Text", WideString("myValue"));
vRange = vDoc.OleFunction("Range", 0, 0);
vRange.OleProcedure(_T("Select"));
vApp.OleProcedure("Quit");

}


Автор: StamSvam 10.7.2012, 22:03
ВСЕ!! РАЗОБРАЛСЯ! Работает!! Ура! 
Спасибо, что пронянчились со мной все это время!

Фрагмент с исправлениями:

Код

vDocs = vApp.OPG("Documents");
vDocs.OleFunction("Add", WideString("d:\\Temp.doc"));

vDoc=vDocs.OleFunction("Item",1);
vDoc.OleProcedure("Activate");

vBookmarks = vDoc.OlePropertyGet("Bookmarks");


Господа, нереально выручили! 

Извините и не сочтите за наглость, но посоветуйте, где мне глянуть инфу по распечатыванию файла? Думаю, там сам разбирусь.

Автор: artsb 11.7.2012, 08:31
Цитата(StamSvam @  10.7.2012,  22:03 Найти цитируемый пост)
Извините и не сочтите за наглость, но посоветуйте, где мне глянуть инфу по распечатыванию файла? Думаю, там сам разбирусь. 

http://www.cyberforum.ru/cpp-builder/thread309825.html
Дополнительная информация: http://wladm.narod.ru/Borland/word.html и http://www.realcoding.net/articles/rabota-s-ms-word.html.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)