出直し!! ヘルプ

連載中

連載 終了

2023/04/30

Notes - Excel 連携:#8)Window オブジェクト

前回の記事で、Application オブジェクトの ActiveWindow プロパティから Window オブジェクトにアクセスする事例を紹介しました。

Notes - Excel 連携:#7)ウィンドウ枠の固定

今回は、この Window オブジェクトの位置づけや使い方について整理します。

Microsoft Learn の Window オブジェクトの説明によると、Application オブジェクトや Workbook オブジェクトからアクセスできるようです。そして、Windows というコレクションオブジェクトも存在するようです。

オブジェクトの関連を順に確認しましょう。


Application オブジェクト

まず、Application オブジェクトです。このオブジェクトには、Windows プロパティが存在し、Window オブジェクトのコレクションを表すとのことです。このプロパティは、Workbooks プロパティと同様、引数にインデックス番号を指定すると単一のオブジェクトが取得できるようです。

Application.Windows プロパティ (Excel)


Workbook オブジェクト

続いて、Workbook オブジェクトを確認したところ、Windows プロパティの記述がありました。これは、ワークブックを表示しているウィンドウを取得するためのプロパティと想像できるのですが、なぜ Windows オブジェクト(Window のコレクション)なのかわかりません。Excel のワークブックは、複数のウィンドウに分けて表示ができるのでしょうか??

Workbook.Windows プロパティ (Excel)

ちなみに、Windows オブジェクトには、Workbook にアクセスするプロパティはありませんでした。Window が保持しているのは、Workbook に限らないからということなのでしょうか?


オブジェクトの相関図

さて、各オブジェクトの関連が見えてきました。図式すると以下のようになります。この関係を押さえておくと、Window オブジェクトと Workbook オブジェクトを混乱することはなくなるかと思います。



前回 Notes - Excel 連携 次回

2023/04/26

@Prompt の使い方

@関数でメッセージボックスを表示できる @Prompt。よく使いますよね。

LotusScript の MessageBox と同様、引数や使い方が多岐にわたるので、覚えるのが難しいです。ダイアログリストフィールドのようにキーワードから選択する画面も作成できるので、MessageBox 以上に複雑ですね。

ということで、今回は、@Prompt でよく使うパターンだけを抽出してサンプルコード付きでまとめてみました。


メッセージ表示

まずは、メッセージを表示するだけの単純なパターンです。使用するキーワードは、[Ok] です。ウィンドウタイトルとメッセージだけの引数ですので、わかりやすいですね。

REM {パラメータセット};
xTitle := "メッセージ表示";
xMessage := "[OK]ボタンだけのメッセージボックスです。";

REM {Prompt表示};
@Prompt([Ok]; xTitle; xMessage)


確認

続いては、処理の実行を確認する場合などに使う、[はい][いいえ]ボタンのあるパターンです。使用するキーワードは、[YesNo] もしくは [YesNoCancel] です。

以下のサンプルは、[YesNoCancel] で記述しています。この形式の場合に限り、[キャンセル]ボタンをクリックした場合、-1 が戻り値になります。それを検知したら、式を強制終了させています。

REM {パラメータセット};
xTitle := "確認";
xMessage := "実行してよろしいですか?";

REM {Prompt表示};
xReturn := @Prompt([YesNoCancel]; xTitle; xMessage);

REM {[キャンセル]の時は処理を抜ける};
@If(xReturn = -1; @Return(""); @Success);

REM {実行する処理を記述};
@If(xReturn = @True;
   @Do(
      REM {[はい]の場合の処理を記述};
    ・・・
   );
   @Do(
      REM {[いいえ]の場合の処理を記述};
    ・・・
   )
)


入力

次は、文字列を入力する場合です。LotusScript の InputBox に相当します。使用するキーワードは、[OkCancelEdit] です。入力した文字列を返します。

[YesNoCancel] 以外の場合、[キャンセル]ボタンをクリックするとそこで、式の実行が終了します。ですので、以下のサンプルでも[キャンセル]ボタンの判定はしていません。

