出直し!! ヘルプ

連載中

連載 終了

2023/09/29

DXL Step-by-Step:#4)文書の更新

今回は DXL で文書を更新する方法についてまとめます。前回取得した文書を更新してみましょう。


フィールドの追加

今回は文書を再保存する方法が主題なので、保存する文書の更新は、テキストフィールドを一つ追加するだけとしたいと思います。

ただ、以前と同じようにベタ書きしてもつまらないので、テキストフィールドを追加する関数 xAppendField_Text を作成しました。これで、メインルーチンでは簡単にフィールドを追加できます。

Function xAppendField_Text( _
                            vddn As NotesDOMDocumentNode, _
                            ByVal vsFldName As String, _
                            ByVal vsText As String) As Boolean
   'document ノード取得
   Dim denCur As NotesDOMElementNode
   Set denCur = vddn.DocumentElement

   'item ノード作成とフィールド名の指定
   Dim denNew As NotesDOMElementNode
   Set denNew = vddn.CreateElementNode("item")
   Call denNew.SetAttribute("name", vsFldName)
   Set denCur = denCur.AppendChild(denNew)

   'text ノードと値のセット
   Dim dtnNew As NotesDOMTextNode
   Set denNew = vddn.CreateElementNode("text")
   Set denCur = denCur.AppendChild(denNew)
   Set dtnNew = vddn.CreateTextNode(vsText)
   Call denCur.AppendChild(dtnNew)
End Function

引数は、文書を表す NotesDOMDocumentNode と 作成するフィールド名 vsFldName、設定する値の vsText となっています。


文書の更新と DocumentImportOption プロパティ

新規文書の保存は、NotesDXLImporter クラスの Import メソッドで、DXL をデータベースに反映しました。文書の更新も同じく Import メソッドを使用します。

ただし、Import メソッドは、文書を保存する設定をコントロールするオプション DocumentImportOption プロパティにより動作が変わります。

DocumentImportOption (NotesDXLImporter - LotusScript®)

具体的には、インポートする DXL を使って文書をどのように処理するか指定することになります。今回は文書の更新なので 5 を指定します。

デフォルト値である 2 は新規文書を作成する設定です。第1回、第2回でこのプロパティを指定せずに新規作成できたのは、デフォルト設定の効果ですね。


文書を保存する関数

DXL を Import する関数も修正します。今回は、保存も更新もできるよう、DocumentImportOption に使用する値を引数に組み込みます。また、DXL もテキストではなく、NotesDOMParser のオブジェクトに変更しています。

Function xImportDXL(vdprs As NotesDOMParser, _
           ByVal viOption As Integer) As Boolean
   'DXL の抽出準備
   Dim nst As NotesStream
   Set nst = xns.CreateStream()
   Call vdprs.SetOutput(nst)
   Call vdprs.Serialize()

   '保存(インポート)
   Dim ndb As NotesDatabase
   Dim dimp As NotesDXLImporter
   Set ndb = xns.CurrentDatabase
   Set dimp = xns.CreateDXLImporter()
   dimp.DocumentImportOption = viOption
   Call dimp.Import(nst.ReadText(), ndb)

End Function


メインルーチン

関数が一通り完成したのでメインルーチンを作成します。

ビューで選択した文書に対するエージェントとして作成し、最初の文書に対して実行します。重要な部分は次の 3 点です。

  1. 第 3 回で作成した関数を利用し、DXL を操作するための NotesDOMParser クラスのオブジェクトを取得
  2. "NewFld" という新規フィールドを作成し、値をセット
  3. 文書を更新として保存

Option Declare
Private xns As NotesSession

Sub Initialize
   Dim ndb As NotesDatabase
   Dim ndc As NotesDocumentCollection
   Dim nd As NotesDocument

   Set xns = New NotesSession
   Set ndb = xns.CurrentDatabase
   Set ndc = ndb.UnprocessedDocuments
   Set nd = ndc.GetFirstDocument()

   Dim dprs As NotesDOMParser
   Set dprs = xDocToDXL(nd)
   Call xAppendField_Text(dprs.Document, "NewFld", "#4 文書の更新")
   Call xImportDXL(dprs, 5)

End Sub


エージェントの実行

ビューで第 2 回で新規作成した文書に対して実行してみます。結果は次のように、希望通りフィールドが追加されました。

ところが、同じ文書に対してもう一度エージェントを実行すると、同じ名前のフィールドがもう一つ追加されてしまいました !?

DXL でフィールドを追加する場合、同名のフィールドが存在するかは関係なく追加できてしまうようです。実際にアプリで利用する際には、NotesDocument クラスの ReplaceItemValue のような関数が必要となりそうですね。


前回 DXL Step-by-Step 次回

2023/09/27

DXL Step-by-Step:#3)文書を DXL で取得

今回は、既存文書を DXL でアクセスする方法を整理します。前回作成した新規文書を DXL を取得して、挙動を確認します。


既存文書を DXL で取得

