2024/05/14

クラス化に挑戦: #2)クラス定義のステートメント

クラス定義に必要なステートメントの基本的な使い方とユーザ定義クラスの基本的な構造についてまとめます。


使用するステートメント

クラス定義に使用するステートメントは主に次の通りです。

ステートメント 役割
Class クラスをユーザ定義します。
Property Get/Set クラスにプロパティを定義します。
Function
Sub
クラスにメソッドを定義します。

Function と Sub については、クラス定義以外でも使用するので、なじみがあると思います。


クラスの定義

まずはクラスの定義方法です。

Class ステートメントで記述を開始し、クラス名を指定します。以下の例では Employee がクラス名となります。そして、Class ~ End Class の間に作成するクラスの中身を記述します。

Class Employee
   (クラスの定義を記述)
End Class

ドミノデザイナーでは、クラスを作成すると左側のオブジェクトのリストに追加されます。


プロパティとメンバ変数

プロパティはインスタンスに対して値の取得と設定ができます。Property の後ろに Get をつけるとプロパティから値を取得でき、Set でプロパティに値を設定できるようになります。また、As 以降でプロパティの型を定義します。下記の例では、Birthday というプロパティは Variant 型で設定と取得ができる定義となっています。

Class Employee
   zvBD As Variant

   Property Get Birthday As Variant
       Birthday = zvBD 
   End Property
   Property Set Birthday As Variant
       zvBD = Birthday 
   End Property
End Class

当たり前ですが、Get / Set とも同じ型になります。なお、Property は Get だけを記述すると取得のみ、Set だけだと設定のみのプロパティとなります。


Class ステートメント直下には変数を宣言できます。この変数はインスタンス内の値を保持するエリアとして使用されます。

Property Get / Set ステートメントではプロパティ名が値のインターフェースとなります。例えば Set の場合は、Birthday にコール元の値が入ってきます。メンバ変数 zvBD をセットすることでインスタンス内に保持させています。


メソッド

値を返すメソッドの作成には Function、戻り値が不要の場合は Sub ステートメントを使用します。Class ステートメント内に記述すること以外は、通常の関数作成とほぼ同様です。以下の例では使用していませんが、引数を定義することもできます。

違いがあるとすれば、メンバ変数にアクセスできることが挙げられます。

Class Employee
      ・・・
   Function GetAge() As Integer
      Dim iAge As Integer

      ' 現在日付と誕生日(zvBD)から年齢を計算
      iAge = ・・・   

      GetAge = iAge   '戻り値をセット
   End Function
End Class


デザイナーとユーザ定義クラス

ここまで作成するとデザイナーでは次のように表示されます。

作成したプロパティやメソッド、メンバ変数はクラス配下のエントリとしてリストされます。リストはソートされており、クリックするとその部分のソースコードが表示できるので、効率的に開発できますね。


クラスの利用

ユーザ定義型クラスは、以下のように変数宣言の New でインスタンスが作成できます。

   Dim oEmp As New Employee

そして、定義済みのクラスと同様に、"."(ピリオド)を入力することにより、プロパティやメソッドにアクセスできます。


前回 クラス化に挑戦


2024/05/12

クラス化に挑戦: #1)クラスって何?

LotusScript でのノーツアプリケーション開発では、NotesSession クラスなど Notes Object Class と呼ばれる定義済みのクラスを使用して開発します。ですので、日ごろから必ずと言っていいほどクラスを利用していることになります。

このシリーズでは、定義済みのクラスではなく、開発者自身が定義する方法をまとめます。クラスには、開発効率を高める効果がありますので、習得しておくと おトク だと思います。

なお、この記事は私自身の経験をもとに記載しています。真似事から始めて、少しずつ経験を積んできた結果をまとめたものになります。オブジェクト指向を本気で勉強し、正確な知識に裏打ちされた記事ではないので、誤解や偏りがあるかもしれません。あらかじめご了承ください。


クラスとは?

プログラミングの世界で ”オブジェクト指向” と呼ばれ、モノ(=オブジェクト)に着目した考え方があります。このオブジェクトの設計書(定義)をクラスといいます。プログラム言語的に表現すると、クラスは属性(プロパティ)と操作(メソッド)を持つデータ型といえます。

