NotesUIWorkspace × NotesUIDocument:LotusScript 的前端自動化

NotesUIWorkspace × NotesUIDocument:LotusScript 的前端自動化

2026.06.10 約 1,510 字

站上前面拆過的類別 — NotesDatabaseNotesDocumentNotesView — 全是後端。它們在伺服器上、在背景 agent 裡都能跑,看不到、也不需要看到使用者的螢幕。但有一整類需求它們碰不到:使用者正在表單上打字、還沒按存檔,你想讀他當下螢幕上那個欄位的值;或想在他按下按鈕時跳一個「確定要送出嗎?」的對話框;或把當前開著的文件直接切到編輯模式。

這些都發生在「前端」 — Notes Client 的視窗裡。對應的就是另一半:NotesUIWorkspace(當前工作區視窗)跟 NotesUIDocument(當前開著的文件)。這篇拆解這組前端雙人組,以及一個一旦搞混就會 debug 半天的核心觀念:螢幕上的值,跟後端 Document 存的值,不是同一個東西。


重點摘要

  • NotesUIWorkspaceDim ws As New NotesUIWorkspace 建立,代表「當前工作區視窗」
  • ws.CurrentDocument 拿到 NotesUIDocument — 使用者現在開著、有焦點的那份文件
  • 核心觀念NotesUIDocument 是螢幕上的文件(含未存檔的編輯);它的 .Document 屬性才是後端 NotesDocument(上次存檔的狀態)
  • 讀/寫螢幕上的欄位用 FieldGetText / FieldSetText;存檔前它們不會進後端
  • 跟使用者互動:Prompt(是非 / 輸入 / 清單對話框)、PickListStrings(從清單選人選文件)
  • 鐵則:UI class 不能在背景 agent、API 呼叫的 agent、或 NotesAgent.Run 觸發的 agent 裡跑 — 只有工作站使用者能執行

前端 vs 後端:先搞懂這一個

這是用 UI class 最常踩的坑,先講清楚。假設使用者打開一份文件、在 Subject 欄位把標題從「報價單」改成「報價單(已修訂)」,但還沒按存檔。這時候:

你呼叫的拿到的值為什麼
uidoc.FieldGetText("Subject")報價單(已修訂)讀的是螢幕上的當前值
uidoc.Document.GetItemValue("Subject")(0)報價單讀的是後端上次存檔的值

官方對 NotesUIDocument 的定義是「Represents the document that’s currently open in the Notes workspace」 — 它是螢幕上那份。而它的 Document 屬性,官方寫得很直接:「The back-end document that corresponds to the currently open document.」 — 對應的後端文件。

所以規則很簡單:還沒存檔的編輯只存在於前端。要拿使用者剛打、還沒存的值,走 FieldGetText;要拿已經存進資料庫的值,走 .Document。搞混這兩個,就會出現「我明明改了,程式卻讀到舊值」的鬼打牆。

NotesUIWorkspace:拿到「當前的東西」

NotesUIWorkspace 是進入前端的入口,直接 New

Dim ws As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Set uidoc = ws.CurrentDocument

它的三個 Current* 屬性,分別對應到使用者眼前的三種東西:

屬性回傳官方說明
CurrentDocumentNotesUIDocument「the document in the window that currently has focus」
CurrentViewNotesUIView當前開著的 view
CurrentDatabaseNotesUIDatabase當前開著的資料庫

⚠️ 一個 focus 陷阱:官方提醒,表單裡的程式碼不能假設自己有焦點,除非它掛在 action button 之類的控制項上。在 composite app 或預覽窗格裡,CurrentDocument 可能不是你以為的那一份。把取 CurrentDocument 的程式放在按鈕事件裡最安全。

NotesUIDocument:操作開啟中的文件

拿到 uidoc 之後,最常用的是這組欄位操作方法:

方法作用(官方原文)
FieldGetText(name)「returns the contents of a field you specify, as a string」
FieldSetText(name, value)「Sets the value of a field… The existing contents… are written over」
FieldContains(name, value)檢查欄位是否含某文字
FieldClear(name)清空欄位
FieldAppendText(name, value)附加文字、不蓋掉原內容