既存文書を DXL でアクセスする流れは次の通りです。

  1. NotesDXLExporter の入力に文書をセット
  2. NotesDOMParser の入力に NotesDXLExporter のオブジェクトをセット
  3. NotesDXLExporter の Process メソッドで DXL の変換を実行
具体的なコードは次の通りです。今回は後続のプログラムで取得した DXL を操作することを前提に、NotesDOMParser クラスのオブジェクトを返す関数として作成してみました。

Function xGetDOMParser(vnd As NotesDocument) As NotesDOMParser
    Dim ns As New NotesSession

    'Dominoデータ を DXL に変換する準備
    Dim dexp As NotesDXLExporter
    Set dexp = ns.CreateDXLExporter()
    Call dexp.SetInput(vnd)

    'パーサーに変換する DXL をセット
    Dim dprs As NotesDOMParser
    Set dprs = ns.CreateDOMParser()
    Call dprs.SetInput(dexp)

    'DXL 変換を実行
    Call dexp.Process()

    Set xGetDOMParser = dprs
End Function


既存文書の DXL

この関数の引数 vnd は、DXL に変換したい文書を指定します。この引数に前回 DXL を使って作成した新規文書を指定して、DXLを抽出すると次のようになりました。

保存(DXL の Import)時に指定した DXL(黄色網掛け部分)以外にもたくさんの項目が出力されています。文書 ID や作成日/作成者など、ノーツ文書の基本的な情報が入っています。DXL で文書を保存する場合も、必要最低限の情報を指定すれば、必要な情報は自動で付加されることがわかります。

NotesDocument クラスを使用して文書を作成しても、フィールドの設定以外は細かな操作は行わず保存するだけなので、同じようなものですね。


前回 DXL Step-by-Step 次回

2023/09/23

DXL Step-by-Step:#2)文書の新規作成 - DOM クラスの利用

前回 は、DXL を使って文書を新規作成する方法を紹介しました。ただ、DXL の作成は文字列を連結しただけの単純な方法でした。今回は、その DXL の作成を DXL 関連の Notes DOM クラスを使用して作成する方法を整理します。

プログラムの構造は 前回 と揃えています。よって、修正するのは、DXL の文字列を作成する関数 xGetDXL だけとなります。関数で作成する DXL は前回と同じ構造の次の通りとします。

<document form='DXL_SbS'>
   <item name='Title'>
      <text>
         #2 DOM クラスで DXL 文字列を作成
      </text>
   </item>
</document>


DOMクラスの利用準備

まず、DOM クラスを利用して DXL を操作するためには、NotesDOMParser クラスが必要でした。このクラスは、DXL を LotusScript で扱いやすくなるよう、構造(DOM 文書ツリー)をオブジェクトに変換する役割を担います。

NotesDOMParser オブジェクトの Document プロパティにより取得できる NotesDOMDocumentNode が XML 全体を表し、文書ツリーのルートとなります。

   Dim ns As New NotesSession
   Dim dprs As NotesDOMParser
   Dim ddn As NotesDOMDocumentNode
        ・・・
   Set dprs = ns.CreateDOMParser()
   Set ddn = dprs.Document

今回は新規文書の作成ですので、DXL をゼロから作成することになります。よって、上記のシンプルなコードで OK です。

ちなみに ddn の値をデバッガで確認すると次の通り、中身は空っぽとなっています。


ノードの作成

ルートの準備ができたので、DXL の中身を追加します。最初は、文書を表す <document> ノードです。

DXL でノードを追加する方法は少し特殊です。

まず、ノードを作成するメソッドは、ルートである NotesDOMDocumentNode オブジェクトにしか存在しません。複数ある Create?????Node というメソッドで、それぞれタイプの違うノードを作成します。

これらメソッドを使って必要なノードを作成して、各ノードオブジェクトの AppendChild メソッドでそのノード配下のノードとしてセットします。

   Dim denCur As NotesDOMElementNode
   Dim denNew As NotesDOMElementNode
        ・・・
   Set denNew = ddn.CreateElementNode("document")
   Call denNew.SetAttribute("form", "DXL_SbS")
   Set denCur = ddn.AppendChild(denNew)

あえて例えるなら、一旦文書を作成しておいて、あとから返答文書にセットするイメージでしょうか?ノーツ屋としてわからなくもないですが、ちょっと回りくどいですね...

なお、<document> ノードは、フォーム名を form 属性で指定します。SetAttribute メソッドを使用してセットしています。属性はオブジェクトに直接追加できるようですね...


ここまで実行すると、ルートオブジェクトの ddn の NumberOfChildNodes が 1 となりノードの追加が成功したことがわかります。また、DocumentElement や FirstChild などの子ノードにアクセスするためのプロパティにも値がセットされています。


フィールドの作成

文書を表す <document> ノードが作成できたので、次は、フィールドである <item> ノードを追加します。