例えば、社員情報を管理するクラスを考えます。

社員情報としては、社員番号 / 名前 / 誕生日 などが考えますが、これら項目がプロパティとなります。そして『年齢を取得する』などの何らかの処理を行って結果を返すような操作がメソッドとなります。


インスタンス

クラス定義をもとに作成した実態をインスタンスといいます。クラスは設計書(定義)でしたが、インスタンスは実態ですのでデータを保持しています。

プログラム上では、インスタンスはオブジェクト変数に格納されます。例えば、先の ”社員情報” クラスで 2 名分のインスタンスを作成すると次のようになります。

◇ 山田 太郎 のインスタンス


◇ 佐藤 花子 のインスタンス


なお、インスタンスは ”オブジェクト” と呼ばれることが多いです。どちらも ”実態” で同じ意味となります。


LotusScript とクラス

LotusScript では、クラスを定義するステートメント Class が用意されています。開発者自身が自由にクラスを定義し、プログラム内で利用できます。

このステートメントを使用してクラスを定義する方法については次回にまとめます。


クラス化に挑戦 次回


2024/05/08

コメントの有効活用

前回の記事 でプログラム内のコメントについて、後任開発者のためにも手を抜かずしっかり記述することが重要だとまとめました。とはいっても、記述するにはそれなりの時間がかかります。せっかく書いたコメントですから有効に活用したいですよね。


ホップアップヘルプ

コメントを有効活用する上で効果的なのが、関数ヘッダコメントです。

8.x 以降のドミノデザイナーでは、関数やメソッドのカーソルをあてるとその関数の定義がポップアップヘルプとして表示されます。

この機能は、開発者自身が作成した関数でも有効となるのですが、ここに関数ヘッダコメントを表示することができます。


関数ヘッダコメントの書き方

別の記事 『Notes - Excel 連携:#25)罫線の設定 ①』で作成した RGB という関数を例に記載します(関数の中身は今回の記事とは何ら関係ありません)。

コメントを記載する前の状態は次の通りです。

Public Function RGB(_
              ByVal vbyR As Byte, ByVal vbyG As Byte, ByVal vbyB As Byte) As Long
    RGB = vbyR + CLng(vbyG) * 256 + CLng(vbyB) * 256 ^ 2
End Function

この関数を使用したエージェントでは、以下のようにポップアップヘルプが表示されます。


スクリプトライブラリを開き、この関数にヘッダコメントを記載します。

%REM
RGB メソッド ( lsXls ライブラリ )
---------------------------------------------------
R, G, B の各要素から Excel で使用する色番号を返します。
 
◆ 引数
vbyR  Byte 赤
vbyG  Byte 緑
vbyB  Byte 青
  
◆ 戻り値
Long
%END REM

Public Function RGB(_
              ByVal vbyR As Byte, ByVal vbyG As Byte, ByVal vbyB As Byte) As Long
    RGB = vbyR + CLng(vbyG) * 256 + CLng(vbyB) * 256 ^ 2
End Function

このように記述すると、この関数のポップアップヘルプに関数ヘッダコメントが表示されます。


変数コメントの場合

変数コメントは、定義の右側にコメントを記述することとしていました。ただ、このコメントはポップアップヘルプに表示されません(事例は変数ではなく定数)。

ポップアップヘルプの機能は、定義直上のコメントしか表示しない仕様のようで、以下のように記述すると表示されました。


ドミノデザイナーでは、右クリックメニューから [宣言を開く] を選択すると、関数や変数などの定義を参照でき、この機能を使えばコメントも確認できます。ただ、元の位置に戻る機能がないので、コメント参照のためだけに画面を切り替えるのは、作業効率が悪くなります。

関数内の変数であれば、宣言と変数の利用は近接するので参照も容易ですし、元の位置に戻るのも簡単です。このような側面から、コメントは宣言の右側とし、プログラムの行数を減らし、関数の一覧性を優先しました。蛇足ですが、関数内の変数のコメントを頻繁に参照する必要がある関数は仕様が複雑すぎるのかもしれません。独立性の観点から改善を検討すべきかもしれませんね。

先の事例のように定数宣言や Private 変数の場合や Public 変数(独立性の観点から積極的にお勧めはしませんが)では、ポップアップヘルプの活用は有効な記述方法だと思います。