REM {パラメータセット};
xTitle := "入力";
xMessage := "入力してください。";

REM {Prompt表示};
xReturn := @Prompt([OkCancelEdit]; xTitle; xMessage; "");
REM {[キャンセル]の時は以下は実行しない(自動で式の評価終了)};

REM {入力した文字(xReturn)の処理};
  ・・・


選択

選択肢の一覧から、選択するパターンです。

REM {パラメータセット};
xTitle := "選択";
xMessage := "選択してください。";
xOptions := "選択肢1":"選択肢2":"選択肢3";
xDefault := @Subset(xOptions; 1);

REM {Prompt表示};
xReturn := @Prompt([OkCancelList]; xTitle; xMessage; xDefault; xOptions);
REM {[キャンセル]の時は以下は実行しない(自動で式の評価終了)};

REM {選択時のの処理(xReturn)};
  ・・・


2023/04/23

リッチテキスト:#2)リンクの作成

前回の『リッチテキスト:#1)フィールドの作成』では、新しいリッチテキストフィールドを作成して、テキストを追加しました。今回はリンクを追加してみましょう。

リンクの作成は、NotesRichTextItem クラスの AppendDocLink メソッドを使用します。このメソッドの構文と引数は、以下の通りです。

Call notesRichTextItem.AppendDocLink(linkTo, comment$)

  • linkTo

リンクするデータベース、ビューまたは文書です。

  • comment$

String 型。リンク上でマウスポインタを押さえたときに表示される文字列です。


このメソッドのポイントは、一つ目の引数である linkTo です。構文にある通り、3種のオブジェクトが指定できます。データベースを指定した場合はデータベースリンク、ビューを指定した場合はビューリンク、そして文書を指定した場合は文書リンクとなります。

例えば、以下のように記述すると、自身のデータベースのリンクをリッチテキスト内に追加します。

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

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

   nd.Form = "RichText"
   nd.Title = "リッチテキスト:#2)リンクの作成"

   Set nrti = nd.CreateRichTextItem("Body")
   Call nrti.AppendText("以下はこのデータベースのリンクです。")
   Call nrti.AddNewLine(1)
   Call nrti.AppendDocLink(ndb, ndb.Title)

   Call nd.Save(True, False)
End Sub


実行すると、以下のようなります。


ところで、Notes8 以降、データベースのことをアプリケーションと呼ぶようになりました。ただ、データベースリンクはデータベースリンクで、アプリケーションリンクと呼んではなんだかしっくりきません。旧世代のノーツから担当しているからでしょうか?

そして、こういった記事を書く時には、アプリケーションと書くべきか、データベースと書くべきか非常に迷います。

そもそも、ノーツクライアントのメニューの [編集] - [リンクのコピー] の下は”アプリケーションリンク”となっていますが、ヘルプには”database”と書いてあります。どっちでもいいってことですね。なじむしかなさそうです(笑)

AppendDocLink (NotesRichTextItem - LotusScript®)


前回 リッチテキストの基本操作 次回

2023/04/19

Excel の列文字列を式で算出

今回は、式言語を使用したクイズです。

問題:

フィールド ColNum に入力された自然数(正の整数)を Excel の列番号 (A ~ Z、AA ~ ZZ)に変換する式を作成しましょう。

なお、入力値は 1~702 とし、結果は A ~ ZZ までの最大2桁とします。


問題解決のアプローチ

解決手法は様々あるかと思います。まさに、十人十色ですよね。

単純に考えると、@If を連ねて書いて実現できます。でも、700行も記述するのはかなり面倒ですよね。 A ~ Z の 26 文字に着目して、26 で割ってから処理をすると、@If を削減できるかもしれません。また、数列的なアプローチや 26 進数として数学的に処理する方法はないでしょうか?

もしかしたら、便利な関数があって、それを利用すると一発で解決!って可能性もありえます。

