2025/09/13

配列の次元数を取得する方法

今回は超個人的な備忘録です。

通常の業務アプリで必要となるシーンは思い浮かばないのですが、先日、汎用的なツールを作成しているときに必要となった処理です。それは、Variant 型変数に配列が入っていた時にその配列の次元数を確認する方法です。


配列であるかの確認

Variant 型 に配列が入っているかは、IsArray 関数で判定できます。

If IsArray(vUnknown) = True Then
   'vUnknown は配列
Else
   'vUnknown は配列ではない
End If


要素数の確認と次元

上記関数では、配列かどうかの判定はできますが、その次元数は判定できません。LotusScript にそれを取得する関数がないようなので、自作することにしました。

利用したのは UBound(LBound でも可)関数です。配列の要素数の最大値(最小値)を取得する関数なのですが、2 つ目の引数に次元を指定できます。

UBound ( arrayName [ , dimension ] )

例えば、3 次元配列の場合、以下のように記述すれば各次元の要素数の最大値が取得できます。

Dim aiArray(5, 3, 15) As Integer
Dim iMax1 As Integer
Dim iMax2 As Integer
Dim iMax3 As Integer
iMax1    = UBound(aiArray, 1)     ' 5
iMax2    = UBound(aiArray, 2)     ' 3
iMax3    = UBound(aiArray, 3)     ' 15


次元数の確認

UBound で存在しない次元数を指定した場合、エラーが発生します。これを利用して Variant 変数内の次元数を確認します。

次元数を返す関数のサンプルは次の通りです。

Function GetDimension(vaArray As Variant) As Integer
   Dim i As Integer
   Dim j As Integer

   On Error GoTo GeneralError

   For i = 1 To 8
      ' 存在しない次元を指定するとここでエラーになる
      j = UBound(vaArray, i)

      'エラーがないのその次元は存在
      GetDimension = i
   Next

ExitFunc:
   Exit Function

GeneralError:
   Resume ExitFunc
End Function


もし引数に配列でない値がセットされた場合には、初回の UBound でエラーが発生するので、0 が返されます。


2025/09/12

QueryAccessPrivileges の判定と演算子の活用

前回 の記事の中で ACL のオプションを取得する QueryAccessPrivileges メソッドを紹介しました。このメソッドの戻り値は、以下の値の組み合わせだと紹介しました。例えば、文書の作成ができて、削除のできる場合には、1 + 2 の 3 となります。これにパブリック文書の読者権限も持っていると 67 となります。

上記のように、戻り値で組み合わされた値が返ってきたとき、あなたならどのように判定しますか?

定数
1 DBACL_CREATE_DOCUMENTS
2 DBACL_DELETE_DOCUMENTS
4 DBACL_CREATE_PRIV_AGENTS
8 DBACL_CREATE_PRIV_FOLDERS_VIEWS
16 DBACL_CREATE_SHARED_FOLDERS_VIEWS
32 DBACL_CREATE_SCRIPT_AGENTS
64 DBACL_READ_PUBLIC_DOCUMENTS
128 DBACL_WRITE_PUBLIC_DOCUMENTS
256 DBACL_REPLICATE_COPY_DOCUMENTS


オプションの値の意味

話を単純化するために上から 4 つの場合を考えます。オプションの値は 1、2、4、8 で、取りうる値(組み合わせ)は 0 から 15 となります。列にオプションの値、行に取りうる値を整理すると次のようになります。

1 2 4 8
0 × × × ×
1 × × ×
2 × × ×
3 × ×
4 × × ×
5 × ×
6 × ×
7 ×
8 × × ×
9 × ×
10 × ×
11 ×
12 ××
13 ×
14 ×
15

これですぐわかりますね。オプションの値は 2 進数の各ビットの値を表すということになります。


判定方法

では、13 を例にどのビットが立っているのか考えてみます。大きいビットから考えて、

  1. 8 で割って 1 以上(〇)
  2. 8 で割った余り 5 を 4 で割って 1 以上(〇)
  3. 4 で割った余り 1 を 2 で割って 1 未満(×)
  4. 2 で割った余りが 1(〇)

というよな判定となります。