まとめ

今回は、記述したコメントの有効活用を紹介しました。この方法を使えば、開発中の作業効率が上がるだけでなく、コメントの不備にも気づくことができます。コメントを記述する動機付けにもなりますので、コメントが更新されていないという状況は減るかと思います。

私の開発チームでは、スクリプトライブラリ開発において Public な関数や定数には今回のようにコメント記述するルールにしています。

2024/05/05

コメントの書き方

ほかの開発者が作成したプログラムがわかりにくいと感じませんか?

その理由は、仕様やインターフェースが不明瞭であったり、プログラムの構造が自分自身の思考とは違っていたり、コーディングの癖が生理的に合わなかったりさまざまです。

ただ、このような状況であっても、コード内のコメントが適切に記述されていれば状況は改善されています。そこで、今回はコード内のコメントの書き方について整理したいと思います。


コメントの種類

プログラム内のコメントは次の 4 つに大別できます。

変数コメント 変数の用途を記述
ブロックコメント ひとまとまりの機能や複雑な部分の説明を記述
関数ヘッダコメント 関数のインターフェースや機能を記述
ライブラリヘッダコメント スクリプトライブラリの機能や使い方を記述


変数コメント

変数コメントは、変数宣言でその変数の用途を記述するもので、変数宣言の右側に続けて記述すると場所を取らずわかりやすくなります。

まれにすべての変数に対してコメントされているプログラムを見ることがありますが、必要最小限にしておかないとかえって見づらくなります。

ループ変数など汎用的な変数、NotesSession や NotesUIWorkSpace など中身が固定的なものは不要です。

例えば、ステータスの変数では、とりうる値やその値の意味、単語を省略した変数名では誤解を招かないよう注記するなど、重要な変数だけにとどめましょう。


ブロックコメント

プログラム内に記述するコメントです。

ビューから次の文書を取得する GetNextDocument メソッドに対して『次の文書を取得』などとコメントしているプログラムを見ることがあります。このようなコードを見ればすぐわかるコメントは不要です。

ブロックコメントは、一連の機能や複雑な部分に対して説明するコメントすると有用です。例えば、あるループ処理があったとして、ループ内の機能概要やループの継続条件/終了条件を記述すると効果的です。


関数ヘッダコメント

関数の先頭に記述するコメントで、機能や使い方を記述します。例えば次のような項目です。

  • 関数の機能
  • 引数の型と説明
  • 戻り値の型と説明
  • 外部参照(この関数を利用するために必要な設計要素)
  • 使い方

これらが記述されていると、安心して関数を利用できます。すべての関数について記述することが望ましいですが、少なくともパブリックな関数には必ず記述しましょう。


ライブラリヘッダコメント

スクリプトライブラリの機能や使い方を記述するコメントで、スクリプトライブラリの先頭である Options セクションの先頭に記述します。

記述する内容は次のような項目です。

  • ライブラリ名称
  • 最終更新者/更新日
  • 作成者/作成日
  • バージョン
  • 外部参照(このライブラリを利用するために必要な設計要素)
  • 使い方
  • 更新履歴


不要なコメントの削除

プログラムの修正履歴や修正前のコードをコメントして一時保管することがよくあります。修正を繰り返したプログラムだとコメントだらけになり、現在のコードのためのコメントがどれか判別できなくなります。

不要となったコードの残骸、修正前のコードのコメント、必要以上の更新履歴は削除するようにしましょう。


まとめ

プログラム内に適切にコメントがあると、プログラムの理解が早くなります。

後任の開発者が最初によりどころとするのはコメントです。プログラムの動作に関係なくても、ビジネス上は重要な役割を持つといってもいいでしょう。手を抜くことなく、しっかり記述しましょう。また、修正時にはコメントもメンテナンスするとよいかと思います。


2024/05/02

関数の引数と値渡し

チーム開発』のラベルを付けた前回の記事『関数のインターフェースと独立性』では、関数作成において、引数と戻り値以外のインターフェースをなくせば、独立性が高くなり、後任者に伝わりやすいプログラムになることを紹介しました。

今回は、この引数にもう少し掘り下げたいと思います。


引数のタイプ