エレガントな表現や回りくどい記述、どんな方法でもプログラムとしては、結果が出れば正解です。ですが、ビジネスで開発をする限り、効率は重要です。例えば、以下のような点だと考えます。

  • 理解しやすいか?
  • メンテナンス性は高いか?
  • レスポンスは早いか?
  • 短いコードであるか?
  • 再利用はしやすいか?

はるか昔、パソコンが巷ではやり始めたころ、『1画面プログラミング』なんてものがありました。1画面で入る範囲で、どれだけ素晴らしいアプリを作れるか?的な競技(?)でした。これは、コード量圧縮に重きを置き、理解しやすさなどは完全に度外視したものではありましたが、問題解決の手段を突き詰めるという意味では、ビジネスでも応用できる部分はあると思います。


サンプルコード

さて、問題の解答例です。

Excel の列番号って A から始まって、Z までアルファベットの順で進みます。その次は、AA となり、AZ まで順に進み、次は BA となります。A ~ Z までの 26 文字を使った、26 進数のような感じなのですが、0 が存在しないので微妙に単純化できません。

私は以下のような式で実現してみました。

xChrs := "A":"B":"C":"D":"E":"F":"G":"H":"I":"J":"K":"L":"M":"N":
              "O":"P":"Q":"R":"S":"T":"U":"V":"W":"X":"Y":"Z";
xXls := xChrs : (xChrs *+ xChrs);
@Subset(@Subset(xXls; ColNum;)); -1)

列番号を構成する A ~ Z までの26文字をリストに準備します。これを使って、以下の行で A ~ ZZ までの文字列をリストに作成しています。

xXls := xChrs : (xChrs *+ xChrs);

ポイントは、演算子として使用している ”*+” です。これは、順列演算子と呼ばれるもので、リスト値同士をすべての組み合わせで加算し、それぞれのリスト値を返します。今回は、文字列ですので加算ではなく、文字列の連結として動作します。この部分だけで、AA ~ ZZ までのリストを一気に生成しています。

それに、一桁の部分をリスト連結させ、必要な文字列リストを完成させています。

最後に、ColNum に対応する値を抜き出します。いったん、その番号までのリスト全体を @Subset で取得して、もう一度 @Subset を -1 で使い、最後の要素だけを抽出し、結果を取得しています。


まとめ

今回のサンプルは、式としては非常に単純化できているかと思います。

ですが、A ~ ZZ までのすべて文字列をいったん一時変数に保管しています。よって、メモリ効率的には良くないといえます。要件が ZZZ までの3桁だったらこの方法はだめかもしれません。

要件に応じて、アプローチも変わることになります。プログラミングって面白いですね。
そう思うのは私だけでしょうか??

2023/04/16

Notes - Excel 連携:#7)ウィンドウ枠の固定

CSV ファイルのようにノーツ文書を Excel シートに一覧出力する場合、1行目は項目名を出力し、ヘッダ行のようにすることがあります。このようなシートでは、ヘッダ行はスクロールを固定して常時表示されると使い勝手が良いですよね。

ということで今回は、Excel の『ウィンドウ枠の固定』を LotusScript から設定する方法です。

以下の例は、ワークシートの1行目を固定するプログラムです。赤字の部分が枠を固定している箇所です。

Sub Initialize
   Dim oXls As Variant
  
   Set oXls = CreateObject("Excel.Application")
   Call oXls.Workbooks.Add
  
   oXls.ActiveWindow.SplitRow = 1
   oXls.ActiveWindow.FreezePanes = True

  
   oXls.Visible = True
End Sub

Application オブジェクトの ActiveWindow プロパティが新登場です。

Application.ActiveWindow プロパティ (Excel)

このプロパティは、『作業中の Excel ウィンドウ (上のウィンドウ) を表す Window オブジェクトを返します。 』だそうです。今回で言うと、直前で Add した Excel のワークブックということですね。そして、”作業中の Excel ウィンドウ”ということなので、Active というプロパティ名なのでしょう。

このプロパティの型は、Window オブジェクトです。

Window オブジェクト (Excel)

