- Remove From My Forums
-
Question
-
I have a macro enabled document template (.dotm) that I open and replace some named fields with real data. After I have the document completed, if I try to print it, all of the fields change to «Error! No document variable supplied». The same thing happens
if I try to save it as a .PDF file. The same thing happens if I save it as a .DOCX to disable the macros first and then try to print.How do I get it to stop modifying my finished document?
microwave tech
Answers
-
Hi «drew»
I suspect the problem is with how you’re replacing the «named fields». From the title and the little you tell us, I’m guessing that you’ve inserted DocVariable fields in the document, but that there is no corresponding Document.Variable object stored
in the file. So when the fields update, you get an error message. Only when a field updates does it dynamically execute the field code and content, and most field types aren’t constantly updating — Word couldn’t function if they did. But the actions you describe
do trigger an update, which explains why you’re seeing the described behavior.It becomes apparent, therefore, that DocVariable fields weren’t meant to be used in this manner. If you were to pass the content to Document Variable objects in the document, it should work correctly. Something like this:
ActiveDocument.Variables(«field1») = «This is the text for field 1»Another possibility, since you’ve already written the code you have, would be to lock the fields so that they cannot update: ActiveDocument.Fields(1).Locked = True
You could also remove the DocVariable fields and insert Bookmarks, instead. Bookmarks were designed to be «data targets»: ActiveDocument.Bookmarks(«Name»).Range.Text = «This is the text for field 1»
Cindy Meister, VSTO/Word MVP,
my blog-
Marked as answer by
Friday, November 30, 2012 7:26 AM
-
Marked as answer by
- Remove From My Forums
-
Question
-
I have a macro enabled document template (.dotm) that I open and replace some named fields with real data. After I have the document completed, if I try to print it, all of the fields change to «Error! No document variable supplied». The same thing happens
if I try to save it as a .PDF file. The same thing happens if I save it as a .DOCX to disable the macros first and then try to print.How do I get it to stop modifying my finished document?
microwave tech
Answers
-
Hi «drew»
I suspect the problem is with how you’re replacing the «named fields». From the title and the little you tell us, I’m guessing that you’ve inserted DocVariable fields in the document, but that there is no corresponding Document.Variable object stored
in the file. So when the fields update, you get an error message. Only when a field updates does it dynamically execute the field code and content, and most field types aren’t constantly updating — Word couldn’t function if they did. But the actions you describe
do trigger an update, which explains why you’re seeing the described behavior.It becomes apparent, therefore, that DocVariable fields weren’t meant to be used in this manner. If you were to pass the content to Document Variable objects in the document, it should work correctly. Something like this:
ActiveDocument.Variables(«field1») = «This is the text for field 1»Another possibility, since you’ve already written the code you have, would be to lock the fields so that they cannot update: ActiveDocument.Fields(1).Locked = True
You could also remove the DocVariable fields and insert Bookmarks, instead. Bookmarks were designed to be «data targets»: ActiveDocument.Bookmarks(«Name»).Range.Text = «This is the text for field 1»
Cindy Meister, VSTO/Word MVP,
my blog-
Marked as answer by
Friday, November 30, 2012 7:26 AM
-
Marked as answer by
|
|||
| Stim
22.04.09 — 16:31 |
Делаю по Книга знаний: v8: Заполнение полей документа Word (шаблон Active Document) Добавил в документ 3 поля,делаю в них запись, на первом выдается ошибка: Ошибка! Переменная документа не указана Остальные два выводятся без ошибок. В чем причина? |
||
| ТелепатБот
1 — 22.04.09 — 16:31 |
Книга знаний: Заполнение полей документа Word (шаблон Active Document) |
||
| Барбариска
2 — 22.04.09 — 16:34 |
Небось пустое значение передал… |
||
| Stim
3 — 22.04.09 — 16:39 |
(2) не пустое.. значение записалось, но вслед за ним записалась эта ошибка |
||
| Кадош
4 — 22.04.09 — 16:40 |
код в студию |
||
| vde69
5 — 22.04.09 — 16:43 |
http://www.infostart.ru/projects/3060/ |
||
| Stim
6 — 22.04.09 — 16:49 |
Процедура БольшаяКраснаяКнопкаНажатие(Элемент)
Поля расположены один за другим в одну строку, в результате получаю: |
||
| Stim
7 — 22.04.09 — 16:54 |
а, млин, туплю. Просто на месте первого поля я ставил раньше другое поле, с другим названием.. Походу это оно и показывается. Как удалить поле? |
||
| Кадош
8 — 22.04.09 — 16:54 |
WordDoc.Application.Visible=Макет1; что это? |
||
| Stim
9 — 22.04.09 — 16:55 |
WordDoc.Application.Visible=1; |
||
| Stim
10 — 22.04.09 — 16:56 |
см (1) |
||
| Stim
11 — 22.04.09 — 16:58 |
как удалить добавленное поле в Ворде? |
||
|
Stim 12 — 22.04.09 — 17:10 |
разобрался. alt+F9 показывает коды полей. |
ВНИМАНИЕ! Если вы потеряли окно ввода сообщения, нажмите Ctrl-F5 или Ctrl-R или кнопку «Обновить» в браузере.
Ветка сдана в архив. Добавление сообщений невозможно.
Но вы можете создать новую ветку и вам обязательно ответят!
Каждый час на Волшебном форуме бывает более 2000 человек.
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Pоwerеd by рhpВB © 2001, 2005 рhpВB Grouр
Русская поддержка phрВB
ИТ-записки Чорнага кашака
Язык программирования самого высокого уровня содержит всего несколько команд для управления программистами
15 мар. 2010 г.
Использование полей и закладок при работе с MS Word из Delphi
В предыдущей заметке «Поиск и замена текста в документе MS Word из Delphi» я рассказывал, как дорабатывал старый модуль, который генерирует клиентам компании письма в формате MS Word с помощью поиска и замены текста. Сдав модуль заказчикам, я в свободное от работы время, переделал его. Вместо поиска и замены использовал поля с переменными (DocVariable).
В шаблон письма с помощью макроса добавил переменные
Sub AddFields()
ThisDocument.Variables.Add «FIO», «FIO»
ThisDocument.Variables.Add «ADDRESS», «ADDRESS»
.
End Sub
и расставил поля по тексту шаблона. В макросе у метода Add первый параметр – название переменной, а второй – ее значение. Я специально сделал их одинаковыми, чтобы пользователям было проще и нагляднее редактировать шаблоны.
Затем внес изменения в методы модуля работающие с MS Word. Если опустить все детали и различную логику, то упрощенно работа с MS Word выглядит так:
Var
wa: WordApplication;
ovDotName, ovFileName: OleVariant;
i: Integer;
q: TSDQuery;
.
begin
.
wa := CreateComObject(CLASS_WordApplication) as _Application;
ovDotName := ‘какой то шаблон.dot’;
wa.Documents.Add(ovDotName, EmptyParam, EmptyParam, EmptyParam);
For i := 0 to q.FieldCount-1 do
MSWordSetVariable(q.Fields[i].FieldName, q.Fields[i].AsString);
.
ovFileName := ‘письмо любимому клиенту.doc’;
wa.ActiveDocument.SaveAs(ovFileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
.
Вот такая логика у индусов, писавших этот кусок MS Word.
После присвоения значений всем переменным, осталось только дать команду полям обновиться новыми значениями переменных. Для этого в VBA у коллекции объектов полей (Fields) есть метод Update:
А для того, чтобы избежать дальнейшего обновления полей, избавимся от их связи с переменными:
У каждого структурного элемента документа (заметки, колонтитула, сноски и т.д.) своя коллекция объектов полей, поэтому, если документ, как у меня, состоит из разных структурных элементов, то методы Update и Unlink необходимо вызвать для каждого из этих элементов. Для этого перебираем все элементы коллекции StoryRanges.
procedure MSWordUpdateStoryRanges;
Var
StoryRanges: Word2000.StoryRanges;
StoryRange: Word2000.Range;
iStoryIndex: integer;
Begin
StoryRanges := wa.ActiveDocument.StoryRanges;
For iStoryIndex := wdMainTextStory to wdFirstPageFooterStory do
Try
StoryRange := StoryRanges.Item(iStoryIndex);
If StoryRange <> nil then
begin
StoryRange.Fields.Update;
StoryRange.Fields.Unlink;
While (StoryRange.NextStoryRange <> nil) do
begin
StoryRange := StoryRange.NextStoryRange;
StoryRange.Fields.Update;
StoryRange.Fields.Unlink;
end;
end;
Except
StoryRange := nil;
End;
End;
И вызываю ее перед сохранением документа.
Все бы ничего, но часть текста передаваемого из программы в MS Word необходимо было выводить жирным шрифтом. А это через переменные не сделать 🙁 В подобном случае «поиск и замену текста» можно заменить на «переход к закладке и вывод текста». Например, в шаблон вставляем закладку с именем ‘писать текст сюда’, а в программе пишем:
Вот и все 🙂
Раннее связывание и использование полей/закладок дало существенный рост скорости генерации писем. Если на 500-х различных документах этого было почти не заметно, то при генерации 15 000 документов, прирост скорости составил 30% (специально проверил несколько раз на одних и тех же исходных данных).
Оценка статьи:




Загрузка…
Adblock
detector
Доброго времени суток!
Я бы хотел поднять скорее идейный вопрос: Как правильно заполнять шаблон Word?
Я знаю как минимум 5 способов заполнения Word шаблона, и все 4 имеют свои недостатки, пятый же настолько ужасен, что я его даже приводить не буду. Но крови он мне попил изрядно, т.к. приходится работать с чужим кодом. И так, давайте я перечислю то что знаю и плюсы и минусы этих подходов, а вы может быть подкините ещё способов для размышления, а может быть даже посоветуете единственно верный! Думаю тема будет многим полезна.
Описываю всё по русской версии Word.
1. DocVariable
Можно вставлять в шаблон поля с типом DocVariable, давать им имена, а потом в коде обращаться к ним примерно так:
| C# | ||
|
В конце надо обязательно вызвать WordDoc.Fields.Update();
Плюсы: можно включить или выключить (Alt+F9) отображение этих переменных в шаблоне. Имена переменных видны прямо в шаблоне. Можно в разные места вставлять одну и ту же переменную.
Минусы: если вдруг по какой-причине вы не присвоили в коде значение такой переменной, то в результате в документе на месте переменной будет красоваться такая ошибка: «Ошибка! Переменная документа не указана.»
2-3 FormText или Bookmarks
Эти 2 способа я объединю, т.к. они по сути одинаковы и второй полностью перекрывает третий.
Второй и опишу. В шаблон вставляется Поле FORMTEXT, ему присваивается Закладка, по которой мы и будет обращаться к полю в коде. Примерно так:
| C# | ||
|
Ну и тоже самое с закладками, т.е. можно вставить в документ Закладку и почти так же к ней обращаться, только закладки не видны в Шаблоне в отличии от FormText, что не очень удобно.
Плюсы: в Шаблоне видны места вставок их отображение можно включать и отключать (Alt+F9). Если в коде мы ей ничего не присваиваем, ничего не происходит.
Минусы: что бы увидеть имя Закладки, нужно лезть в свойства Поля. Одно и тоже Поле (т.е. с одинаковой Закладкой) нельзя использовать в разных местах.
4. Replace Text
Можно из кода воссоздать вызов всем знакомой функции «Найти и заменить». В коде это всё выглядит немного громоздко, благо один раз метод написали и забыли. Но суть следующая. В Word вставляются текстовые «метки», т.е. такие сочетания, которые вы будете искать в коде и заменять. Кто-то используется для меток угловые скобки, кто-то фигурные… Не суть, главное что бы метки уникальной выделялись из текста. Т.е. вы можете вставить в документ такую метку {FieldName}, а в коде искать текст «{FieldName}» и заменять на то, что вам надо.
Плюсы: в Шаблоне явно видны как сами метки, так их имена. Можно использовать одну и ту же метку в разных местах. Если вы из кода попытаете заменить не существующую метку, ничего не произойдёт. В предыдущих случаях этот момент идёт в минус, т.к. попытка обратиться к не существующему полю вызовет ошибку, но минус так себе, поэтому я его там и не указал.
Минусы: если в коде вы по какой-то причине пропустили какую либо метку, она очень не красиво будет смотреться в результирующем документе. Сам подход архитектурно кривоват, т.е. какие-то сепец. символы для выделения меток, да и после замены какой-либо метки вы уже ничего не можете с ней сделать в Шаблоне, что тоже не очень хорошо.
А какой способ используете вы и почему? Может быть я не правильно использую какой-либо из вышеперечисленных способов и можно исключить перечисленные в нём минусы? Я лично пользуюсь первым способом, т.к. его единственный минус наиболее безобидный на мой взгляд.
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
Автор Katish, 09 апр 2012, 06:54
0 Пользователей и 1 гость просматривают эту тему.
У меня в печатной форме макет типа ActiveDocument Word.
В нём есть поле -Текст.
Заполняю его в модуле:
Текст = "";
...
Документ.Variables("Текст").Value = Текст;
Если значение переменной отличаются от «», то всё гут, если вдруг значение — пустая строка, то на выходе ошибка:
Ошибка! Переменная документа не указана.
Какой выход из ситуации?
Пробел не предлагать — получается сильно много пробелов при таких полях подряд.
Если Не ПустаяСтрока(Текст) Тогда
Документ.Variables("Текст").Value = Текст;
конецесли;
Если долго всматриваться в учебник…то в голову может прийти мысль его открыть!
Цитата: Dethmontt от 09 апр 2012, 08:25
Если Не ПустаяСтрока(Текст) Тогда
Документ.Variables("Текст").Value = Текст;
конецесли;
В этом случае значение переменной вообще не указывается и выдаётся аналогичная ошибка.
На каком месте ошибка ТО???????????????????
Если долго всматриваться в учебник…то в голову может прийти мысль его открыть!
Добрый день! Столкнулся с ситуацией, когда в вордовском шаблоне под печатную форму, присланном из одной организации уже задействованы пользователями docVariable. Они судя по всему как-то увязаны с некой таблицей-структурой внутри doc файла. Т.е. на листе уже есть параметр ФИО и он заполняется Петров Петрович Петр-ом при открытии документа. При этом если добавлять свои параметры, то уже на уровне Word-a без привлечения 1с пишет «Ошибка! Переменная документа не указана.» Дескать твоего параметра нет в моей таблице соответствия. И от этого при формировании печатной формы уже в 1С мои данные не попадают на лист. Скопировать на новый чистый лист не получится, много текста с графикой, форматирование ломается. Да и при фрагментарном переносе эта табличка переменных судя по всему тоже копируется. Надо как-то ее удалить или отключить. Как?
Посмотреть макросы документа.
Не могу понять где это расположено. Зашел в Макросы, выбрал Макросы из ИмяДокумента(Документ), там пусто. В Активных шаблонов — пусто, в Normal.dotm тоже пусто.
Может, ссылка куда-то висит?
В этом же меню Макросы? Нет, там еще есть Команд Word но я так полагаю это стандартное и не сильно редактируемое.
Могу файлик docx выложить исходный.
Прошу прощения, я так и не понял — как эта статья поможет мне избавиться от «Ошибка! Переменная документа не указана.» в существующем документе без создания нового документа.
Ругаться перестало после удаления переменных, описанных внутре документа: For Each CurVar In ActiveDocument.Variables CurVar.Delete Next Однако пока не могу с уверенностью сказать, что ничего в документе не напортил. Может лучше было добавить в документ нужных переменных.
Тэги: 1С 8
Комментарии доступны только авторизированным пользователям
В предыдущей заметке «Поиск и замена текста в документе MS Word из Delphi» я рассказывал, как дорабатывал старый модуль, который генерирует клиентам компании письма в формате MS Word с помощью поиска и замены текста. Сдав модуль заказчикам, я в свободное от работы время, переделал его. Вместо поиска и замены использовал поля с переменными (DocVariable).
В шаблон письма с помощью макроса добавил переменные
Sub AddFields()
ThisDocument.Variables.Add «FIO», «FIO»
ThisDocument.Variables.Add «ADDRESS», «ADDRESS»
…
End Sub
и расставил поля по тексту шаблона. В макросе у метода Add первый параметр – название переменной, а второй – ее значение. Я специально сделал их одинаковыми, чтобы пользователям было проще и нагляднее редактировать шаблоны.
Затем внес изменения в методы модуля работающие с MS Word. Если опустить все детали и различную логику, то упрощенно работа с MS Word выглядит так:
Var
wa: WordApplication;
ovDotName, ovFileName: OleVariant;
i: Integer;
q: TSDQuery;
…
begin
…
wa := CreateComObject(CLASS_WordApplication) as _Application;
ovDotName := ‘какой то шаблон.dot’;
wa.Documents.Add(ovDotName, EmptyParam, EmptyParam, EmptyParam);
For i := 0 to q.FieldCount-1 do
MSWordSetVariable(q.Fields[i].FieldName, q.Fields[i].AsString);
…
ovFileName := ‘письмо любимому клиенту.doc’;
wa.ActiveDocument.SaveAs(ovFileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
…
Где MSWordSetVariable – процедура, которая присваивает значение переменной в документе MS Word.
procedure MSWordSetVariable(ovVariable: OleVariant; const sValue: String);
begin
If sValue = »
then wa.ActiveDocument.Variables.Item(ovVariable).Value := ‘ ‘
else wa.ActiveDocument.Variables.Item(ovVariable).Value := sValue
end;
Если переменной MS Word присвоить пустую строчку, то в поле выводится текст ‘Ошибка! Переменная документа не указана.’, поэтому вместо пустого значения я присваиваю ей пробел (так же делает и сам MS Word). Это связано со странной работой MS Word с коллекцией переменных. Если переменной присвоить пустую строку, то количество переменных в коллекции (Variables.Count) уменьшается на единицу, а попытка удалить переменную (Variables.Item(ovVariable).Delete) дает ошибку ‘Объект был удален’, т.е. присвоение пустой строки равносильно удалению. При этом после удаления переменной из коллекции переменных, присвоение ей непустого значения выполняется успешно и количество переменных в коллекции увеличивается на единицу, т.е. присвоение непустого значения равносильно вызову метода добавления переменной в коллекцию переменных.
Продемонстрирую вышесказанное примером кода, по которому видно как изменяется количество переменных (iCount):
iCount := wa.ActiveDocument.Variables.Count; // iCount = 3
wa.ActiveDocument.Variables.Item(ovVariable).Value := »;
iCount := wa.ActiveDocument.Variables.Count; // iCount = 2
wa.ActiveDocument.Variables.Item(ovVariable).Value := ‘не пусто’;
iCount := wa.ActiveDocument.Variables.Count; // iCount = 3
wa.ActiveDocument.Variables.Item(ovVariable).Delete;
iCount := wa.ActiveDocument.Variables.Count; // iCount = 2
wa.ActiveDocument.Variables.Item(ovVariable).Value := ‘не пусто’;
iCount := wa.ActiveDocument.Variables.Count; // iCount = 3
wa.ActiveDocument.Variables.Item(ovVariable).Value := »;
iCount := wa.ActiveDocument.Variables.Count; // iCount = 2
wa.ActiveDocument.Variables.Item(ovVariable).Delete; // ошибка ‘Объект был удален’
Вот такая логика у индусов, писавших этот кусок MS Word.
После присвоения значений всем переменным, осталось только дать команду полям обновиться новыми значениями переменных. Для этого в VBA у коллекции объектов полей (Fields) есть метод Update:
wa.ActiveDocument.Fields.Update
А для того, чтобы избежать дальнейшего обновления полей, избавимся от их связи с переменными:
wa.ActiveDocument.Fields.Unlink
У каждого структурного элемента документа (заметки, колонтитула, сноски и т.д.) своя коллекция объектов полей, поэтому, если документ, как у меня, состоит из разных структурных элементов, то методы Update и Unlink необходимо вызвать для каждого из этих элементов. Для этого перебираем все элементы коллекции StoryRanges.
procedure MSWordUpdateStoryRanges;
Var
StoryRanges: Word2000.StoryRanges;
StoryRange: Word2000.Range;
iStoryIndex: integer;
Begin
StoryRanges := wa.ActiveDocument.StoryRanges;
For iStoryIndex := wdMainTextStory to wdFirstPageFooterStory do
Try
StoryRange := StoryRanges.Item(iStoryIndex);
If StoryRange <> nil then
begin
StoryRange.Fields.Update;
StoryRange.Fields.Unlink;
While (StoryRange.NextStoryRange <> nil) do
begin
StoryRange := StoryRange.NextStoryRange;
StoryRange.Fields.Update;
StoryRange.Fields.Unlink;
end;
end;
Except
StoryRange := nil;
End;
End;
Т.к. в моем шаблоне есть только основной текст и одна заметка, то вместо процедуры, которая перебирает все элементы коллекции StoryRanges, я сделал процедуру, которая работает только с одним ее элементом:
procedure MSWordUpdateStoryRange(const iStoryIndex: integer);
Var
StoryRange: Word2000.Range;
begin
Try
StoryRange := wa.ActiveDocument.StoryRanges.Item(iStoryIndex);
If StoryRange <> nil then
begin
StoryRange.Fields.Update;
StoryRange.Fields.Unlink;
end;
Except
End;
end;
И вызываю ее перед сохранением документа.
MSWordUpdateStoryRange(wdMainTextStory);
MSWordUpdateStoryRange(wdTextFrameStory);
Все бы ничего, но часть текста передаваемого из программы в MS Word необходимо было выводить жирным шрифтом. А это через переменные не сделать 🙁 В подобном случае «поиск и замену текста» можно заменить на «переход к закладке и вывод текста». Например, в шаблон вставляем закладку с именем ‘писать текст сюда’, а в программе пишем:
Var
ovBookmarkName, ovWhat: OLEVariant;
begin
ovWhat := wdGoToBookmark;
ovBookmarkName := ‘писать текст сюда’;
Try
If wa.Selection.GoTo_(ovWhat, EmptyParam, EmptyParam, ovBookmarkName) <> nil then
begin
wa.Selection.TypeText(‘просто текст ‘);
wa.Selection.Font.Bold := 1;
wa.Selection.TypeText(‘жирный текст’);
wa.Selection.Font.Bold := 0;
wa.Selection.TypeText(‘ просто текст’);
end;
Except
End;
Вот и все 🙂
Раннее связывание и использование полей/закладок дало существенный рост скорости генерации писем. Если на 500-х различных документах этого было почти не заметно, то при генерации 15 000 документов, прирост скорости составил 30% (специально проверил несколько раз на одних и тех же исходных данных).
P.S. При работе с ранним связыванием мне не нравится только одно – многие параметры в методах объявлены, как VAR (даже индекс элемента коллекции!), поэтому приходится заводить для них специальную переменную типа OleVariant.