これを逐一 If 文で書くと結構な分量になります。また、QueryAccessPrivileges のように 256( = 9 ビット)までとなるととんでもない分量になることが想像できます。

そこで、プログラミングらしく、ループを使って効率的に判定する方法を考えてみましょう。

Sub Initialize
   Dim iBit As Integer
   Dim iSrc As Integer    'サンプル地
   Dim iOpt As Integer   'オプションの値

   iSrc = 13

   '上のビットから順に処理
   For iBit = 3 To 0 Step -1
      'ビットの値を算出
      iOpt = 2 ^ iBit

      'ビットが立っているか判定
      If iSrc / iOpt >= 1 Then
         MsgBox CStr(iOpt) & " = 〇"
      Else
         MsgBox CStr(iOpt) & " = ×"
      End If

      'ビットの値の余りを算出
      iSrc = iSrc Mod iOpt
   Next
End Sub

べき乗の演算子(^)と剰余を求める命令(mod)を使い、上のビットから演算を進めることで、ループを効率的に活用しています。

なお、If 文の判定では、判定方法に記載した通り割り算を使用しましたが、以下のように書き換えることができます。

      If iSrc >= iOpt Then

割り算した結果を比較して判定するより、整数値を比較するだけの方が明らかに演算コストが安いですよね。


論理演算の動作

もっと簡単に判別する方法はないでしょうか? 実はあるんです。

それは、論理演算子 And を使用する方法です。If 文で複数の条件を記述する際に利用するあの And のことです。

      If a = b And b = c Then

And 演算子は上記のような使い方のほかに 2 つの値のビットを比較し、その結果を返す機能があります(上記判定もその一部)。事例に挙げた 13 の場合と 3 の And の結果は次のようになります。

   13 And 8 = 1101 And 1000 = 1000 = 8
   3 And 8 = 0011 And 1000 = 0000 = 0

And 一つあればそのビットが立っているか判定できることがわかります。


論理演算の活用

最初の表の定数は 1, 2, 4, 8, 16 … と各ビットの値が定数化されています。この定数と And とを活用すると保持しているオプションが ”ワンパン” で判定できます。

例えば『個人フォルダ/ビューの作成』を持っているか判定する場合、次のように記述します(iOpt に QueryAccessPrivileges の戻り値が入っている前提)。

   If (iOpt And DBACL_CREATE_PRIV_FOLDERS_VIEWS) > 0 Then


まとめ

今回は、QueryAccessPrivileges の戻り値を題材に論理演算子の活用方法についてまとめました。

ご存じの通り、コンピュータは 2 進数で動作しています。それを活用した演算子や言語仕様が各所にちりばめられています。10 進数で姓つしている私たちには気が付きにくいのですが、うまく活用することで、超お得になることがあります。

デザイナーヘルプやサンプルを見て、2 進数っぽいものが出てきたら調べてみましょう。お宝が発見できるかもしれませんよ。


2025/09/11

アクセス権限とロールの取得 - LotusScript

前々回 は DB に対してアクセスレベルと文書の削除権限などのオプション設定を取得する関数である @UserAccess、前回 はロールを取得する @UserRoles を紹介しました。同様の情報は LotusScript  でも取得可能です。

使用するのは NotesDatabase クラスのメソッドとなります。順に確認しましょう。


アクセスレベルの取得

データベースに対する現在のアクセスレベルを返します。構文は次の通りです。

level% = notesDatabase.QueryAccess( name$ )

引数は1つです。

1 name$ 文字列 ユーザ、グループ、または、サーバ名

@UserAccess との使い方の違いは、

  • 調査する DB が引数ではなく、DB オブジェクトを取得してそのメソッドをコールする
  • 現在のユーザだけでなく、調査する名前を引数で指定する
  • 引数にはユーザだけでなく、グループやサーバ名を指定できる

点となります。

また、戻り値についても注意が必要です。@UserAccess と同じく 0(なし)~ 6(管理者)を返すのですが、型は ”文字列” ではなく Integer 型となる点にも注意してください。


オプションの取得

文書の作成や削除権限など ACL のオプション設定を取得するには、QueryAccessPrivileges メソッドを利用します。

privileges& = notesDatabase.QueryAccessPrivileges( name$ )

使い方や引数は、QueryAccess メソッドと同じでです。