Microsoft Learn の記述読んでも、いまいちよくわかりませんが、ActiveSheet や ActiveCell プロパティのように今使っているオブジェクト(LotusScript 的に言うと”Current”?)があり、ウィンドウサイズを表す Height や Width プロパティがあることから、UI にまつわるオブジェクトだと判断できます。

この Window オブジェクトに『ウィンドウ枠の固定』するためのプロパティがあります。

SplitRow プロパティは固定する行番号で、その固定を有効にするのが FreezePanes プロパティです。

また、シートによっては行と列を固定した場合もあるかと思います。このようなときは、SplitColumn プロパティを使用します。

例えば、以下のサンプルでは、1行目と1列目のスクロールが固定されます。

oXls.ActiveWindow.SplitRow = 1
oXls.ActiveWindow.SplitColumn = 1
oXls.ActiveWindow.FreezePanes = True
前回 Notes - Excel 連携 次回

2023/04/14

実行時間の計測(Timer 関数)

LotusScript で作成したプログラムが遅い場合にレスポンスの改善に取り組むことは一般的かと思います。対策の最初のステップは、現状把握ですね。どこが遅いのかを知ることにより、効果的な対策を打つことができます。

LotusScript のプログラムの実行時間を測ることになるのですが、時刻を取り扱う Now 関数では秒までしか取得できません。LotusScript の実行速度からすると、秒単位の測定では誤差が大きく、正確に状況把握できません。

そこで利用するのが、Timer 関数です。

この関数は、午前 0 時からの秒数を返す関数なのですが、ポイントは、1/100 秒まで取得できる点です。

Dim sgST As Single

sgST = Timer()
Call Func1()
Print Format(Timer() - sgST, "0.00")

sgST = Timer()
Call Func2()
Print Format(Timer() - sgST, "0.00")

Timer 関数の戻り値は、Single 型です。測定を開始する前の Timer 関数の値を変数 sgST に保持させ、測定する範囲である Func1() または Func2() 関数が終わったら、その時点の Timer 関数の戻り値と比較して、処理時間を Print しています。

なお、この関数、日付をまたがると正しい値が取得できないので、ご注意ください。

2023/04/12

NSF メンテナンス 三種の神器

ドミノサーバ管理をしているのデータベースが壊れるという現象に出くわすことがあります。最近のバージョンでは、あまり聞かなくなったので、安定性はずいぶん上がってきたのだと思いますが...

もし、データベースが壊れたときに実行するのが、データベースのメンテナンスコマンドです。いざ使いたいとなった時に、構文やオプションを調べるのは面倒です。使用する機会がこないよう祈りつつではありますが、ここに整理しておきます。

以下が、古来から”三種の神器”とあがめられているノーツの神コマンドです。

load fixup -F xxx.nsf
load updall -R xxx.nsf
load compact -c xxx.nsf

それぞれのコマンドの詳細は以下の通りです。


fixup

fixup は、壊れたデータベースを修復します。オプションの -F を指定すると、すべての文書を検査対象として実行します。

オプションの詳細は以下を参照ください。

修復のオプション


updall

Updall は、データベースのビュー索引と全文索引を管理します。オプションの -R は、使用されているすべてのビューを再構築します。

[タスク] - [開始] ツールの Updall の [基本] オプション

[タスク] - [開始] ツールの Updall の [再構築] オプション


compact

compact は、データベースを圧縮するコマンドです。オプションの -c コピー圧縮方式で、データベースの破損問題の解決などに使用します。

圧縮オプション


2023/04/09

リッチテキスト:#1)フィールドの作成

リッチテキストフィールドは、フォントやサイズ、色、文書リンクや添付ファイルなど様々なデータが格納できます。また、それらを混合して保存できるとても便利で柔軟なフィールドです。

ノーツに携わり始めたころ、その便利さに感心しました。しかも、違う OS で参照しても同じように見えることに驚いたことを覚えています。当時は、Windows 3.1 と OS/2(?) だったような気がしますが...

ノーツのフィールドは、テキストや数値など型を指定します。ですので、そのフィールドに保存される値もその型となり単純です。