VBA の言語仕様として、関数の引数には、値渡しと参照渡しの 2 つのタイプがあります。


◇ 値渡し

値渡しの特徴と使い方は次の通りです。

  • 引数の値はコピーされ関数に渡される
  • 関数内で値を変更しても呼び出し元の値は変更されない
  • 値渡しは、引数の定義で ByVal を指定すること

図式化すると次のようになります。

まず、外側のプログラムで、関数 Sample を呼び出します。引数 a には "Lotus" と入っていて、関数内の変数 b で値を受け取ります。関数 Sample 内で、b の値を "Script" に変化させていますが、関数を抜けた後の変数 a の値に変化はありません。


◇ 参照渡し

参照渡しには次のような特徴があります。

  • 値を保持しているメモリ領域のアドレスを関数に渡す
  • 同じメモリ領域を共有するので、関数内で値を更新すると呼び出し元の値も変更される

先の事例を参照渡しに変更した場合を同じく図式化すると次の通りとなります。

参照渡しの場合は、関数内で値を変更すると呼び出し元の変数 a の値が更新されます。

値渡しの場合、引数の宣言で ByVal と記載しました。LotusScript では、参照渡しを指定するキーワードがありません。ByVal をつけないと参照渡しとなります。


引数の使い方

引数のタイプを指定するには条件があります。

値渡しが設定できるのは、数値や文字列などの単一のデータを表す変数(スカラー型)となります。引数の宣言で ByVal と記述すると値渡し、省略すると参照渡しとなります。

配列や Variant、オブジェクト変数は値渡しは指定できません。必ず参照渡しとなります。


引数のタイプと関数の独立性

引数と戻り値以外のインターフェースを持たない関数を前提に考えます。

引数が値渡しの場合、関数内でどのような処理が行われたとしても、呼び出し元に影響を与えるのは戻り値だけとなります。逆に引数が参照渡しの場合、関数内の処理によっては値が書き換わる可能性があるということです。

独立性という側面で考えた場合、値渡しでよい引数は ByVal と記述することで独立性を高められるということになります。


チーム開発と引数

以前、紹介した『命名規則の例(LotusScript)』で変数の命名規則について記述しました。変数名の先頭がスコープやコーディング上の用途を指定していました。この ”用途” にあたる部分が今回の引数のタイプに該当します(命名規則をまとめたページはこちら)。

下図の通り紹介したのですが、赤枠の部分が引数に関する部分です。

例えば、次のように記述します。

   Sub Sample( ByVal vsName As String )

   Sub Sample( rsName As String )

これで変数名を見るだけで、値渡しか参照渡しかの判定が可能となります。


同様の命名規則は、スカラー変数以外にも適用しています。実際には、値渡しは設定できないのですが、変数名で次のように使い分けています。

vndSample 関数の外側から与えられる文書
関数内で文書を変更しない
rndSample  関数内で文書をセットし呼び出し元へ返す変数

vndSample では、文書は変更しないとなっていますが、別の文書に差し替えない限り、フィールドのセットなどの更新までは禁止していません(利便性優先)。


まとめ

今回は関数の引数にフォーカスして、引数の値渡しと参照渡しについて紹介しました。

かなり細かな話になりましたが、こういった機能があることを理解し、活用することで、より見やすい、理解しやすいプログラムを作成できるようになります。参考になれば幸いです。

2024/04/27

DXL Step-by-Step:#33)表の幅のしくみ

今回は表の幅や列の幅について調査した結果をまとめます。


表の幅の設定

表の幅の設定は 3 種類に大別できます。そして、固定幅にすると 左・中央・右 にそろえるかを指定できます。

この設定を DXL で表現すると次のようになります。

  <table widthtype='fixedleft'>

table ノードの widthtype という属性で指定されるということですね。その種類は以下の通りです。

属性 設定値 補足
widthtype 'fitmargins' マージンに合わせる
'fitwindow' ウィンドウに合わせる
'fixedleft' 固定幅(左)
'fixedcenter' 固定幅(中央)
'fixedright' 固定幅(右)

表のプロパティで固定幅の表を選択すると「位置」を選択できます。それに合わせて、固定幅の設定は 3 種類用意されています。


マージンの設定