戻り値は数値なのですが、少し特殊で、次の値の組み合わせで返します。これは複数のオプションが指定されることへの対処ですね。判定時には、値と定数のどちらでも使用できます。

定数
1 DBACL_CREATE_DOCUMENTS
2 DBACL_DELETE_DOCUMENTS
4 DBACL_CREATE_PRIV_AGENTS
8 DBACL_CREATE_PRIV_FOLDERS_VIEWS
16 DBACL_CREATE_SHARED_FOLDERS_VIEWS
32 DBACL_CREATE_SCRIPT_AGENTS
64 DBACL_READ_PUBLIC_DOCUMENTS
128 DBACL_WRITE_PUBLIC_DOCUMENTS
256 DBACL_REPLICATE_COPY_DOCUMENTS

定数名を見ればどのオプションかはすぐ分かるので、説明は割愛しています。

@UserAccess との違いは『文書を複製またはコピー』オプションも含めて処理してくれる点です。


ロールの取得

ロールを取得するには、QueryAccessRoles メソッドを利用します。使い方や引数は、上記メソッドと同じです。

roles = notesDatabase.QueryAccessRoles( name$ )

戻り値は String 型の配列となります。


名前の指定方法

引数はすべてのメソッドで共通ですが、指定方法には少しだけ注意が必要です。ユーザ名を指定する場合、階層付きでつける必要があります。


指定するユーザ名
× User01 Notes
User01 Notes/Domino
CN=User01 Notes/O=Domino


まとめ

今回は、LotusScript でアクセス権限やオプション、ロールを取得する方法をまとめました。これら機能は @UserAccess や @UserRoles に比べ後発の機能なので、使用がスッキリまとまっていますね。


2025/09/09

ロールの取得 - @UserRoles

前回は接続ユーザのアクセスレベルや文書削除権限などのオプション設定を取得する関数 @UserAccess を紹介しました。今回は同じく ACL 設定からロールを取得する @UserRoles を紹介します。


機能と構文

 @UserRoles は、現在のユーザが持つロールを取得することができます。構文次の通りなのですが、引数がありませんので、構文というほどのものではありませんね...

@UserRoles

戻り値は文字列リストで、現在のユーザが持つロールが列挙されます。また、戻り値内のロール名は [ ](角括弧)で囲まれた文字列となります。


利用例

ロールの利用例として一般的なのは、アクションボタンの非表示だと思います。上記 ACL 設定を例にすると、[Approver] ロールを持ったユーザのみ[承認]ボタンを表示するというものです。具体的には次のように記述します。


!(
   @IsMember("[Approver]"; @UserRoles)
)

@UserRole の戻り値はリスト値となるので、それを前提とした式の記述が必要です。上記式では @IsMember を利用してリスト値内に存在するかを確認しています。

アクションボタンの非表示やリスト値の演算に関しては以下の記事も参考にしてください。


2025/09/08

アクセスレベル(ACL)の取得 - @UserAccess

ACL の設定で文書を作成する権限がないのに[新規作成]ボタンが表示されるのはおかしいとエンドユーザやお客様から指摘されたことはありませんか? こんな時に利用できるのが @UserAccess という関数です。今回はこの関数の動作についてまとめます。


機能と構文

 @UserAccess は、引数で指定したデータベースに対して、現在のユーザのアクセスレベルを含む、アクセス情報を詳細に確認することができます。構文は次の通りです。

@UserAccess( server :file ; [ accessPrivilege ] )

引数は次の通りです。

1 server  文字列 サーバ名
file  文字列 データベースのファイルパス
2 accessPrivilege キーワード 省略可能
特定のアクセス情報を指定して取得する場合に利用

最初の引数は、サーバ名とファイル名をリストで指定するタイプで、@DbLookup など他の関数と同様です。通常は、自分自身の DB を調査することになるので @DbName を指定することになりますね。


戻り値(キーワードなし)

まずは、キーワードを省略した場合の戻り値の確認です。

@UserAccess(@DbName)

この式をフィールド(複数値可)のデフォルト式にセットします。作成者ユーザでテストした結果は次のようになります。