しかし、リッチテキストフィールドは前述の通り、様々なデータが連なって格納されます。これをプログラムで操作できるように、LotusScript では、専用のクラスが多数用意されています。例えば、

  • NotesRichTextItem クラス
  • NotesRichTextDocLink クラス
  • NotesRichTextStyle クラス
  • NotesRichTextParagraphStyle クラス

などなどです。

プログラムでリッチテキストを操作する場合、用途に応じたクラスを使用する必要があります。多種多様なデータを取り扱うことから、必要となるクラスやプロパティ / メソッドが多数あり、使い方に注意すべき点もあります。

このシリーズでは、LotusScript のリッチテキストの取り扱い方法を整理していきます。


新規リッチテキストフィールドの作成

今回は初回ですので、一番単純なパターンです。

新規文書を作成し、そこにリッチテキストフィールドを作成し、文字列をセットするだけです。サンプルプログラムは以下の通りです。

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

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

   nd.Form = "RichText"
   nd.Title = "リッチテキスト:#1)フィールドの新規作成"

   Set nrti = nd.CreateRichTextItem("Body")
   Call nrti.AppendText("このフィールドはリッチテキストです。")
   Call nrti.AddNewLine(1)
   Call nrti.AppendText(|フィールド名は、"| & nrti.Name & |"です。|)

   Call nd.Save(True, False)
End Sub


NotesRichTextItem クラス

リッチテキストフィールドを表すクラスです。このオブジェクトを起点にして、リッチテキストフィールドの値にアクセスします。

今回のサンプルでは、文書とフィールドを新規に生成しています。リッチテキストフィールドの作成は、NotesDocument クラスの CreateRichTextItem メソッドを使用します。このメソッドの戻り値は、作成された NotesRichTextItem のオブジェクトとなります。

文字列の追加は、AppendText メソッドを使用しています。なお、このメソッドは、文字をどんどん追加するだけのメソッドです。改行したいときには、AddNewLine メソッドを使用して明示的に指示する必要があります。


NotesItem クラス と NotesRichTextItem クラス

NotesRichTextItem クラスは、NotesItem クラスを継承しています。継承とはオブジェクト指向で出てくる基本的な概念となります。

オブジェクト指向とは、プログラムの作成手法の一つです。モノ(=オブジェクト)を組み合わせてプログラミングする、今時は主流の考え方と言えます。オブジェクトには、データと機能があり、それぞれプロパティとメソッドとして定義されます。継承はオブジェクトの定義において、共通部分をうまく使って別のクラスを定義するって感じでしょうか。

簡単に言うと、NotesRichTextItem では、NotesItem クラスが持つプロパティやメソッドも使用できるということです。上記サンプルで、Name というプロパティにアクセスしてフィールド名を取得しています。これは NotesItem クラスから継承した機能を利用しているということになります。

リッチテキストの基本操作 次回

2023/04/07

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

前回は、ダイアログボックスの開き方について記述しました。

今回は、開いたダイアログ側のプログラムについて記載します。ダイアログボックスでは、通常のフォームとは違うコーディングが必要となります。

ダイアログボックスの設計はフォームでできています。フォームを開くときには、通常のフォームと同様に QueryOpen、PostOpen イベントが発生します。

閉じるときも同様に QueryClose イベントが発生します。ただ、通常のフォームと違うのは、フォームの閉じ方です。ダイアログボックスは、単に閉じるだけではなく、[OK]か[キャンセル]のボタンのどちらかで閉じることになります。入力チェックは、[OK]の時だけ実行する必要があります。必須項目を入力しないと、キャンセルできないのは不便ですからね...

その判定を行うのが、NotesUIDocument クラスの DialogBoxCanceled プロパティです。

以下のコードは、私がダイアログボックスに記述する一般的なプログラムです。最初に DialogBoxCanceled プロパティが True の場合、イベントを抜けています。これで、キャンセルした場合は、入力チェックせずに閉じることになります。

その後、入力チェックする関数 IsError() をコールして、エラーがあった場合は、ダイアログボックスが閉じるのをキャンセルするため Continue を False にセットしています。 これで、正しく入力するまで[OK]で閉じれないようにしています。