ノードの追加なので、<document> ノードと同様の手順で作成します。注意すべきは、親となるノードが <document> となるだけです。

   Set denCur = ddn.AppendChild(denNew)

   Set denNew = ddn.CreateElementNode("item")
   Call denNew.SetAttribute("name", "Title")
   Set denCur = denCur.AppendChild(denNew)

AppendChild メソッドの戻り値は、追加したノード自身となります。追加した <document> ノードは denCur 変数で受けていますので、これにフィールドを追加することになります。

このように、DOM クラスでノードを追加するには、親となるノードと追加するノードの2つを操作することになります。変数名を工夫するなど、見やすくしておかないと、あとで見た時に理解に時間がかかりそうです。


フィールドの中身の作成

フィールドを表す DXL は、値の型を表す <text> ノードがあり、そのタグの間に値がべた書きされています。この値を表す部分は、DOM クラスでは、NotesDOMTextNode となります。

具体的には次のように <text> ノードを作成し、それに対して、テキストノードを追加します。テキストノードは NotesDOMTextNode オブジェクトとなるので、別の変数 dtnNew で受け取っています。

   Dim dtnNew As NotesDOMTextNode
        ・・・
   Set denCur = denCur.AppendChild(denNew)


   Set denNew = ddn.CreateElementNode("text")
   Set denCur = denCur.AppendChild(denNew)

   Set dtnNew = ddn.CreateTextNode("#2 DOM クラスで DXL 文字列を作成")
   Call denCur.AppendChild(dtnNew)


DXL を文字列で取得

DXL の構造(DOM 文書ツリー)は、NotesDOMParser が管理していました。ここから、完成した DXL をテキスト文字列で抽出する必要があります。

   Dim nst As NotesStream
        ・・・
   Set nst = ns.CreateStream()
   Call dprs.SetOutput(nst)
   Call dprs.Serialize()

   xGetDXL = nst.ReadText()

まず、SetOutput メソッドを使用して、NotesDOMParser クラスの出力先を指定します。今回は出力先に NotesStream クラスのオブジェクトを使用しました。

NotesStream クラスは、テキストデータやバイナリデータを効率よく扱うためのクラスです。今回は、NotesDOMParser の出力を NotesStream で受け取り、ReadText メソッドで DXL 全体を文字列として取得しています。

なお、NotesDOMParser クラスの Serialize メソッドは、DOM 文書ツリーを検索し XML を出力オブジェクトにストリーミングします。この命令を実行することにより、NotesStream クラスのオブジェクト nst に値がセットされます。


まとめ

xGetDXL 関数の全体を掲載します。

文書を作成して、フィールドを1つ作成するだけですが、なかなか長いコードとなります。NotesDocument クラスなど LotusScript 標準のクラスで実現できることは、わざわざDXL で実施する必要はなさそうですね...

Function xGetDXL() As String
   Dim ns As New NotesSession
   Dim dprs As NotesDOMParser
   Dim ddn As NotesDOMDocumentNode
   Dim denCur As NotesDOMElementNode
   Dim denNew As NotesDOMElementNode
   Dim dtnNew As NotesDOMTextNode

   ' DXL 作成準備
   Set dprs = ns.CreateDOMParser()
   Set ddn = dprs.Document
   
   '<document>ノード作成
   Set denNew = ddn.CreateElementNode("document")
   Call denNew.SetAttribute("form", "DXL_SbS")
   Set denCur = ddn.AppendChild(denNew)

   '<item>ノード作成
   Set denNew = ddn.CreateElementNode("item")
   Call denNew.SetAttribute("name", "Title")
   Set denCur = denCur.AppendChild(denNew)

   '<text>ノード作成
   Set denNew = ddn.CreateElementNode("text")
   Set denCur = denCur.AppendChild(denNew)

   '<text>ノードに値を作成
   Set dtnNew = ddn.CreateTextNode("#1 DXLをDOMクラスから作成")
   Call denCur.AppendChild(dtnNew)

   '作成した DXL をテキストで抽出
   Dim nst As NotesStream
   Set nst = ns.CreateStream()
   Call dprs.SetOutput(nst)
   Call dprs.Serialize()

   xGetDXL = nst.ReadText()
End Function


前回 DXL Step-by-Step 次回

2023/09/22

DXL Step-by-Step:#1)文書の新規作成

『DXL Step-by-Step』シリーズでは、DXL 活用の調査・検証で、実現できたこと、発見したことなどをとりとめなく紹介します。


はじめに

DXL(Domino XML Language)は、ドミノ内の文書や設計を XML アクセスできる機能です。DXL の基本的な情報については、『連載:DXL ことはじめ』にまとめております。はじめての方はこちらを先に参照ください。

連載:DXL ことはじめ』の 第1回 にも書きましたが、DXL を使用すると LotusScript 標準のノーツクラスだけでは操作できないことが実現できます。

例えば、これまでの検証で次のようなことが実現できたと紹介しました。

◇ リッチテキストに対する操作

  • 画像をインラインイメージで添付
  • 表を動的に作成し、罫線や背景色などを設定
  • ボタンなどアクションをコードとともに動的に生成