表の余白の設定で、表の左右の余白(マージン)の設定が可能です。

DXL では、次の属性を使用し、それぞれ、長さ(インチ)と割合(%)で指定できます。

属性 設定値 補足
leftmargin '2in' 左マージン(インチ)
'10%' 左マージン(%)
rightmargin '12.3456in' 右マージン(インチ)
'100%' 右マージン(%)


幅の設定とマージン

ノーツクライアントで表を作成し、DXLで確認すると、次のようなことがわかりました。

  • 左マージンは 1 インチの場合は省略してよい(属性を作成しない)
  • 右マージンは明示的に設定したいときだけ設定
  • マージンに合わせる表のデフォルトは左右のマージンを指定しない

また、ウィンドウに合わせる表では、左マージン 0.75 インチで、右は指定されませんでした。左マージンを指定しないだけでなく、0 でもないことが不思議ですね。

また、固定幅であっても左右のマージンを % で指定できるようでした。ただ、ウィンドウの幅を変えるとマージンが変化し、表が ”固定” されないので、活用しづらそうだと感じました。


前回 DXL Step-by-Step


2024/04/25

DXL Step-by-Step:#32)表の作成と行・列の間隔

DXL 活用の調査・検証で、実現できたことや発見したことご紹介する『DXL Step-by-Step』シリーズの第 32 回です。前回から ”表” の構造について整理しています。

今回は、実際に LotusScript で表を作成してみます。そして、LotusScript の NotesRichText*** クラスでは操作できない行の間隔と列の間隔を設定します。


メインルーチン

#27)イメージリソースの表示』で作成したエージェントをコピペして修正する前提で記載します。記載していない部分のプログラムについては変更はありません。

まずは、DXL を作成するメインルーチン xSetDXL です。表を作成する関数をコールしています。また、表の前後には段落が必要となりますので、同じ処理となりますので、関数化しています(関数については後述)。

Function xSetDXL(vdprs As NotesDOMParser)
         ・・・
   Dim denTbl As NotesDOMElementNode
         ・・・
   'リッチテキスト作成
   Set den = ddn.CreateElementNode("richtext")
   Set denRT = denItem.AppendChild(den)

   '段落定義
   Call xSetDXL_pardef(ddn, denRT)


   '表前の段落追加
    Call xSetDXL_par(ddn, denRT)

   '表の追加
    Set denTbl = xSetDXL_table(ddn, denRT, 3, 2)

   '表後の段落追加
    Call xSetDXL_par(ddn, denRT)
 End Function


表の作成

xSetDXL_table 関数では、新しい表を作成しリッチテキストに追加します。引数で作成する表の行数と列数を指定できるようにしています。前回確認した通り、表のノードは複数の階層で構造化されていました。以下のリストでは階層がわかるよう変数の文字色を変えています。

Function xSetDXL_table(_
            vddn As NotesDOMDocumentNode, _
            vdenRT As NotesDOMElementNode, _
            ByVal viRows As Integer, ByVal viCols As Integer _
            ) As NotesDOMElementNode
   Dim denTbl As NotesDOMElementNode
   Dim denRow As NotesDOMElementNode
   Dim den As NotesDOMElementNode
   Dim iCol As Integer
   Dim iRow As Integer

   '表の作成
   Set den = vddn.CreateElementNode("table")
   Set denTbl = vdenRT.AppendChild(den)

   '列定義の作成
   For iCol = 1 To viCols
      Set den = vddn.CreateElementNode("tablecolumn")
      Call denTbl.AppendChild(den)
   Next

   '行の作成
   For iRow = 1 To viRows
      Set den = vddn.CreateElementNode("tablerow")
      Set denRow = denTbl.AppendChild(den)

      'セルの作成
      For iCol = 1 To viCols
         Set den = vddn.CreateElementNode("tablecell")
         Call denRow.AppendChild(den)
      Next
   Next

   Set xSetDXL_table = denTbl
End Function


段落の作成

xSetDXL_par 関数は表の前後に配置する空の段落を作成します。par ノードを作成し、リッチテキストに追加するだけのシンプルなプログラムとなっています。