Sub QueryClose(Source As NotesUIDocument, Continue As Variant)
   If Source.DialogBoxCanceled Then Exit Sub
   If IsError(Source) Then
      Continue = False
      Exit Sub
   End If
End Sub

なお、入力チェックする関数は以下の通りとなります。ここで、入力の有無や関連するフィールドの整合性を確認します。

エラーがなかった(=最後まで関数が実行された)場合、戻り値を False にセットし、エラーがなかったことを呼び出し元に返しています。

%REM
   エラーの場合、メッセージを表示して True を返す
%END REM
Function IsError(vnuid As NotesUIDocument) As Integer
   Dim nd As NotesDocument

   Set nd = vnuid.Document

   IsError = True

   '以下に入力チェックを記述(エラー時は関数を抜ける)
   If CStr(nd.XXXX(0)) = "" Then
      MessageBox "XXXXを入力して下さい。", 16, "入力エラー"
      Exit Function
   End If

   '次の入力チェックを以下に記述

   IsError = False '最後まで実行したらエラーなし

End Function

2023/04/05

ダイアログボックスの開き方

日付を From - To で入力する場合など、いくつかのフィールドをまとめて入力する場合では、ダイアログボックスを開く場合があります。ダイアログでは、別のフォームやビューに移動することができず、入力完了するまで他の操作を受け付けなくできます。

開発者としては、入力チェックはもとより、フィールド間の関係性チェックなどを独立したフォーム内で完結できるので、メインのフォームの設計をシンプルにできます。そして、同様の画面が必要になった場合に流用ができ、作業を効率化できます。

実際にダイアログボックスの利用するには、一定のプログラムが必要となります。

今回は、ダイアログボックスを開くプログラム(呼び出し側)の一般的なコードをテンプレート的に掲載します。

Dim ns As New NotesSession
Dim ndb As NotesDatabase
Dim nuiw As New NotesUIWorkspace
Dim ndDlg As NotesDocument
Dim b As Boolean
Dim sFm As String

Set ndb = ns.Currentdatabase
Set ndDlg = ndb.Createdocument() 'ダイアログ用文書

'A) ダイアログのフォーム名をセット
sFm = "dlgForm"

'B) 初期値(フィールド)をセット
ndDlg.dlgMsg = "基準日を入力してください。"
ndDlg.dlgDate = Today

'ダイアログの表示
b = nuiw.Dialogbox(sFm, True, True, False, False, False, False, ndb.Title, ndDlg)
If b = True Then
   'C) ダイアログ[OK]で閉じた時の処理を以下に記述

End If

処理の流れは、以下の通りです。

  • 接続中の DB 内にダイアログボックス用の仮の文書(ndDlg)を準備
  • その文書に初期値を設定して、ダイアログを開く
  • [OK]ボタンで閉じた時は処理を継続(If 文の中身を実行)

なお、この仮の文書(ndDlg)は、Save メソッドで保存しないので、このプログラムが終了と同時に消滅し、文書としては残りません。


使い方

以下の通りです。

  1. レイアウト領域を使ったダイアログボックスフォームを作成
  2. 上記コードをダイアログを開くボタンなどにセット
  3. ダイアログボックスフォーム名を A) にセット
  4. ダイアログボックスに初期表示する値を B) に指定
  5. [OK]で閉じた時の処理を C) に記述

なお、今回はテンプレートコード掲載が目的ですので、フォーム作成手順などの詳細は割愛します。ご了承ください。

2023/04/02

Notes - Excel 連携:#6)マクロの記録の活用 と 文法の違い

ノーツと Excel を連携してアプリを作り始めると、やりたいことがどんどん出てきますよね。

その都度、Microsoft Learn で調べるのは非常に大変です。プロパティやメソッド、オブジェクト自身も非常にたくさんあります。そして、オブジェクトの関連をやさしく図式した概要的な記事は存在しません。少なくとも私は発見できていません。