◇ 設計に対する操作

  • イメージリソースを登録/変更/削除
  • エージェントの実行時間設定など詳細な情報の取得

これまであきらめていた機能が実現できる可能性がありますよね。この連載の中で、順にご紹介していく予定ですのでお楽しみに!


なお、DXL に関しては、現在も調査を進めている段階です。手探りで記述しコードをご紹介することになります。必要に応じて訂正や効率的なコードを改めて記載することになるかもしれません。その点はご容赦ください。


通常の文書作成

第 1 回の今回は、文書の新規作成です。

LotusScript のエージェントで、普通に文書を新規作成するには、次のようなコードになります。

Sub Initialize
   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim nd As NotesDocument

   Set ndb = ns.CurrentDatabase
   Set nd = ndb.CreateDocument()
   nd.Form = "DXL_SbS"
   nd.Title = "#1 LotusScript から普通に文書を作成"

   Call nd.Save(True, False)
End Sub

データベースに対して、CreateDocument メソッドで文書を作成し、フォーム名を含むフィールドに値をセット、最後に Save メソッドで保存しています。


DXL の文書作成

これを DXL で作成するように書き直すと、次のようになります。

Sub Initialize
   Dim sDXL As String

   sDXL = xGetDXL() 'DXL文字列を作成
   Call xCreateNewDoc(sDXL)
End Sub

Function xGetDXL() As String
   Dim s As String

   'DXL文字列を作成
   s = "<document form='DXL_SbS'>"
   s = s & "<item name='Title'>"
   s = s & "<text>"
   s = s & "#1 DXL文字列から文書を作成"
   s = s & "</text>
   s = s & "</item>"
   s = s & "</document>"
   
   xGetDXL = s
End Function

Function xCreateNewDoc(ByVal vsDXL As String) As Boolean
   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim dimp As NotesDXLImporter
   
   Set ndb = ns.CurrentDatabase
   
   Set dimp = ns.CreateDXLImporter()
   Call dimp.Import(vsDXL, ndb)
End Function

ポイントは、文書を作成する DXL と文書を保存する方法の2点です。それぞれ関数化しており、メインルーチンからコールするようにしてます。

実行すると以下のように Form と Title フィールドがセットされた文書が作成されます。


DXL の作成

DXL の作成を行っているのは xGetDXL 関数です。

今回は、単純に DXL をコード内に直接定数として記述したリテラル文字列を連結させて作成しています。作成される文字列は以下の通りです(見やすくなるよう改行とインデントしています)。

<document form='DXL_SbS'>
   <item name='Title'>
      <text>
         #1 DXL文字列から文書を作成
      </text>
   </item>
</document>

まず、文書を表す document ノードを作成します。DXL ではフォーム名は、フィールドではなく、document ノードの form という属性で表します。

フィールドは、document ノード配下に item ノードを作成し、フィールド名は name という属性で指定します。フィールドの値は、フィールドの型を表すノード、今回は文字列なので text ノードを作成します。実際の値は、text タグの中にセットします。

既存文書を DXL に変換すると様々なノードや属性が設定されています。ですが、新規作成の場合は、必要最低限の DXL だけで正常に登録できます。


文書の作成

文書の作成は、xCreateNewDoc 関数で実施しています。

DXL を使って更新(今回は文書の作成)を行うには、NotesDXLImporter クラスを使用します。NotesSession クラスの CreateDXLImporter メソッドで、オブジェクトを取得して利用します。

Import メソッドが、DXL をデータベースに反映するメソッドです。引数は、反映するDXL と反映先となるデータベースとなります。どこに(データベース)何を(DXL)反映するのか一目瞭然でわかりやすいですね。

この Import メソッドを実行すると DXL がドミノオブジェクトに変換されデータベースに反映されます。今回の場合では、文書が作成されるということになります。


なお、Import メソッド実行時に DXL に問題があると、エラーが発生します。よって、DXL の生成を失敗したからと言って、データベースを壊すようなことはないようです。

DXL Step-by-Step 次回

2023/09/21

リスト値を@関数でアクセス

ノーツでは、Excel シートのように複数の項目(列)を複数行入力するような画面をリスト値で表現することがよくあります。

このような構造に対して、@関数でアクセスする方法をまとめます。


まず、次のようなフォームがあり、交通機関(Type)と金額(Cost)のフィールドはリスト値を持つ設定だったとします(区切り文字は改行)。

このフォームを使用して作成した以下の文書をサンプルデータとして使用します。


特定の要素の金額を取得

例えば、”阪神” の金額を取得するには次のように記述します。

xSchType := "阪神";
xIndex := @Member(xSchType; Type);
xCost := @If(xIndex = 0; 0; Cost[xIndex]);
@Prompt([Ok]; ""; xSchType + " = " + @Text(xCost))

まず、検索文字列を xSchType にセットし、それが交通機関に含まれるか検索します。検索は、@Member で行い、見つかった場合はその要素番号を返します(1 ~)。見つからない場合は 0 を返します。