最初がアクセスレベルで、0(なし)~ 6(管理者)を表します。それ以降は各オプションの設定(1 でチェック)となります。非常にわかりやすいのですが、いくつか注意点があります。

1つ目は、戻り値は文字列リストである(数字だが ”数値” ではない)ことです。この結果を使った非表示式を記述するときなどでは、混乱しがちとなりますので注意してください。

2つ目は、オプションに『文書を複製またはコピー』が含まれない点です。このオプションは、@UserAccess がリリースされて以降に追加されました。既存のアプリに影響が出ないよう配慮されているからです。その代わりとして、省略可能な引数 accessPrivilege が追加され、そのオプションを取得できるように拡張されています。

互換性を保ちつつ、機能追加も行われた結果ということですね。


引数 accessPrivilege の使い方

この引数を使うと特定の権限だけの結果を得ることができます。例えば、先の『文書を複製またはコピー』の設定を取得するのであれば、次のように指定します。『文書を複製またはコピー』にチェックがあれば ”1”、なければ ”0” を返します。

@UserAccess(@DbName; [REPLICATEORCOPYDOCUMENTS])

指定できるキーワードは、次の通りです。

[ACCESSLEVEL] アクセスレベル
[CREATEDOCUMENTS] 文書の作成
[DELETEDOCUMENTS] 文書の削除
[CREATEPERSONALAGENTS] 個人エージェントの作成
[CREATEPERSONALFOLDERSANDVIEWS] 個人フォルダ/ビューの作成
[CREATELOTUSSCRIPTJAVAAGENTS] LotusScript/Javaエージェントの作成
[CREATESHAREDFOLDERSANDVIEWS] 共有フォルダ/ビューの作成
[READPUBLICDOCUMENTS] パブリック文書[読者]
[WRITEPUBLICDOCUMENTS] パブリック文書[作成者]
[REPLICATEORCOPYDOCUMENTS] 文書を複製またはコピー

また、キーワードは :(コロン)を使って複数指定でき、戻り値はその要素だけを含んだリスト値となります。


まとめ

今回は @UserAccess 関数を紹介しました。これを利用すれば、ACL で設定したアクセスレベルやオプションを判定できます。例えば、この記事の最初に記載した『文書を作成する権限がないユーザには[新規作成]ボタンを表示しない』式は次の通りとなります。

!(
   @UserAccess(@DbName; [CreateDocuments])="1"
)

ここまで詳細な非表示の制御は、個人的には費用対効果が低いと考えており、設定していません。ただ、「できない」と「できるけどしない」は大きな違いだと思います。


2025/09/05

LotusScript の実行確認と NotesLog クラス

LotusScript のプログラムを作成すると、開発中やデバッグ中はもちろん、運用中の DB でもどのように処理が行われたのか確認したくなります。手段としては、

  1. デバッガで処理状況を確認
  2. MessageBox で処理状況を表示
  3. Print 文で処理状況を表示
  4. 実行ログを出力

というような方法が考えられます。主に、1 と 2 は開発中、3 と 4 は運用中でも利用できる方法となりますね。

今回は、4 の実行ログの出力方法についてまとめます。


NotesLog クラス

LotusScript の標準クラスには NotesLog クラスが装備されています。これを利用すると、簡単に実行ログを残すことができます。

このクラスは、出力先が選べることが特徴です(同時に複数指定可能)。

  • ノーツアプリケーション(データベース)
  • メール
  • テキストファイル(ローカル実行の場合)
  • エージェントログ(エージェントの場合)


利用の流れ

NotesLog クラスの基本的な利用方法は次の通りです。

Sub Initialize
   'ログを作成
   Dim nlog As New NotesLog("NotesLog クラスのテスト")

   'ログをオープン(出力先を指定)
   Call nlog.OpenAgentLog()

   '通常のログを出力
   Call nlog.LogAction("通常のメッセージ...")

   'エラーログの出力
   Call nlog.LogError(1000, "エラーが発生しました!")

   '最後にログをクローズ
   Call nlog.Close()
End Sub

NotesLog オブジェクトを新規作成した後、ログをオープンします。このオープンのメソッドが出力先ごとに存在し、このサンプルではエージェントログを指定しています。複数のログを同時にオープンすることで、同時に複数出力できます。