ネットで検索しても、やりたいことを端的に記述した記事に出くわすことは少ないです。

このようなときに、活用できるのが、Excel に搭載されている『マクロの記録』機能です。この機能は、Excel の UI で行った操作を Excel VBA のマクロとして保存してくれます。


マクロの記録の使い方

リボンのマクロをクリックすると以下のメニューが表示されますので、[マクロの記録]を選択します。

すると、マクロの名称を入力する画面が表示されますので、そのまま[OK]をクリックします。それ以降の Excel の操作がマクロとして記録されますので、VBA コードを調べたい操作を行います。

マクロ記録中は、記録を開始したメニューが [記録終了] に変化します。必要な操作が終わったらクリックして、マクロの記録を停止します。

記録したマクロは、[マクロの表示]で確認できます。


Excel VBA との文法の違い

以下は、Sheet1 を選択後、シートを追加して、追加したシートを一番後ろの4番目にドラッグ&ドロップで移動した操作を記録したマクロです。

Sub Macro1()
'
' Macro1 Macro
'

'
   Sheets("Sheet1").Select
   Sheets.Add
   Sheets("Sheet4").Select
   Sheets("Sheet4").Move After:=Sheets(4)
End Sub

LotusScript の記述とずいぶん違いますね。

まず、Sheets オブジェクトの取得するコードがありません。Excel VBA は詳しくありませんが、基本的なオブジェクトは生成済みということなのでしょう...

続いて、Sheets オブジェクトに対して、シート名を添え字的に渡して、ワークシートオブジェクトにアクセスしています。シート番号(数値)だけではなく、名前でもアクセスできる柔軟な仕様のようです。便利ですね。

次は、Select メソッドです。Worksheet オブジェクトのメソッドとして、Microsoft Learn に記述があり、シートを選択するメソッドだそうです。

Worksheet.Select メソッド (Excel)

次の行の Add メソッドは、 Worksheets オブジェクトのメソッドです。Worksheets と Worksheet 混乱しないように注意しましょう。

Worksheets.Add メソッド (Excel)

構文を見てもらうと引数が4つあります。ただ、これら引数は省略できるとあり、その場合選択したシートの直前に追加されるとのことです。ここで、前行の Select メソッドが有効になるということですね。

以降は追加したワークシートを一番後ろに移動するコードです。以下の行に注目してください。LotusScript では見たことのない記述です。

   Sheets("Sheet4").Move After:=Sheets(4)

メソッドの詳細は以下の通りで、Before と After の引数があります。Excel VBA では、これら引数に値を与える場合、[引数名 := 設定する値]と記述でき、この仕様で、引数の順番にとらわれず、不要な引数を省略しやすくなっているようです。

Worksheet.Move メソッド (Excel)

LotusScript の場合、この記述は文法エラーとなり、利用できません。対策は簡単で、Microsoft Learn に記載の構文の通りに記述すればいいのです。ただ、以前記載した通り、このサイト機械翻訳の影響か不必要に日本語化されています。

以下は、日本語のサイトと原文の比較です。原文を見れば構文が良くわかりますね(笑)

 

LotusScript での記述

それでは、LotusScript で新しいワークシートを一番後ろに追加する方法です。

   Set oSheets = oXls.Workbooks(1).WorkSheets
   Call oSheets.Add( , oSheets(oSheets.Count))

Worksheets オブジェクトは何度もアクセスするので、oSheets 変数(バリアント型)にいったん代入しています。なお、oXls は、これまでの流れで、Application オブジェクトが入っている前提です

2行目で、Add メソッドをコールしています。このメソッドの引数は Worksheet オブジェクトです。oSheets.Count で最後のシートを指定して取得、After 側の引数にセットしているので、この1行だけで一番後ろに追加されます。


まとめ

今回はマクロの記録機能とその使い方、文法の違いなどをご紹介しました。

記録したコードはそのままでは動かないことも多く、Microsoft Learn で調べた上で、LotusScriptに移植する必要があります。

