C# 搜索和替换的替代方法,用于告知应用程序在Word文档中放置内容的位置

C# 搜索和替换的替代方法,用于告知应用程序在Word文档中放置内容的位置,c#,delphi,ms-word,ole,placeholder,C#,Delphi,Ms Word,Ole,Placeholder,我编写了一个应用程序,也可以进行文档管理。我在Delphi中这样做,但这个问题可能也适用于c#。我刚刚得到了一个非常有用的回复,这使得ma搜索和替换工作变得更加容易 我的一个要求是,每次用户打开文档时,根据db数据自动在文档中插入一些数据。到目前为止(包括上述问题的主题),我只做了一次:“从模板创建文档”=替换一次,工作就完成了。搜索和替换在这方面非常有效,对用户来说也非常容易 现在的要求是每次打开一个文档时都要连续地执行,因此同一个文档应该包含“占位符”和“真实数据”(一旦打开) 一个简单的例

我编写了一个应用程序,也可以进行文档管理。我在Delphi中这样做,但这个问题可能也适用于c#。我刚刚得到了一个非常有用的回复,这使得ma搜索和替换工作变得更加容易

我的一个要求是,每次用户打开文档时,根据db数据自动在文档中插入一些数据。到目前为止(包括上述问题的主题),我只做了一次:“从模板创建文档”=替换一次,工作就完成了。搜索和替换在这方面非常有效,对用户来说也非常容易

现在的要求是每次打开一个文档时都要连续地执行,因此同一个文档应该包含“占位符”和“真实数据”(一旦打开)

一个简单的例子是:

在word文档的标题中,用户希望为3个字段插入一种占位符:公司徽标(图像)、修订编号(整数)、修订日期(日期时间)

目前我知道的唯一技术是:

1) 搜索和替换-我已经用它做其他事情了

2) 占位符-我从来没有用过,但我想这是可行的(这里有几篇类似的帖子)

使用搜索和替换,我将执行以下操作:

a) 我要求用户准备一个word文档,在文档中,他用大括号(如{NAME}{lasname})来写db字段(注意:这只是大括号,不是MSWord占位符)。 b) 当用户“签出”文档进行编辑时,他将继续阅读{NAME}和姓氏} c) 当用户“以只读模式打开”时,我执行搜索和替换技巧

这肯定会起到西斯搜索和替换的作用,这对于用户来说是一种非常简单的理解方法

我想占位符(可以用CTRL+F9在Word中插入的占位符)也同样适用

但是如何处理图像呢


在word文档中插入运行时内容的替代方法有哪些?

在关于如何避免255限制()的问题的评论中,我提到了文档变量,这可能是向word文档添加运行时内容的一种更简单的方法

我认为它们也是你在这里想要做的事情的答案。但是,它们对于最终用户来说并不完全直观,因此保留占位符也很好。不幸的是,这并不是完全可能的,因为最终的文档将同时包含一个占位符和一个文档变量字段。这最终会导致价值观的重复

不过,我们可以保留最终用户的席位。当然是他们最初编辑的“模板”。甚至在已经“转换为使用变量”的现有word文档中添加新的占位符

下面的代码显示了如何执行此操作。我还没有包括打开和关闭Word和/或文档的“标准”Word自动化部分。我从您使用模板打开新文档开始,现在准备开始替换值。然后,解决方法有三种:

HideExistingFieldCodes(Doc);
AddFieldDocVarsToPlaceHolders(Doc);
SetValuesForDocVars(Doc);
之所以需要隐藏的ExistingFieldCodes,是因为我们也将占位符用作文档变量的名称,如果字段显示的是它们的代码而不是它们的值,那么我们最终会用一个新的文档变量替换文档变量名,这可能会使Word发出很大的呕吐声

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(名为Placeholder Sedit)保存占位符名称和值。它的第一行包含此控件中列的标题,将被跳过。在那之后,它是一个在所有占位符上循环的问题,并且对于每个在Word文档中循环出现的占位符

最后,我们可以开始添加实际的文档变量,它将保存文档变量字段的值。(哦,我多么喜欢这些听起来相似的名字。)

在这个方法中,我循环所有文档变量字段,并从每个字段的代码中提取变量名。然后,我尝试查找该名称的文档变量。如果它还不存在,我就添加它。如果它确实存在,我会更改它的值。如果性能是一个问题,并且在许多字段中使用了一个变量名,则可以对此进行一些优化,以仅更改一次值

哦,我已经用一个“保存的”文档进行了测试,但我还没有将新的占位符添加到一个已经通过将占位符转换为文档变量字段的文档中。所以,你可能还得稍微缓和一下


顺便说一下:在Word中,Shft-F9在显示代码和显示值之间切换字段。您必须使用“选项”对话框(Word2003,不知道在以后的版本中会出现在哪里)一次性显示/隐藏所有选项。选项是“显示”或“查看”选项卡上“显示”或“显示”下的“字段代码”(翻译自荷兰语)。

以防万一:您使用的是哪个delphi版本和哪个word版本?delphi XE,理想情况下我希望支持word 2003 2007 2010,如果出于任何原因我需要放弃支持,当然我更喜欢只支持2010,或者2007年和2010年。假设您的应用程序运行在域上,那么通过Active Directory自动获取用户名和其他必需属性来进一步实现自动化如何?好的,那么您应该可以使用D2010为Word2000调制的东西(实际上是97代码:-))对Word2003安装使用W2000接口)。正在调整文档变量的现有代码,并将其与占位符相结合。这对我来说也是一个有趣的练习,因为文档变量对于最终用户来说并不那么直观。我会回来的,但是现在我需要遛狗…我非常感谢你分享我的一切
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;