【问题标题】:Word VSTO Add-In : Changing the Text of a Paragraph through its RangeWord VSTO 加载项:通过其范围更改段落的文本
【发布时间】:2024-01-21 21:19:01
【问题描述】:

我正在编写一个加载项,它使用 GroupContentControl (GCC) 对使用“标题 1”样式的每个段落进行写保护。一旦受到保护,这些标题只能通过加载项进行更改。为此,我编写了一个简单的方法,删除段落的 GCC,为段落的 Range.Text 属性分配一个新值,再次将样式设置为 Heading 1,然后设置一个新的 GCC 来保护段落。

我还是 Office 编程新手,我的假设是 Range 对象可以在整个过程中使用。但是,该方法行为不端:它在新段落文本之后插入一个回车符,然后将文档中的下一个段落设为标题 1 段落,然后对其进行写保护。

这是我的方法,以及对日志函数的调用以查看 Range 的演变:

void ChangeParagraphText (Word.Paragraph p, string NewText)
{
    Log("Range 1 : " + p.Range.Start + " - " + p.Range.End);
    UnlockTitle(p);     // remove the paragraph's GroupContentControl     
    Log("Range 2 : " + p.Range.Start + " - " + p.Range.End);
    p.Range.Text = NewText;   // change the paragraph's text
    Log("Range 3 : " + p.Range.Start + " - " + p.Range.End);
    p.Range.set_Style(Globals.ThisAddIn.Application.ActiveDocument.Styles[Word.WdBuiltinStyle.wdStyleHeading1]);
    Log("Range 4 : " + p.Range.Start + " - " + p.Range.End);
    LockTitle(p);       // write-protect the paragraph with a GroupContentControl
    Log("Range 5 : " + p.Range.Start + " - " + p.Range.End);
}

运行后,以下是日志文件的内容:

Range 1 : 90 - 99
Range 2 : 90 - 97
Range 3 : 100 - 102
Range 4 : 100 - 102
Range 5 : 100 - 104

这教会了我一些有趣的事情。对于初学者,Range 的长度在移除 GCC 后会减少 2,而在添加 GCC 后会增加 2。不过,重要的部分是 third 日志条目:它表明,一旦我分配了段落的 Range.Text,Range 就会完全改变。这些值与我的测试文档中的 next 段落匹配。

读取 Range.Text 属性还显示它以换行符 (ASCII 13) 结尾。如果我省略它,我最终会在标题 1 段落后面附加下一段。如果我把它放进去,我最终会将第二段视为标题 1 段。这些行为都不适合我。

我的问题是:如何以编程方式更改段落的文本?我认为它必须是可能的,但如果不是,解决方法是什么?我一直在考虑在要更改的段落之后创建一个新段落,然后删除旧段落,但这似乎不雅。

【问题讨论】:

    标签: c# .net ms-word vsto


    【解决方案1】:

    首先,快速回答您提出的问题:如何替换段落的文本,同时保持段落本身完好无损。我无法使用您提供的代码,因为它没有显示您如何派生 p。所以我取了一个任意的Paragraph 对象:

    Word.Range rngPara = doc.Paragraphs[1].Range;
    object unitCharacter = Word.WdUnits.wdCharacter;
    object backOne = -1;
    rngPara.MoveEnd(ref unitCharacter, ref backOne);
    rngPara.Text = "replacement text";
    

    背景信息

    问题的最重要方面是理解 Word Range 对象。

    首先,永远不要依赖StartEnd 属性来识别范围。它们适用于“快照”,或者将一个范围的开始或结束点动态设置为一秒。但是,一旦对文档进行任何编辑,您就可以将它们丢弃。

    当您需要使用 Ranges 时,在代码运行期间使用 Range 对象(变量)。

    是的,正如您分析的那样,Paragraph.Range.Text 确实包含段落结尾标记。这是 ANSI 13,不仅仅是回车;在幕后,它存储了大量有关段落格式的信息。如果您只想使用文本,请将 Range 缩短一个字符,以将段落标记留在 Range 之外。这可以使用MoveEnd 方法完成。

    【讨论】:

    • 谢谢辛迪,成功了。我希望 Word 不只是使用基本的“char”,我只是找不到如何在谷歌上找到它。 Word.WdUnits 是我会写下来探索的!我也不了解 Range 对象的“秘密生活”。我完全期望一个段落的范围是那个段落的范围,直到被删除或宇宙的热死:-) 出于好奇,你知道为什么在存在 GroupContentControl 时 Text 会有这种微小的长度差异吗?它是文档文件中控件元数据的某种隐藏引用或链接吗?
    • @JeanRoch A Range 是从左到右、从上到下的连续内容单元(因此表格列不能是范围)。一旦你有了一个范围,你可以使用各种“移动”方法来改变它的尺寸。对象的Range 不可变的(例如,Paragraph.Range),但是一旦你有一个从它派生的独立 Range 对象,你就可以使用它。 CC、字段和其他一些东西确实在文本流中有“标记”。有些是可测量的(字段的代码,包括其括号),有些不是(内容控件)。
    • 再次感谢您,辛迪。我似乎掌握了 Office 编程的窍门 :-)
    • 是的,Word 并不容易——尤其是如果您不是从用户 POV 那里熟悉它的话 :-) 在许多方面,它反映了用户使用它的方式,这对任何人来说都是令人生畏的否则!
    最近更新 更多