準備が整った後は、ログを出力します。LogAction は通常のログメッセージ、LogError はエラー用となります。

ログの出力が終わったら、Close メソッドでログの終了を宣言します。


では、ログの出力先の指定であるオープンのメソッドを順に確認しましょう。


◇ ノーツアプリケーション(データベース)に出力

OpenNotesLog メソッドは、ノーツデータベースにログを文書として出力します。引数は出力するデータベース(サーバとファイルパス)で、指定すると、その DB 内にノーツの文書として出力します。

Call notesLog.OpenNotesLog( server$, dbfile$ )

出力先のデータベースは、テンプレートの『エージェントログ』から作成しておきます。

ログが出力されると、ビューに文書が追加されます。この機能をうまく使えばログの一元管理が可能ですね。

ところで、実際の出力結果は次のようになりました。

まず、ログの出力ごとに文書が生成されるようです。先のコードを実行したところ、通常のログとエラーログは別文書となりました(1回の実行で2文書作成)。

また、ログタイプにはどちらも × が表示されており、両方がエラーだったように見えます。ビューの設計を確認したところ、"Action" でなければ × となるようになっています。

ところが、出来上がった文書は、”アクション” となっています。これは明らかに日本語化のバグですね...。このままではあまりに悲しいので、テンプレートからの引継ぎをせずに、ビューを修正しておきましょう。


◇ メール

OpenMailLog メソッドを使えば、ログをメールで送信することができます。引数は、メールの宛先と件名となります。

Call notesLog.OpenMailLog( recipientsV, subject$ )

実行すると次のようにメールが送信されます。メールの場合は一連のログが1通のメールとなって送信される仕様のようですね。

◇ テキストファイル

OpenFileLog メソッドでは、ログをローカルのテキストファイルとして保存します。引数は、出力するテキストファイルの名前(パス含む)となります。

Call notesLog.OpenFileLog( path$ )

実行すると引数で指定したファイルにログが出力されます。


◇ エージェントログ

LotusScript で記述されたエージェントの場合、OpenAgentLog メソッドを使えば、ログをエージェントログに出力することができます。

Call notesLog.OpenAgentLog

実行が終了した後、エージェントの右クリックメニューより[ログの表示]を選択することで確認できます。このログでは最後の実行分のログしか確認できない点に注意が必要ですね。


2025/09/03

ダイアログボックスの再利用

数回にわたってダイアログボックスの作り方についてまとめてきました。今回のその一連の記事の最後になります。

画面の作り方から始まり、初期値の与え方、入力チェックなど、実際のアプリ作成で必要となる手法を紹介しました。自由自在に作れるようになってくると、同じダイアログボックスを他のフォームや他のアプリで使いまわしたくなります。そこで、今回は、再利用についてまとめたいと思います。


ダイアログ再利用の例

例えば、下図のような部門選択ダイアログがあったとします。この画面をアプリ A、B、C で使うことを想像してください。各フォームでは、部と課のフィールド名が違います。特に、アプリ C では、1 つのフォームで 2 つの部門を必要としています。

このような場合どのように作るべきでしょうか?

(2025/9/5 間違い訂正のため画像を差し替えました)

@DialogBox で作ることを前提にすると、アプリ A と B では、部と課のフィールド名が違うダイアログフォームを作ることになります。アプリ C に至ってはフィールド名が異なるダイアログフォームを 2 種類用意する必要があります。

これでは、似て非なるフォームが多数生成され、煩雑で非効率で、将来のメンテナンス性は悪いと言わざるを得ません。しかし、この問題は、LotusScript の DialogBox メソッドを使えば、解決できます。


ダイアログフォームの修正

まずはダイアログ用のフォームを汎用的となるよう修正します。フォーム名には ”汎用” とわかるよう ”Std” を付け、フィールドには "dlg" と接頭文字を付加しています。

ポイントはメッセージ欄です。作成時の計算結果として dlgMsg フィールドを配置、初期値にメッセージを記述します。また、作成時の計算結果を指定するとフィールドの背景を透明にでき、メッセージを自然に表示できます。


[選択]ボタンの修正

続いて、呼び出し側のコードを次の通り書きなおします(最初の図のアプリ A の報告書フォームの場合)。