Function xSetDXL_par(_
            vddn As NotesDOMDocumentNode, _
            vdenRT As NotesDOMElementNode _
            ) As NotesDOMElementNode
   Dim den As NotesDOMElementNode

   '段落の作成
   Set den = vddn.CreateElementNode("par")
   Call den.SetAttribute("def", "1")
   Set xSetDXL_par = vdenRT.AppendChild(den)
End Function


ここまでのエージェントを実行すると、3 行 2 列の素気のない表が作成されます。


行・列の間隔

それではこの表に行の間隔と列の間隔をセットしてみましょう。プロパティの以下の設定です。


この設定は table ノードの次の属性で設定します。

属性 設定値 補足
columnspacing '0.0591in' など 列の間隔(セル内側の左右余白)
rowspacing '0.0591in' など 行の間隔(セル内側の上下余白)


間隔を設定する関数を新規に作成します。

引数で渡された table ノードに対して SetAttribute メソッドで属性をセットしています。

Function xSetDXL_table_spcing( _
            vdenTbl As NotesDOMElementNode, _
            ByVal vdRowSpan As Double, ByVal vdColSpan As Double)
   Dim s As String

   '行の間隔
   s = DXL_CMToInch(vdRowSpan)
   Call vdenTbl.SetAttribute("rowspacing", s)

   '列の間隔
   s = DXL_CMToInch(vdColSpan)
   Call vdenTbl.SetAttribute("columnspacing", s)
End Function

ポイントは、設定の単位がインチであることです。引数で指定されたセンチメートルの値を #17)段落の定義② 余白の設定 で作成した関数 DXL_CMToInch でインチの文字列に変換しています。


メインルーチンには以下の行を追加し、行の間隔を 0.2 cm、列の間隔を 0.5 cm に設定します。

         ・・・

   '表の追加
   Set denTbl = xSetDXL_table(ddn, denRT, 3, 2)

   '間隔の設定
   Call xSetDXL_table_spcing(denTbl, 0.2, 0.5)

   '表後の段落追加
   Call xSetDXL_par(ddn, denRT)
End Function


実行結果

できあがったプログラムを実行すると行列の間隔を設定した表が作成されます。

ちなみに、この処理で作成される DXL は次の通りとなります。


前回 DXL Step-by-Step 前回


2024/04/22

DXL Step-by-Step:#31)表の基本構造

DXL 活用の調査・検証で、実現できたことや発見したことご紹介する『DXL Step-by-Step』シリーズの第 31 回です。今回からは ”表” の構造について調査した結果をまとめます。

LotusSctipt の NotesRichText*** クラスでは、表は作成できるのですが、細かなプロパティの操作はできないものが多いです。DXL を使えばほとんどの操作(たぶん全部?)が可能となりますので、できることが一気に広がりますよ。


表の作成位置

第 15 回 でリッチテキストフィールドを表す richtext ノード直下には、次の 4 種のノードが配置できると紹介しました。

pardef 段落の定義
par 段落
table
section セクション

前回まで "段落" やその中に配置できるオブジェクトに関してでしたが、今回からまとめる "表" は段落と並列に配置される table ノードとなります。


表の基本構造

まずは table ノードの周辺の仕様を確認します。

以下の DXL はリッチテキストに 3 行 2 列の単純な表を作成し、出力した結果です(一部省略して表示しています)。

まず richtext ノードに段落定義を表す pardef ノードがあり、それに並列に table ノードが配置されています。上表の通りですね。

また、table ノードの前後には段落を表す par ノードが存在します。いろいろと検証した結果、このノードがないと表を正しく表示できない”仕様”のようです。特に上側の par ノードがないと DXL と保存(Import)できませんでした。

リッチテキストに表を作成すると前後の段落が作成されるので、この動作を踏襲しているのだと思います。


表の中身

続いて table ノードの中の構造を確認します。

まず、表の列の定義があります。tablecolumn ノードが 1 列分を表します。今回の表は 2 列だったので、tablecolumn ノードも 2 つ存在します。

列定義に続いて、行を表す tablerow ノードが存在します。今回は 3 行存在するということですね。


行の中身

行である tablerow ノードの配下には tablecell ノードが列数分だけ配置されています。

tablecell ノード内には、段落を表す par ノードが存在します。表のセルは、richtext ノードと同様の機能を持つ仕様になっています。これを利用して表内にさまざまなコンテンツを配置できます(詳細は別の機会にまとめます)。