そうはいっても、マクロの記録は、実現方法のきっかけづくりとして、オブジェクトやメソッドにあたりを付けるという意味で、重要な役割を持っています。ぜひ、ご活用ください。

前回 Notes - Excel 連携 次回

2023/04/01

表示用の計算結果ってどんな仕様??

今日は”エイプリルフール”ですね。そんな日ですから、少し不思議な話をします。ですが、決してウソではなくホントの話です...

先日、Notes 12.0.2 評価の最中に、表示用の計算結果フィールドの挙動をテストしていて不思議な現象に出会いました。まずはそのお話からです。


表示用の計算結果はフィールドが作成されない

サンプルフォームを作成して、以下のフィールドを配置します。

フィールド名 種類
Fld_Text テキスト - 編集可能 (なし)
Fld_Now 日付/時刻 - 表示用の計算結果 @Now

以下の手順で文書を作成します。

  1. 文書を作成すると、現在の時刻が表示されます
  2. フィールドに値を入力し、保存します(文書①)

続いて、Fld_Now を『計算結果』に変更し、同じ手順で文書を作成します(文書②)。

比較すると、文書①には Fld_Now は存在せず、文書②には存在します。表示用計算結果は、値を保存しないフィールドということになります。

ここまでは、想定通りの挙動でした。


表示用の計算結果はフィールドが存在すると計算結果!?

次に、Fld_Now を『表示用の計算結果』に戻して、もう一度テストをします。

文書①を編集モードで開き、F9 キーを押し画面を更新します。Fld_Now には現在の日付が表示されます。その後、Esc キーを押し、画面を閉じます。保存を確認されず閉じたので、画面を更新しても文書の値は更新されなかったと判断されたようです。

では、同じことを文書②で実行します。すると、画面を閉じる際に保存を確認されました。保存した後、文書のプロパティを確認すると、フィールドが存在し、時刻が更新されています。

表示用の計算結果フィールドなのに、計算結果のように動作します。この挙動は、仕様なのでしょうか? バグなのでしょうか??


昔々の怪奇体験

はるか昔、2000年代初頭です。あるユーザ企業より問い合わせを受けました。ノーツDBの文書内の一部のフィールドが消失することがあるという申告です。

現象を確認すべく、訪問して症状を見せてもらいました。ビューに整然と並んでいる文書の所々が抜け落ちていました。設計を見てもフィールドを削除するようなコードは存在しません。そもそも、R3の開発コースしか受けてない方が作っていたので、フィールドの数は多いことを除けばいたって単純です。もちろん、LotusScript のコードも存在していませんでした。

困り果てながら、F9 を押していると、目の前でビューから値が消えました! そして、そのタイミングで、文書を保存されていることを発見しました。さらに、他の文書も同じユーザの更新が絡んでいました。

詳細を確認したところ、そのユーザだけ、ノーツのバージョンが R4.66b でした。それ以外のユーザは確か R4.61 だったかと思います。このバージョンの差だけで挙動を分けていたのですが、そのきっかけが『表示用の計算結果』でした。

調査や検証の結果、R4.6 の途中のバージョンから『表示用の計算結果』と同じフィールド名が存在した場合、保存時にフィールドを消去するように仕様が変更されたようです。そして、このアプリの開発担当者は、『作成時の計算結果』を使うべきところを『表示用の計算結果』としていたんです。

たったこれだけのことが、この怪奇体験のすべてでした...


まとめ

ちょっと思い出話が長くなりました。

今回発見した 12.0.2 の挙動も、はるか昔の怪奇体験も、存在するフィールドを『表示用の計算結果』で使用するから発生しています。しかも、R4.6 の頃に体験した現象は、12.0.2 では発生なくなっています。

仕様として何が正しいか? どうあるべきか? はさて置きまして、あらぬトラブルを避けるため『表示用の計算結果』として使用するフィールドには、値を持たせないということを徹底した方がよさそうです。命名規則を決めて、重複しないようにルール化するのもいいかもしれませんね。

とりあえず、製品が想定しない状況は避けるべきかもしれませんね。
世にも奇妙な現象に出くわさないようご注意ください...