現在の文書 dlgCur とは別に、ダイアログボックス用の新規文書 ndDlg を作成しています。[OK]でダイアログを閉じると If 文に入り、選択結果を現在の文書に反映しています。

Sub Click(Source As Button)
   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim ndDlg As NotesDocument    'ダイアログ用文書
   Dim nuiw As New NotesUIWorkspace
   Dim nuid As NotesUIDocument
   Dim ndCur As NotesDocument    '編集中の文書

   Dim sTitle As String

   '編集中の文書の取得
   Set nuid = nuiw.CurrentDocument
   Set ndCur = nuid.Document

   'ダイアログ文書の準備
   Set ndb = ns.CurrentDatabase
   Set ndDlg = ndb.CreateDocument

   'タイトルのセット
   sTitle = "報告部門"

   '初期値のセット
   ndDlg.dlgMsg = sTitle & "を選択してください。"    'メッセージ

   If True = nuiw.DialogBox("dlgStdSetDept", True, True, , , , , sTitle & "の選択", ndDlg) Then
      '選択結果を反映
      ndCur.Dept = ndDlg.dlgDept
      ndCur.Func = ndDlg.dlgFunc
   End If
End Sub

少々回りくどいコードに見えるかもしれませんが、別フォームで利用する場合を考えれば、この構造の良さがわかります。

例えば、アプリ B、問い合わせ管理フォームの場合は次のようになります。

Sub Click(Source As Button)
         ・・・
   'タイトルのセット
   sTitle = "問い合わせ部門"

   '初期値のセット
   ndDlg.dlgMsg = sTitle & "を選択してください。"    'メッセージ

   If True = nuiw.DialogBox("dlgStdSetDept", True, True, , , , , sTitle & "の選択", ndDlg) Then
      '選択結果を反映
      ndCur.ReqDept = ndDlg.dlgDept
      ndCur.ReqFunc = ndDlg.dlgFunc
   End If
End Sub

ダイアログを一切変更することなく別のフォームでも利用出ることがわかります。


まとめ

社内のアプリ数が増え、メンテナンスすべきコード量が増えてくると、今回のような共通化、効率化が必要となってきます。共通部品を作成し揃えておくと、開発工数の削減だけでなく、アプリの構造がレイヤー分けされ、開発作業の分業化ができるようになります。アプリ開発者はアプリの仕様に専念できるので、エンドユーザに対してより良い活動をする時間が確保できます。

なお、今回の記事には、バグが潜んでいます。何かわかりますか?

共通化に伴い、ダイアログフォーム内のフィールド名を変更しました。その結果 ”課” 用のキーワードの式が正常動作しなくなるのです。忘れず修正しましょう。

@DbLookup("Notes":"NoCache"; ""; "vFunc"; dlgDept; 2)

似て非なる設計要素を作るとこのようなバグが発生しやすくなります。これを抑制するためにも共通化をおススメします。


2025/09/02

ダイアログボックスの入力チェック

これまで数回にわたり、ダイアログボックスの使い方を紹介してきました。前回前々回 は検索条件を入力することをイメージした画面を紹介しました。

このような画面では、必須項目があり、それを入力しないとダイアログボックスを閉じられない( = [OK]ボタンが通らない)というような機能が欲しくなります。

そこで、今回は、ダイアログボックスの入力チェックを紹介します。


実現方法

通常のフォームの場合、入力チェックは保存時(QuerySave イベント)に行います。ですが、ダイアログボックスの場合、文書を保存しないのでこのイベントは使えません。

利用するのは、画面の閉じる前に発生する QueryClose イベントとなります。単純に使用すると[キャンセル]ボタンで閉じるときにもチェックされてしまいます。これを回避するために、ダイアログボックス用の機能、NotesUIDocument クラスの DialogBoxCanceled プロパティを使用します。このプロパティは True の場合、[キャンセル]ボタンでダイアログが閉じられたということうを表します。


入力チェックの例

例えば上記画面で期間(From、To)とエリア(Area)を必須にしたいとします。そのスクリプトは次のようになります。