まとめ

今回は表の基本構造についてまとめました。HTML の表と近い構造ですので、理解しやすいかと思います。

次回からは、表の細かな設定についてまとめます。表には多種多様な機能・設定が存在します。検証できたものから順に紹介します。


前回 DXL Step-by-Step 次回


2024/04/16

作ってみよう:#5)お小遣い帳 - Nomad 用フォームの作成

前回に引き続き、Nmad Mobile で使いやすい修正を加える作業を行います。今回の対象はフォームです。そのままの状態だと情報量の割に隙間が多く、画面を有効に使えていません。

この改善を含めて、Nomad 専用のフォームを追加します。


左右余白の削除とスクロールの停止

既存のフォームをコピー&ペーストして、Nomad 用フォームとして仕上げます。

まずは、フォームの左右に隙間をなくし、画面を有効活用します。これには、表の幅の設定を「ウィンドウに合わせる」に変更すると対応できます。

ただ、スクロールの発生に合わせて、上下だけでなく左右にまでスクロールできてしまいます。これだと使いずらく、結果的に状況は悪化しています。

今回のケースでは、フレームセットでスクロールを止める方法で対処します。


事前にフレームセット mfsExpence を作成します(モバイル用の設計要素には m を先頭についけています)。フレームは1つだけにして、名称を mfrmForm とし、スクロールを停止します。

Nomad 用フォーム側では、自動フレームの設定で、先ほど作成したフレームセットを指定します。


上下の余白削除とレイアウトの変更

タイトル行(”支出”と表示しているグレーの帯)の上下のスペースが無駄なので詰めます。また、スペースの有効活用とスマホ用の対応として次の修正を行います。

  • 表の上下を非表示に変更
  • 金額と日付を横並びに配置
  • 表の連結は不意におかしな動作をすることがあるので、表を項目ごとに分離
  • 各表の間のスペースは非表示
  • キーボードを使用するメモ欄を上に移動
  • 罫線が主張しすぎるので、色を薄くし、下側のみ表示
  • フォントを少し大きく

また、画面に隙間が空くなど美しくなかったので次の調整を行いました。

  • タイトル行の 行の間隔: 0 cm
  • 明細行の 行の間隔: 0.3 cm


リストボックスの利用

ダイアログリストの[▼]ボタンは少し小さいですね。また、タップ数も増えますので、リストボックスを採用し直接的な入力に変更します。

フィールドの種類をリストボックスに変更し、サイズを入力すると設定できます。また、列を追加してフィールドを横並びにして、操作しやすくしてみました。


なお、場所の項目は将来的に Google Map と接続する予定なので、そのままとしています。


フォーム名の設定

このフォームはノーツ用とは別の”スマホ用”ですので、次の通りフォーム名を設定します。

   m.支出 | mfExpence | fExpence

別名が 2 つ存在します。ノーツでは別名が複数あるフォームで文書を保存した場合、フォーム名が記録される Form フィールドには最後の別名がセットされます。

これを利用して、スマホから文書を作成する場合、このフォームで開くようにしています(後述)。また、Nomad 用のビューから開いた場合もこのフォームで開くようにフォーム式を設定します。


この設定により、環境に応じたフォームで表示するアプリが作成できます。Form フィールド的には 1 種類となりますので、ノーツとハイブリッド運用でも、既存環境への影響を低減できますね。


新規作成ボタン

文書を作成するためのボタンを配置します。ボタンを作成するフォームは前回作成した埋め込みビューを配置しフォーム mfViewByMon です。

ボタン
閉じる @Command([FileCloseWindow])
新規作成 @Command([Compose]; "mfExpence")

前述の通り、新規作成時のフォーム名が "mfExpence" となっているのがポイントです。



動作確認

これで、スマホ用の対応はいったん完了です。Nomad Mobile からアクセスして動作確認を行います。多少の改造は、必要ですが、見た目や操作性はよくなったと思います。


最後に

今回採用したフォームのスクロール停止方法では、縦方向のスクロールもできなくなります。フォーム内に項目が多くスクロールが必要になる場合には利用できませんので、ご注意ください。

フレームに横方向のスクロールだけ止める機能があるといいんですけどね...