最小的官方範例就是把當前文件的 Subject 印出來:

Dim workspace As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Set uidoc = workspace.CurrentDocument
Messagebox( uidoc.FieldGetText( "Subject" ) )

文件狀態的控制則靠這些:Save() 存檔、Close() 關閉、Refresh()(官方:「its computed fields are recalculated」重算計算欄位)、Reload() 把後端的變更重抓回前端。還有幾個常用唯讀/讀寫屬性:IsNewDoc(「a document that hasn’t been saved」尚未存檔的新文件)、EditMode(讀寫,是否在編輯模式)、ModifiedSinceSaved(有沒有未存變更)。

FieldSetText 還是 Document.ReplaceItemValue?

兩個都能改欄位,但時機不同:

  • FieldSetText — 改的是螢幕上的值,使用者馬上看得到,也會觸發表單上的相依邏輯。適合「使用者正開著文件、你要即時改他看到的內容」。
  • Document.ReplaceItemValue(走後端 NotesDocument)— 改的是後端值,不碰 UI。適合背景處理、或不需要視覺回饋的程式化修改。

簡單記:人正看著螢幕、要即時反應 → FieldSetText;其餘 → 後端 Document。

跟使用者互動:Prompt 與 PickList

前端類別最實用的就是「問使用者」。Prompt 一個方法包辦多種對話框,官方定義:「Displays a dialog box and returns a value based on your actions in the dialog box.」

Dim ws As New NotesUIWorkspace
Dim ans As Variant
ans = ws.Prompt(PROMPT_YESNO, "確認", "確定要送出這張報價單嗎?")
If ans = 1 Then
Call uidoc.Save()
End If

用第一個參數的常數決定對話框型態,常見的有:

常數對話框回傳
PROMPT_OK只有 OK
PROMPT_YESNO是 / 否1 / 0
PROMPT_YESNOCANCEL是 / 否 / 取消1 / 0 / -1
PROMPT_OKCANCELEDIT文字輸入框輸入的字串
PROMPT_OKCANCELLIST單選清單選中的字串
PROMPT_OKCANCELLISTMULT多選清單字串陣列

簽章是 ws.Prompt(type%, title$, prompt$ [, default] [, values]) — 清單型態時,把選項用 values 陣列帶進去。

要讓使用者「從一個 view 裡挑文件」,則用 PickListStrings(官方:「Creates a string array from a list selected by the user」)或 PickListCollection(回傳 NotesDocumentCollection)。這比自己刻一個選擇清單省事得多。

一定要知道的限制:UI class 不能在背景跑

這是 UI class 跟後端類別最大的分界,也是新手最常撞的牆。官方原文:

「You cannot use the UI classes in a background agent, an agent called through an API, or an agent called by the NotesAgent Run method. Only workstation users can run scripts that access UI objects.」

換句話說:只有工作站上、真人操作的情境才能用 UI class。 排程 agent、Web agent、被 NotesAgent.Run 觸發的 agent 裡放 NotesUIWorkspace,輕則拿到 Nothing、重則報錯。需要在背景改文件,就老老實實走後端 NotesDatabase / NotesDocument

這條限制其實正好對應前面的「前端 vs 後端」:UI class 的存在前提就是「有個使用者、有個螢幕」。沒有螢幕的場景,本來就不該用它。

同類別在其他語言

跟 GPS 那組一樣,這次的跨語言結論也是 沒有對應

語言對應類別說明
Java(lotus.domino.*後端 Java API 沒有任何 UI 類別
SSJS / XPagesXPages 是完全不同的元件模型(viewdocument data source、SSJS 事件),不是 NotesUIDocument 的對應

NotesUIWorkspace / NotesUIDocumentLotusScript + Notes Client 專屬的前端類別。要做 Web 端的等價互動,走的是 XPages 那套自己的世界 — 那會是未來另一篇單獨的題目,跟這裡的 client 前端不是同一條路。

參考來源

← 回到文章列表