金額の取得は、[ ] の演算子を使用して、フィールド Cost を配列としてアクセスし、必要な要素の値を取得しています。

実行すると次のようにメッセージが表示され、正しく金額が取得できることがわかります。

なお、@Member で指定した交通機関が見つからない場合は、金額を 0 としています。

また、取得できる要素番号は 1 がスタートです。これを 1-based indexing と呼びます。ちなみに、LotusScript のデフォルトは、0-based indexing です。


ループを使用したアクセス

続いては、ループ処理を記述できる @For という関数を使用して、すべての要素にアクセスする方法です。

ヘルプによると構文は、次の通りです。

@For( initialize ; condition ; increment ; statement ; ... )

引数は、1つ目がループ変数の初期条件、2つ目が継続条件式(True の場合ループ処理を継続)、3つ目がループ変数の変更式、それ以降がループ処理の本体で、最大 252 個の処理が記述できます。

例がないとわかりにくいので、サンプルコードを示します。この式はリスト値の値を順にメッセージボックスで表示します。

xMax := @Elements(Type);
@For(n := 1; n <= xMax; n := n + 1;
   xMsg := Type[n] + " = " + @Text(Cost[n]);
   @Prompt([Ok]; ""; xMsg)
)

変数 n がループ変数で、n は 1 から始まり、リスト値の要素数まで、1つずつ加算しなら、ループを処理します。実行する処理は、インデントした部分の2行で、メッセージボックスに表示する文字列を生成し、それを@Prompt で表示しています。

実行すると次のようになります。

2023/09/19

ビューで行の高さを超えたことを表示

リスト値を表示するビューでは、区切り文字に改行を指定して、値を整然と表示させることがよくあります。

ただ、このビューの設定、リスト値の数が多くて行の高さを超えたかどうかが判断できません。例えば、上記の1件目のデータは次の通り8要素でした。ビューだけを見ると5要素だと誤解されかねません。


このような場合に利用できる列式を作成しました。汎用的に作成したので、パラメータをセットするだけで、様々なシーンで利用できます。

REM {パラメータセット};
xMaxRow := 5;
xListVal := Type;
xUnit := "件";

REM {以下は共通部分(変更しないで下さい)};
xCount := @Elements(xListVal);
@If(xCount <= xMaxRow;
   xListVal;
   @Subset(xListVal; xMaxRow-1):
      (" … (計 " + @Text(xCount) + " " + xUnit + ")")
)

指定できるパラメータは次の3つです。これらの変数に値を与えるだけで利用できます。

変数 用途
xMaxRow 数値 ビューのプロパティで指定した行の高さ
xListVal 文字列リスト 列に表示するリスト値
xUnit 文字列 リスト値の単位


実行すると次のように表示されます。xMaxRow を超える場合は、最後の要素を件数表示に置き換えて表示します。

2023/09/17

Notes - Excel 連携:#19)グラフ関連オブジェクトまとめ

第 11 回から前回の第 18 回まで、グラフのオブジェクトである Chart オブジェクトの操作について解説してきました。今回はそのまとめとして、これまでの登場したオブジェクトの関係とその操作について整理ります。


グラフの作成方法

まず、シート内に存在するグラフは、Worksheet オブジェクト内の Shapes プロパティから取得するオブジェクトに含まれます。Shapes オブジェクトは、グラフ(Chart)だけでなく、画像(Picture)やテキストボックス(Textbox)などの様々なオブジェクトの集合となります。

グラフの追加は、Shapes オブジェクトの AddChart2 メソッドで作成しました。AddChart2 を実行すると Shape オブジェクトを返します。この Shape オブジェクトには、グラフ(Chart)オブジェクトが含まれ、Chart プロパティ経由でアクセスするという流れでした。


Chart オブジェクト内の構造

まず、Chart オブジェクトには、グラフの種類を決定する ChartType プロパティが存在し、グラフとして表示するデータを指定する SetSourceData メソッドを紹介しました。

指定方法は Range オブジェクトを使用します。Range オブジェクトは柔軟な仕様ですので、単一の範囲を指定することはもちろん、複数の範囲、連続した列、複数の連続しない列など様々指定方法ができました。

グラフ内のオブジェクトとしては、グラフのタイトルである ChartTitle オブジェクトと、軸の設定である Axes オブジェクトについて紹介しました。

グラフのタイトルのフォントを変更するには、ChartTitle オブジェクトからいくつものオブジェクトを掘り進んで設定する必要がありました。

ここまでを関連図で書くと次のようになります。


オブジェクト構造の補足

上記関連図では、連載記事内で触れたプロパティとメソッドのみ表示しています。それぞれのオブジェクトには、ほかにもたくさんのプロパティやメソッドが存在します。このような関係を理解したうえで、Microsft Learn を読み進めると混乱しなくて済むかと思います。


例えば、本編の連載では触れませんでしたが、軸に表示する文字のフォントを変更することができます。軸に表示する文字(の集合)のオブジェクトは TickLabels でしたが、Format というプロパティを持っています。このプロパティの型は ChartFormat で、グラフタイトルオブジェクトの Format プロパティと同じです。

