NotesRichTextNavigator × NotesRichTextRange:用程式走訪與改寫 Rich Text

NotesRichTextNavigator × NotesRichTextRange:用程式走訪與改寫 Rich Text

2026.06.11 約 1,049 字

你接到一個任務:500 份文件的 Body 欄位裡,把舊公司名「Acme」全部換成「AcmeCorp」;順便統計每份文件裡有幾個 doclink。站上先前那篇 NotesRichTextItem 能讓你拿到整個 Body 欄位、也能 AppendText,但它給的是「整塊」 — 它沒辦法讓你說「走到下一個表格」「選中這一段然後換字」。

要做到這種細粒度的走訪與改寫,Domino 從 Release 6 起補了兩個搭檔:NotesRichTextNavigator(在 rich text 裡「移動游標」)跟 NotesRichTextRange(選定一段、然後對它動手)。這篇拆解它們怎麼配合,以及 FindAndReplace 一個不講就會中招的陷阱。


重點摘要

  • 兩者都從 NotesRichTextItem 生出來:body.CreateNavigator()body.CreateRange()
  • Navigator = 游標:用 FindFirstElement / FindNextElement 配合 element-type 常數(RTELEM_TYPE_TABLERTELEM_TYPE_DOCLINK…)一個一個走
  • 限制:navigator 只能在「同一種型態」之間走 — 找得到「下一個表格」,但沒有「不分型態的下一個元素」
  • Range = 選取範圍:用 SetBegin / SetEnd(搭配 navigator)劃定範圍,再 SetStyle 套樣式、Remove 刪除、讀 TextRun / TextParagraph
  • FindAndReplace(target, replacement, options) 回傳替換次數;選項可組合:RT_FIND_CASEINSENSITIVERT_REPL_ALL
  • 鐵則FindAndReplace 之後,該 rich text item 裡所有 navigation marker(含 range)全部失效 — 要繼續用得重建

從 NotesRichTextItem 生出兩個工具

兩個類別都不是 New 出來的,而是從你已經拿到的 rich text 欄位身上長出來:

Dim body As NotesRichTextItem
Set body = doc.GetFirstItem("Body")
Dim rtnav As NotesRichTextNavigator
Set rtnav = body.CreateNavigator()
Dim rtrange As NotesRichTextRange
Set rtrange = body.CreateRange()

分工很清楚:navigator 負責「移動到哪」,range 負責「對選中的那段做什麼」。 兩者常常一起用 — navigator 先定位,再把位置交給 range 當邊界。

NotesRichTextNavigator:依元素型態走訪

官方定義:「Represents a means of navigation in a rich text item.」 它維護一個「目前位置」,你用 Find 系列方法去移動它:

方法作用
FindFirstElement(type)移到第一個某型態的元素,回傳 Boolean
FindNextElement(type)移到下一個同型態元素
FindNthElement(type, n)移到第 n 個
FindFirstString(s) / FindNextString(s)移到字串出現處
GetElement()取得目前位置的元素

type 用的是 RTELEM_TYPE_* 常數,涵蓋 rich text 裡各種「東西」:

常數元素
RTELEM_TYPE_TEXTPARAGRAPH文字段落
RTELEM_TYPE_TEXTRUN文字 run(同樣式的一段)
RTELEM_TYPE_DOCLINKdoclink
RTELEM_TYPE_FILEATTACHMENT附件
RTELEM_TYPE_OLEOLE 物件
RTELEM_TYPE_SECTION可收合區段
RTELEM_TYPE_TABLE表格
RTELEM_TYPE_TABLECELL表格儲存格

⚠️ 最關鍵的限制,官方原文:「Navigation is within elements of the same type… You cannot find or get an element regardless of type.」 — navigator 只能在同型態之間移動。沒有「不管什麼型態、給我下一個元素」這種操作。你要走訪 Body,得先決定「我這趟要找哪一種」,一種一種掃。

走訪典型寫法就是 FindFirstElement 開頭、FindNextElement 收尾的迴圈:

If rtnav.FindFirstElement(RTELEM_TYPE_DOCLINK) Then
Dim n As Integer
Do
n = n + 1
Loop While rtnav.FindNextElement(RTELEM_TYPE_DOCLINK)
End If
' n = 這份文件 Body 裡的 doclink 數量

NotesRichTextRange:選一段、然後動它

官方定義:「Represents a range of elements in a rich text item.」 預設範圍涵蓋整個 item。它的唯讀屬性讓你「看」這段是什麼:

屬性內容
Type範圍裡第一個元素的型態
TextRun從起點到下一個換行或樣式變化的文字
TextParagraph從起點到下一個段落的文字
Style第一個 text run 的樣式
Navigator一個被限制在此範圍內的 navigator

而方法讓你「動」這段:SetBegin(nav) / SetEnd(nav) 用 navigator 劃定起訖、SetStyle(style) 把樣式套到整段、Remove() 刪掉範圍內容、Reset() 還原成預設、Clone() 複製。

把選定範圍的字級改大,就是官方範例的做法:

Set body = doc.GetFirstItem("Body")
Set rtrange = body.CreateRange
Set rtstyle = session.CreateRichTextStyle
rtstyle.FontSize = 18
Call rtrange.SetStyle(rtstyle)
Call doc.Save(True, True)

FindAndReplace:跨 rich text 找字換字

FindAndReplaceNotesRichTextRange 最實用的方法。簽章:

count& = rtrange.FindAndReplace(target$, replacement$, [options&])

回傳值是實際替換的次數Long)。options 可以用加法或 Or 組合:

常數意義
RT_FIND_CASEINSENSITIVE1不分大小寫(預設分)
RT_FIND_PITCHINSENSITIVE2不分字距
RT_FIND_ACCENTINSENSITIVE4不分重音
RT_REPL_PRESERVECASE8替換時保留原大小寫
RT_REPL_ALL16替換全部出現處(預設只換下一個)

把 Body 裡所有「Acme」換成「AcmeCorp」、不分大小寫:

Dim rtrange As NotesRichTextRange
Set rtrange = body.CreateRange
Dim cnt As Long
cnt = rtrange.FindAndReplace("Acme", "AcmeCorp", _
RT_REPL_ALL Or RT_FIND_CASEINSENSITIVE)
If cnt > 0 Then Call doc.Save(True, True)

⚠️ 這裡有個一定要記住的陷阱,官方原文:「All navigation markers in the RTitem, including ranges, are invalidated after a FindAndReplace operation.」 — FindAndReplace 跑完之後,這個 rich text item 上所有的 navigator 跟 range 全部失效。如果你還要繼續走訪或再選範圍,必須用 SetBegin / SetEnd 重建一個新的 range,不能沿用舊的。很多「換完之後程式就怪怪的」都是栽在這裡。

同類別在其他語言

這組在三種語言都有,名字也一致:

語言對應類別建立方式
Java(lotus.domino.*RichTextNavigator / RichTextRangertitem.createNavigator() / createRange()
SSJS / XPagesRichTextNavigator / RichTextRange同上

觀念完全共通:navigator 走訪、range 選取改寫、element-type 常數那一套都一樣。差別只在 Java / SSJS 用方法呼叫(findFirstElement(...))而非屬性語法,且常數在 Java 端是 RichTextItem.RTELEM_TYPE_* 的形式。要寫後端 Java 版批次處理 rich text 時,這篇的流程可以直接搬。

參考來源

← 回到文章列表