前回 作ってみよう


2024/04/14

作ってみよう:#4)お小遣い帳 - Nomad からのアクセスとビューの作成

前回までで、支出が入力ができるアプリになりました。最終的には入力はスマホから行う予定ですので、Nomad Mobile からアクセスしてみましょう。


まずは、そのままの状態で Nomad Mobile で開いてみます。

ノーツのアプリがそのままスマホで利用できるのは本当にすごいと思います。ただ、使いやすいか?と言われると、残念ながらそうではありません。PC は横長、スマホは縦長の画面ですから仕方ありませんね。Nomad はスマホを横持ちすると横画面になりますが、それでも、画面解像度の差もあり厳しいです。

スマホで使いやすくするためには、スマホに合った開発が必要ということですね。今回は、スマホからの利用を想定したアプリですので、専用の設計を追加することとします。


ビューの作成

画面構成として、左にナビ、右にビューという画面構成に無理があります。まずは、最初の画面がビューだけになるようにしたいと思います。次のようなイメージです。

単一カテゴリ機能を利用して、カテゴリの開閉が必要のないすっきりとした画面を作成します。


ビューの作成

先にビューを作成します。

フォームの埋め込みビューの単一カテゴリ機能を使用します。重要なのは表示するデータの判定に使用される 1 列目ですね。次のような式を記述して、年月度でカテゴライズします。

   xY := @Text(@Year(Date)) + "年";
   xM := @Right("0" + @Text(@Month(Date)); 2) + "月";
   xY + " " + xM


フォームの作成

続いて、下記のような新規フォーム mfViewByMon を作成します。

まず次の 3 つのフィールドを作成します。

フィールド 種類 詳細
SaveOptions テキスト このフォームは保存する必要がないので設定。
デフォルト値は "0"。常時非表示。
CategoryKey_Lst テキスト 表示用の計算結果。複数値。常時非表示。
CategoryKey コンボボックス 「ウィンドウに合わせる」表の中に配置。

その下に埋め込みビューを配置します。先に作成したビューを埋め込みビューとして配置しして、画面いっぱいに表示されるように設定します。

続いて「単一カテゴリの表示」にフィールド CategoryKey を指定します。これで、CategoryKey フィールドで指定した年月のデータだけがビューに表示されるようになります。


最後に選択肢の設定を行います。

CategoryKey_Lst は選択肢を保持するための一時フィールドです。表示用の計算結果で、下記の式を指定します。

   xLst := @DbColumn("":"NoCache"; "":""; "mvExpenceByDate"; 1);
   @If(@IsError(xLst); ""; xLst)

埋め込みビューの 1 列目をリストで取得しています。


次にコンボボックスの CategoryKey フィールドです。

選択肢はフィールドプロパティで「式で選択肢を設定」を指定、以下の式を設定します。

   @GetField(@ThisName + "_Lst")

フィールドのデフォルト値には以下の式に設定します。

   xLst := @GetField(@ThisName + "_Lst");
   @Subset(xLst; 1)

CategoryKey_Lst の値を使って、複数回の検索(@DbColumn)を行わないようにしています。


フレームセットの作成

画面構成の一番外側にあたる上下 2 分割のフレームセット mfsMain を作成します。

上側のフレーム mfrmHeader はアプリのタイトルを表示するだけのフレームです。作成手順は記載しませんが、@DbTitle でアプリ名称を表示するだけの単純なページを指定し、都合のいい高さで固定します。

下側のフレーム mfrmView には先ほど作成したフォーム mfViewByMon を配置します。

なお、各フレーム内のスクロールは止めておきましょう。


最後に、アプリケーションのプロパティで作成したフレームセットを開く設定をして完了です。

これで View まわりの開発は完了ですので、Nomad で確認します。次のようにすっきりした画面で表示されます。

※ 2024/4/15 加筆 アクションボタンは次回の作成です。


参考文献

Nomad アプリの作り方は、下記セミナー資料に参考にさせていただいております。

HCL Nomad Mobile アプリ開発セミナー

Nomad の挙動や設定テクニックなど、網羅的に記載されているので、ぜひご確認ください。私自身、今後もさまざなシーンでお世話になることになるかと思います。


前回 作ってみよう 次回