Excel VBA のオブジェクト構造は、それぞれのオブジェクトが十分に独立化していて、様々な場所で再利用できるよう設計されています。そのため、階層が深く、入り組んだ関係に見えることもあります。しかし、そのオブジェクトの役割しっかり確認すると、理解しやすくなります。


前回 Notes - Excel 連携 次回

2023/09/15

ビューの合計とリスト値

前回の『リスト値とビュー』の続きです。

ノーツのビューはプロパティを設定するだけで合計を表示することができます。例えば、合計列で次のプロパティを設定します。

すると、次のようにビューに合計が表示されます。便利な機能なので利用されている方も多いかと思います。


この合計機能ですが、リスト値を使用したビューでは、いくつか注意点があるのでまとめます。


リスト値の列の合計

まず、リスト値のフィールドを表示している列に”合計”を設定した場合です。この場合は、リスト値の1つ目の値だけを合計し、2つ目以降の値が無視されて合計が算出されます。

もし、このようなビューで合計を表示したければ、上記画面の合計列のようにいったん文書ごとの合計を計算しておいて、それをビューで合計させる必要があります。


リスト値をカテゴライズした場合

続いては、リスト値をカテゴリ表示させたビューと合計の関係です。

使用するビューは前回作成したカテゴリの交通機関の金額が表示されるようにしたビューです。このビューでは、リスト値の文書は、ビューにリスト値の要素数だけ表示されます。そのため、文書ごとの合計金額を”合計”すると、正しく表示れません。要素(交通機関)毎の金額を合算する必要があります。


まとめ

ビューの合計機能は、プロパティをセットするだけで、合計が表示される単純でとても便利な機能なのですが、リスト値を使用したビューの場合、正しく合計されない場合があります。はありますが、リスト値を使用した文書の場合は、要件(希望)にあった合計が表示されているか、確認しましょう。

2023/09/13

リスト値とビュー

今回は、リスト値のフィールドをビューに表示する方法をまとめます。

まず、次のような簡略化した交通費精算をイメージしたフォームを準備しました。交通機関と金額が対となったリスト値の項目です。

このフォームを使用して、サンプルデータをいくつか用意し、ビューを作成します。


リスト値の列

最初は単純に、次のようなビューを作成します。リスト値を改行で羅列、対になる項目を隣接させて表示し、内容を整然と表示します。

まず、列のプロパティで『複数値の分離記号』を”改行”に設定します。この設定を対となるリスト値の項目(交通機関と金額)に設定します。

続いてビューのプロパティで行の高さを最大の 9 に設定し、『行の高さを内容に合わせる』にチェックを入れます。この設定で、リスト値が2つの場合は2行、1つの場合は1行と都合よく高さを調整してくれます。


リスト値のカテゴリ

続いて、リスト値をカテゴリ項目として利用した場合の動作です。

交通機関を一列目に移動し、列のプロパティで『カテゴリ別』を選択します。すると、自動的に『複数値を別のエントリで表示』がチェックされます。

ノーツで表示すると次のようになります。交通機関ごとにカテゴライズされました。

ただ、これだと金額がリストとして複数行表示されたままで、その交通機関でいくらだったのか不明です。これを改善するには、金額の列でも『複数値を別のエントリで表示』をチェックします。

この設定で、カテゴリの交通機関の金額が表示されるようになります。


ソート時の注意

実際のアプリケーションでは、上記のようなビューで、カテゴリ内の明細行をソートさせたい場合があると思います。このような場合、少し癖がありますので、ご注意ください。

まず、明細行のソートする場合、ソートする列は『複数値を別のエントリで表示』する列より左にある必要があります。上記例でいうと、件名ではソートできますが、合計金額ではソートできません。

また、『複数値を別のエントリで表示』を選択した列では、ソートできません。上記事例で、金額列にソートをセットすると次の通り、正しく表示されません。2件のリスト値の文書が4行表示されています。これは、対になるはずの交通機関と金額のすべての組み合わせが出力されているのではないかと想定します。

このように少し癖がある動作になりますので、開発の際には、十分確認ください。テストデータが少なすぎても、多すぎても気が付かないのでご注意ください。

2023/09/12

DXL ことはじめ:#9)フィールドの値の取得

今回は、フィールドの値の取得方法についてまとめます。

まずは、DXL でフィールドの中身がどのように表現されているか確認しましょう。下図の通り、"text" というエレメントがあり、その中に値が表示されています。

これを LotusScript でアクセスするためには、次のようなプログラムとなります。なお、フィールドのノードの取得は前回作成の関数を利用しています。

'document ノードの取得
Set den = ddn.DocumentElement

'フィールドのノードを取得
Dim denFld As NotesDOMElementNode
Set denFld = xGetField(delmn, "address1")

'フィールドの値を取得
Dim denVal As NotesDOMElementNode
Dim dtn As NotesDOMTextNode
Set denVal = denFld.FirstChild
Set dtn = denVal.FirstChild


