Альтернативы поиску и замене для использования в сообщении приложению, куда помещать контент в документ Word - PullRequest
0 голосов
/ 22 июля 2011

Я пишу приложение, которое также занимается управлением документами. Я делаю это в Delphi, но вопрос может относиться и к c #. Я только что получил очень полезный ответ на этот вопрос, который я задал , что облегчило работу ma Search & Replace.

У меня есть одно требование - автоматически вставлять некоторые данные в документ в соответствии с данными БД каждый раз, когда пользователь открывает документ. До сих пор (и это включает тему вопроса, упомянутого выше), я делал это только один раз: «создать документ из шаблона» = заменить один раз, и работа сделана. Search & Replace отлично работает для этого, это также очень просто для пользователей.

Теперь требуется делать это постоянно, каждый раз, когда я открываю документ, поэтому один и тот же документ должен содержать «заполнитель» и «реальные данные» (после того, как я его открою).

Простой пример:

в заголовке документа word, пользователь хочет вставить вид заполнителя для 3 полей: company_logo (изображение), номер редакции (целое число), дата редакции (datetime).

В настоящее время мне известны только следующие техники:

1) Поиск и замена - я уже использую это для других дел

2) заполнители - я никогда не использовал это, но я думаю, что это выполнимо (здесь есть несколько сообщений, таких как этот )

С поиском и заменой я бы сделал так:

a) Я прошу пользователя подготовить текстовый документ, в котором он пишет поля db в фигурных скобках, например {NAME} {SURNAME} (примечание: это просто фигурные скобки, а не заполнители MSWord). б) когда пользователь «выписывает» документ для редактирования, он продолжает читать {ИМЯ} и ФАМИЛИЯ} в) когда пользователь «открывается в режиме только для чтения», я делаю трюк «Поиск и замена»

Это наверняка сработает с Search & Replace, и это очень простая техника для пользователя.

Полагаю, то же самое будет работать с метками-заполнителями (которые можно вставить в Word с помощью CTRL + F9).

а что делать с изображениями?

А какие альтернативы можно использовать для вставки содержимого во время выполнения в текстовый документ?

1 Ответ

2 голосов
/ 23 июля 2011

В комментарии к вашему вопросу о том, как избежать ограничения 255 ( Как обойти ограничение 255 символов в MSWord Search & Replace с помощью OLE ), я упомянул переменные документа как, возможно, более простой способ добавить время выполнения содержание в текстовый документ.

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

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

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

HideExistingFieldCodes(Doc);
AddFieldDocVarsToPlaceHolders(Doc);
SetValuesForDocVars(Doc);

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

procedure HideExistingFieldCodes(const Doc: WordDocument);
var
  i: Integer;
begin
  for i := 1 to Doc.Fields.Count do begin

    // Only interested in document variables.
    if Doc.Fields.Item( i ).Type_ = wdFieldDocVariable then begin
      Doc.Fields.Item( i ).ShowCodes := False;
    end;

  end;
  // Do not call Doc.Fields.Update, because that will show all fields' code again.
  // Doc.Fields.Update;
end;

После того как коды любых существующих полей были скрыты, мы можем отсканировать документ на наличие заполнителей и заменить каждое из них на поле переменной документа.

procedure AddFieldDocVarsToPlaceHolders(const Doc: WordDocument);
var
  i: Integer;
  OleTrue: OleVariant;
  OleFalse: OleVariant;
  OleEmpty: OleVariant;
  FindText: OleVariant;
  Replace: OleVariant;
  FieldType: OleVariant;
  NewField: Field;
begin
  OleTrue := True;
  OleFalse := False;
  OleEmpty := '';
  Replace := wdReplaceOne;
  FieldType := wdFieldDocVariable;

  // Skip the titles.
  for i := 1 to PlaceHoldersEdit.Strings.Count do begin
    FindText := Format('%s', [PlaceHoldersEdit.Keys[i]]);

    FWord.Selection.SetRange(0, 0); // Back to the beginning of the document.
    while FWord.Selection.Find.ExecuteOld({FindText}FindText, {MatchCase}EmptyParam, {MatchWholeWord}EmptyParam,
      {MatchWildcards}EmptyParam, {MatchSoundsLike}EmptyParam, {MatchAllWordForms}EmptyParam, {Forward}OleTrue,
      {Wrap}OleFalse, {Format}EmptyParam, {ReplaceWith}OleEmpty, {Replace}Replace )
    do begin
      NewField := FWord.Selection.Fields.Add({Range}FWord.Selection.Range, {Type_}FieldType, {Text}FindText, {PreserveFormatting}OleTrue);
      NewField.ShowCodes := False; // Make sure document variable name is hidden
      // Select this field and set selection to the end of its definition, making
      // doubly sure we won't find its name and replace it again.
      NewField.Select;  
      FWord.Selection.SetRange(FWord.Selection.End_, FWord.Selection.End_);
    end;

  end;
end;

Я использовал TValueListEditor (с именем PlaceHoldersEdit) для хранения имен и значений заполнителей. Его первая строка содержит заголовки столбцов в этом элементе управления и пропускается. После этого необходимо выполнить цикл по всем заполнителям и каждому циклу вхождения в документе Word.

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

procedure SetValuesForDocVars(const Doc: WordDocument);

  function ExtractVarNameFromField(VarCode: WideString): OleVariant;
  var
    s: string;
    i: Integer;
  begin
  // Code Text:  DOCVARIABLE Naam \* MERGEFORMAT
  // Code Text:  DOCVARIABLE Naam
    s := Trim(VarCode);
    Delete(s, 1, Length('DOCVARIABLE '));
    i := Pos('\* MERGEFORMAT', s);
    if i > 0 then begin
      Delete(s, i, Length('\* MERGEFORMAT'));
    end;
    Result := Trim(s); // Somebody might have added extra spaces by hand...
  end;

  function PlaceHolderValue(const aKey: string): string;
  begin
    Result := PlaceHoldersEdit.Values[aKey];
  end;

  function ReplaceCrLfWithCr(const aSource: string): string;
  begin
    Result := StringReplace(aSource, #13#10, #13, [rfReplaceAll]);
  end;

var
  i: Integer;
  OleVarName: OleVariant;
  OleVarValue: OleVariant;
  DocVar: Variable;
begin
  for i := 1 to Doc.Fields.Count do begin

    // Only interested in document variable fields.
    if Doc.Fields.Item( i ).Type_ = wdFieldDocVariable then begin

      OleVarName := ExtractVarNameFromField( Doc.Fields.Item( i ).Code.Text );
      OleVarValue := ReplaceCrLfWithCr(PlaceHolderValue(OleVarName));

      // Word removes fields/variables with an empty string as their value, 
      // adding a space prevents that.
      if OleVarValue = '' then begin
        OleVarValue := ' ';
      end;

      DocVar := Doc.Variables.Item(OleVarName);
      if VarIsNull(DocVar) then begin
        Doc.Variables.Add( OleVarName, OleVarValue );
      end else begin
        DocVar.Value := OleVarValue;
      end;

    end;

  end;
  Doc.Fields.Update;
  Doc.Fields.ToggleShowCodes;
end;

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

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

Кстати: в Word Shft-F9 переключает поле между отображением его кода и отображением его значения. Вам придется использовать диалоговое окно «Параметры» (Word2003, не знаю, где он оказался в более поздних версиях), чтобы показать / скрыть их всех одним махом. Параметр «Коды полей» в разделе «Показать» или «Показать» на вкладке «Показать» или «Просмотр» (перевод с голландского).

...