Sub QueryClose(Source As Notesuidocument, Continue As Variant)
   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim nd As NotesDocument

   '[キャンセル]時はチェックなしで閉じる
   If Source.DialogBoxCanceled Then Exit Sub

   '[OK]時の入力チェック
   Set ndb = ns.CurrentDatabase
   Set nd = Source.Document

   '期間(From)
   If nd.From(0) = "" Then
      Msgbox "期間を入力して下さい。", 16, ndb.Title
      Call Source.GotoField("Form")
      Continue = False
      Exit Sub
   End If
   '期間(To)
   If nd.To(0) = "" Then
      Msgbox "期間を入力して下さい。", 16, ndb.Title
      Call Source.GotoField("To")
      Continue = False
      Exit Sub
   End If
   'エリア(Area)
   If nd.Area(0) = "" Then
      Msgbox "エリアを選択して下さい。", 16, ndb.Title
      Call Source.GotoField("Area")
      Continue = False
      Exit Sub
   End If
End Sub

構造はいたって単純です。

QueryClose は Continue 引数に False に変更する(初期値は True)と、閉じる操作をキャンセルします。そこで、[キャンセル]ボタンの操作であれば、有無を言わさず閉じるよう Exit Sub しています。

その後、対象のフィールドを順にチェックして、未入力であれば、エラーメッセージを表示して、Continue を False にして、処理を終了しています。これで、入力に不足があった場合、[OK]で閉じることができなくなります。


プリビュー時の注意

入力チェックを記述してからプリビュー表示する場合は、注意が必要です。プリビューを閉じるとき、DialogBoxCanceled は False となるので、入力チェック処理が稼働します。すべて入力しないとプリビューが閉じれなくなります。

万一、フィールド名を書き間違えた場合、入力するすべがなく、永久ループに陥ります。こうなると Designer を強制終了するしかないので、十分注意してください。


最後に

ここまで記事を書き終えた時点で何となく既視感があったのでチェックしたところ、昨年 4 月に、全く同じタイトル『ダイアログボックスの入力チェック』で記事を書いていました...。

ダイアログボックス関連の話の流れで仕方がなかったとご理解ください。


2025/09/01

ダイアログボックスと初期値の設定

前回は DialogBox メソッドで引数に文書を指定する方法を 2 つ紹介しました。既存の文書をヘルプのように表示する方法と新規文書を作成して表示する方法です。

後者は、ダイアログボックスとのインターフェースに新規文書を使うイメージですが、その活用の方法について補足します。


使いやすいダイアログを目指して

前回のプログラムでダイアログボックスを表示すると、入力項目は空っぽの状態となり、すべての項目を埋めていく必要があります。

これだとアプリとしては不便です。便利に使うためには、適切な初期値が必要です。


初期値を与える方法 ①

一番単純な方法は、ダイアログ用フォームのフィールドでデフォルト値の式をセットします。

今回のプログラムでは、新規作成した文書をダイアログに渡していたので、文書内にフィールドは存在しません。ですので、ダイアログを開くタイミングでフィールドが生成され、デフォルト値をセットする処理が実行されます。


初期値を与える方法 ②

少し複雑な処理(検索や演算)を行う場合、ダイアログ用フォームの QueryOpen で処理するという方法もあります。LotusScript で記述するので、自由度の高いコーディングが可能です。


初期値を与える方法 ③

新規作成した文書にフィールドを作ってからダイアログボックスを開くことでも初期値を与えることができます。

この方法では、ダイアログの呼び出し側で初期値をセットするので、呼び出し側の状況に応じた初期値が決定できます。よって、ダイアログを複数の用途で利用する場合に特におススメの方法といえます。

例えば、次のコードは、期間は今日から 30 日後、エリアは ”近畿” を初期値としています。

Sub Click(Source As Button)
         ・・・
   Set ndb = ns.CurrentDatabase
   Set nd = ndb.CreateDocument

   '初期値のセット
   nd.From = Today
   nd.To = Today + 30
   nd.Area = "近畿"


   If True = nuiw.DialogBox("dlgSearchCondition", True, True, , , , , "出力条件", nd) Then
      '条件を取得
      vFrom = nd.From(0)
          ・・・
End Sub

この方法であれば、”九州” を初期値にしたい場合でも、簡単に対応できますね。


2025/08/31

DialogBox メソッドと NotesDocument の利用