MsgBox dtn.NodeValue

まず、"item" ノードから見て、最初の子ノードが必要なので、FirstChild プロパティで取得しています。子ノードは、DXL でいうと次の通りです。

<text>鳥取県</text>

ここから値の "鳥取県" を取得するには、子ノードである denVal の NodeValue プロパティで取得できそうなのですが、これだと正しく取得できません。値部分(ただのテキスト文字列)は、DXL 上ノード扱いとなっています。ノードの種類は、テキストノードで、サンプルコードの通り、NotesDOMTextNode オブジェクト経由でアクセスします。

このテキストノードは、"text" ノードから見て最初のノードとなるので、FirstChild プロパティで取得しています。

そして、テキストノード(dtn)の NodeValue プロパティがフィールドの値となります。

フィールドの値にアクセスするだけなのですが、階層が深くて、少しややこしいですね。


フィールドの型

今回の郵便番号マスタ文書では、団体コード(code)と都道府県コード(prefcode)は数値フィールドとしていました。この DXL は次の通りで、タグ名が "number" となっていました。

<item name="code">
   <number>31302</number>
</item>
<item name="prefcode">
   <number>31</number>
</item>

DXL はテキスト文字列ですから、フィールドの型もタグ(文字列)で表されていて、値も文字列で現れています。LotusScript で値を操作するには、タグに合わせて文字列の値をその型に変換する必要があります。


参考までに、リスト値の場合を紹介します。次の例は、3つに分けて保存されている住所の情報を "address" というフィールドにリスト値で保存した場合です。"item" タグの下に "textlist" というタグがあり、その下に値が列挙されています。

<item name='address'>
   <textlist>
      <text>鳥取県</text>
      <text>岩美郡岩美町</text>
      <text>相谷</text>
   </textlist>
</item>


また、名前フィールドの場合は次の通り、属性に "names" というタグが追加されています。

<item name='Name' names='true'>
   <textlist>
      <text>CN=Admin Notes/O=Domino</text>
      <text>CN=User01 Training/O=Domino</text>
   </textlist>
</item>


このように、DXL はデータの型や値により、柔軟に構造が変わります。どのような構造となるのか詳細を知るには、DTD を読み解くか、実験を繰り返し調査するしかないようです。

また、階層は一定でないことことから、LotusScript でプログラミングするには少し厄介ですね。


最後に

今回の『DXL ことはじめ』シリーズはこれで終了です。

DXL がどんなものか、LotusScript でアクセスするためにはどうするのか、どのようなクラスが用意されているのかが概要を説明しました。

事例としたのは既存文書の読み込みでしたが、書き込み(更新)や新規作成も可能です。リッチテキストの中身の操作もできます。また、設計要素も DXL でアクセスでき、更新や新規作成が可能です。

このような具体的な使い方については別の機会にまとめたいと思います。

前回 DXL ことはじめ

2023/09/10

表形式のフォームとリスト値

ノーツでアプリケーションを作成していると、Excel シートのように複数の項目(列)を複数行入力するような画面が必要となることがあります。

例えば、交通費精算フォームです。行先、交通機関、金額を1セットとして、使用した経路数分入力するような画面です。

単純に作成すると、次のような感じになるかと思います。

ただ、このような構造だと、必要な行数分フィールドをあらかじめ作成する必要があります。多数の行を作成すると、フォームが間延びしますし、入力する行が少ないと美しくないですね。また、見やすく非表示にしようとすると、1行ごとに非表示式が微妙に変化し、設定が面倒です。

そもそも、入力できる行数に上限が発生するので、汎用性があまりないといえます。


このような場合に利用できるのがリスト値です。

リスト値とは1つのフィールド内に複数の値を持つ状態を言います。プログラミング的には配列と呼ばれるような形式です。ノーツでは、複数値と呼ぶことが多いです。

このブログではあえて”リスト値”と呼びます。プロパティ名が”複数値”となっていることもあり、”複数値”を連呼することになり、記事がこってり重く感じたためです...


リスト値を使用したフォームの例

先に例を示します。上記フォームをリスト値で表現すると次のようなイメージになります。

フィールドを表す『 』は各列で1つとなっていて、それぞれの値は改行で区切られています。フォームの設計は次のようなっています。

このような構造であれば、フィールドは1つで入力する件数(行数)に制限はありません。もちろん、文書やフィールドの制限があり無限ではありませんが、一般的には制限はないといってよいと思います。


フィールドの設定

リスト値のフィールドの設定ですは、プロパティで『複数値も可』にチェックを入れるだけです。これで、このフィールドは、リスト値を保持できるようになります。

続いて、リスト値の区切り文字を設定します。区切り文字の設定は、入力時(編集モード)に効果のある『値の分離記号』と表示(読み込みモード)で使用する分離記号が指定できます。

今回の事例では、疑似的に表のように見せるため、値の分離記号は”改行”を指定します。これにより、リスト値の要素ごとに改行されます。同じ設定を表内のすべてのフィールドに行うと、同じ要素の値が横に並んで表示されます。


利用時の注意

今回の事例に上げたリスト値を利用して表のように表示する方法は、ノーツアプリ開発においてよく使用されます。ただ、いくつか注意点があります。


◇ Null 値は詰まる

リスト値では、値がない場合要素が詰まってしまいます。例えば、次のように入力します。

F9 を押して画面を更新すると次のように要素が詰まってしまいます。文字列の場合は、空白(スペース)をセットするなど対策が必要です。


◇ 数値や日付の場合

数値フィールドや日付の場合、型が一致しないことから、値のない場合にスペースをセットできません。このような場合、数値や日付値をテキスト値として保持するなど対策が必要となります。


◇ 値が長い場合

今回の対応は、区切り文字の改行を利用して疑似的に表っぽく表示しています。フィールドの値が長い場合、折り返しが発生するのですが、この場合、行のずれが発生して各要素の関係がつかめなくなります。


コメントなど文字数が特定できない場合などは、表示する文字数を決定するなど折り返しが発生しないようにしましょう。

また、表の列幅を固定にして、折り返しに備えることも有効ですね。


まとめ

リスト値を使用すると設計がシンプルになり、画面構成をすっきりさせることができます。

ただ、上記の通り、一部制約やテクニックを要する点もあります。まずは、テキスト(文字列)だけで構成される場合や必須入力(値を必ず入力する)の場合から活用するとよいかと思います。

2023/09/08

Notes - Excel 連携:#18)グラフの目盛線

前々回は縦軸、前回は横軸の設定について触れました。今回は軸の設定の中でも、目盛線の設定について整理します。

まずは、前回作成したサンプルのグラフを使って、VBAで目盛線を設定する方法を調査します。マクロの記録を有効にし、目盛線を順に有効にします。

記録された VBA は次の通りとなります。

ActiveChart.SetElement (msoElementPrimaryCategoryGridLinesMajor)
ActiveChart.SetElement (msoElementPrimaryValueGridLinesMinorMajor)
ActiveChart.SetElement (msoElementPrimaryCategoryGridLinesMinorMajor)

Chart オブジェクトの SetElement メソッドは、グラフに引数で指定した要素を追加するメソッドです。

Chart.SetElement メソッド (Excel)

引数に指定できる要素は  MsoChartElementType 列挙 にあらかじめ定義されています。記録されたマクロで使用している定数は次の通り、目盛線を表していました。

定数 説明
334 msoElementPrimaryCategoryGridLinesMajor 主項目軸に沿って目盛線を表示
331 msoElementPrimaryValueGridLinesMinorMajor 数値軸に沿って目盛線と補助目盛線の両方を表示
335 msoElementPrimaryCategoryGridLinesMinorMajor 主項目軸に沿って目盛線と補助目盛線の両方を表示

補助目盛線の間隔は、Excel では次の項目で設定します。

VBA では Axes オブジェクトの MinorUnit プロパティとなります。”主” が MajorUnit、”補助” が MinorUnit です。覚えやすいですね。


LotusScript の記述

ここまでの設定を LotusScript で記述すると、次のようになります。

'X 軸の設定
oChart.Axes(xlCategory).CategoryType = xlTimeScale
oChart.Axes(xlCategory).MajorUnit = 7
oChart.Axes(xlCategory).TickLabels.NumberFormatLocal = "m/d;@"


'目盛線の追加
Call oChart.SetElement(msoCategoryGridLinesMajor)
Call oChart.SetElement(msoValueGridLinesMinorMajor)
Call oChart.SetElement(msoCategoryGridLinesMinorMajor)

'Y 軸補助目盛線の設定
oChart.Axes(xlValue).MinorUnit = 250

oXls.Visible = True

もちろん、mso で始まる定数は、スクリプトライブラリに事前に定義しておきます。なお、VBA の定数名のままだと長すぎるとエラーが出たので、短く調整しています。

出来上がったプログラムを実行すると次のような結果となります。


MsoChartElementType 列挙

先ほど紹介した MsoChartElementType 列挙 には、軸以外の要素も多数定義されています。以下に一部を抜粋します。グラフタイトル、凡例、データラベルなどがあります。

定数説明
0msoElementChartTitleNoneグラフ タイトルを表示しません
1msoElementChartTitleCenteredOverlayタイトルを中央揃えで重ねて表示
100msoElementLegendNone凡例を表示しません
101msoElementLegendRight凡例を右に表示
105msoElementLegendRightOverlay凡例を右に重ねて配置
200msoElementDataLabelNoneデータ ラベルを表示しません
201msoElementDataLabelShowデータ ラベルを表示

それ以外にも、軸ラベルや単位、対数目盛の設定など様々です。SetElement メソッド一つでいろいろなことができそうですね。

試しに、上記の 1 と 105 を SetElement メソッド設定すると次のようにプロットエリアを大きくしたグラフにできます。

前回 Notes - Excel 連携 次回