前回の記事では NotesUIWorkspace クラスの DialogBox メソッドを紹介しました。このメソッドでは @DialogBox とは違いダイアログボックスに表示する文書をしてする引数が存在します。今回は、この引数を使った事例を紹介ます。

flag = notesUIWorkspace.DialogBox( form$ , [autoHorzFit] , [autoVertFit] , [noCancel] , [noNewFields] , [noFieldUpdate] , [readOnly] , [title$] , [notesDocument] , [sizeToTable] , [noOkCancel] , [okCancelAtBottom] )


ヘルプ機能

ノーツの[ヘルプ]メニューから[アプリケーションの使い方]を選択するとそのアプリのヘルプを表示する機能があります。表示するコンテンツはデザイナーで記述する上、アプリで 1 つしか記述できません。これでは自由度がありませんね。ビューやフォームなどの機能ごとにヘルプが表示でき、アプリの運用部門がコンテンツを作成できるようにしておけばよりタイムリーなヘルプとなります。

DialogBox メソッドを使用すれば、このような機能が作成できます。

例えば、”はじめに” という文書を表示する[ヘルプ]ボタンは、次のようなコードとなります。

Sub Click(Source As Button)
   Dim nuiw As New NotesUIWorkspace
   Dim ndHelp As NotesDocument

   ' キーワードに一致したヘルプ文書を取得
   Set ndHelp = GetHelpDoc("はじめに")

   ' 取得したヘルプ文書をダイアログで表示
   Call nuiw.DialogBox(ndHelp.Form(0), , , True, , , True, ndHelp.Title(0), ndHelp, , , True)
End Sub

GetHelpDoc は引数に指定したヘルプ文書を取得する関数をイメージしています(関数の中身やヘルプ文書のフォームは主題ではないので割愛)。

メソッドの引数にはこれまで使っていないものが含まれますので、設定理由とともにすべてを列挙します。

1 form$ フォーム名は文書のままとする
2 autoHorzFit
省略
表やレイアウト領域に合わせせない
3 autoVertFit
4 noCancel キャンセルボタン不要なので True
5 noNewFields 省略
6 noFieldUpdate
7 readOnly 編集する必要はないので True
8 title$ ヘルプ文書のTitle フィールドを設定
9 notesDocument ヘルプ文書を指定
10 sizeToTable 省略
11 noOkCancel
12 okCancelAtBottom ボタンを下に表示したいので True

ボタンをクリックすると[OK]ボタンだけが下部に表示されたダイアログボックスが表示されます。レイアウト領域や表に合わせない設定の場合、ウィンドウサイズは、画面の解像度から適当に判断されるようです。


検索条件の入力

Excel 帳票や CSV ファイルに出力する際、対象データを限定したいことがあります。このような機能の作成にも DialogBox メソッドが使えます。

まず、ダイアログ用のフォームを作成します。

ビューのアクションボタンにこのダイアログを利用した帳票出力機能を作成します。サンプルコードは次の通りです(帳票出力する関数 ExportList は本題ではないので割愛)。

Sub Click(Source As Button)
   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim nd As NotesDocument
   Dim nuiw As New NotesUIWorkspace

   Dim vFrom As Variant           '期間(開始)
   Dim vTo As Variant                 '期間(終了)
   Dim sArea As String               'エリア
   Dim vKeyword As Variant   'キーワード(リスト)

   Set ndb = ns.CurrentDatabase
   Set nd = ndb.CreateDocument

   If True = nuiw.DialogBox("dlgSearchCondition", True, True, , , , , "出力条件", nd) Then
      '入力した条件を取得
      vFrom = nd.From(0)
      vTo = nd.To(0)
      sArea = nd.Area(0)
      vKeyword = nd.Keyword   'ここだけリスト値

      '帳票出力
      Call ExportList(vFrom, vTo, sArea, vKeyword)
   End If
End Sub

ポイントは新規作成した文書を使用している点です。ダイアログボックスで入力された値は、この新規文書内のフィールドとして返ってきます。それを取得することで、入力値を取得しています。

なお、帳票出力後はそのまま処理を終了しています。新規作成された文書は、保存していないので、プログラムの終了と同時に消滅することになります。