ゲスト執筆者 Michael Höhne, 4D developer (ドイツ、ミュンヘン)
4D v18 R5には、見落とされているかもしれない、あるいは少なくともこれまであまり注目されていなかった機能があります。フォームマクロです。正直なところ、私も最近まであまり時間をかけていませんでした。今回は、リストボックスのカラム、カラムヘッダー、フッターに命名規則を適用する際に、時間を大幅に節約するマクロを紹介します。自分のニーズに合わせて簡単に変更することができます。専用レポもGithubで公開しています。
context first …
私たちは日々の業務で、開発を容易にするためにマクロを使用していますが、これは本当にクールな機能です。他の多くの企業と同じように、私たちもコーディングガイドラインを持っています。これは、大きなチームで仕事をするときに、お互いのコードをより簡単に理解するために不可欠なものです。4D言語は長い間変わらず、私たちのガイドラインも変わりませんでした。しかし、ORDA、クラス、そして変数やメソッドの宣言に新しい構文が登場しました。これらの新しい言語要素を取り入れ、内部仕様の全般的な見直しを行う時期がやってきました。その見直しの一環として、フォームのオブジェクト名について検討しました(新しい要素とはまったく関係ないのですが)。チェックボックス、ラジオボタン、フィールド、そして最後にリストボックスについて話をしました。私たちの命名スタイルについて書くつもりはありません。重要なのは、リストボックスは単一のオブジェクト名を持たないということです。リスト ボックスには、リスト自体、各列、各列のヘッダーとフッターに対応する名前があります。5列のシンプルなリストボックスには、16個のオブジェクト名があります。リストボックスに命名規則を適用しようとすると、面倒な作業になることがあります。自動化できたらいいと思いませんか?
会社を含むリストボックスのオブジェクト名は、次のようになります。
- listCompanies はリストそのものです。
- listCompaniesCol1、listCompaniesCol2、…というように、列の名前を指定します。
- listCompaniesCol1Header、listCompaniesCol2Header、…は、列ヘッダーです。
- listCompaniesCol1Footer, listCompaniesCol2Footer, …, は、カラムフッターです。
これはあくまで一つの可能性です。開発者に命名の好みを尋ねると、多くの異なる意見が返ってくる可能性があります。しかし、上記の名前と同じようなパターンがあるかもしれません。すべてのカラム、ヘッダー、フッターの名前は、リストボックスの名前に基づいています。これらの名前は、フォームマクロで割り当てることができます!
クイックスタート
フォームマクロは、フォームデザイナーのコンテクストメニューから利用できます。
フォームマクロを使ったことがない場合、コンテキストメニューにマクロメニューが全く含まれていない可能性があります。
この場合、まず formMacros.json ファイルを作成し、Project の Sources フォルダに置きます。
formMacros.jsonファイルは、メニュー項目の名前と、コードを実装するクラスを定義します。この例で使用するファイルは、とてもシンプルです。
{ "macros": { "Rename Listbox columns": { "class": "RenameListboxColumns" } } }
Macros メニュー(フォームデザイナーのコンテキストメニューに表示されます)に、「Rename Listbox columns」というメニュー項目を追加するよう、4D に指示します。メニュー項目をクリックすると、”RenameListboxColumns” という名前のクラスのonInvoke メソッドが呼び出されます(詳細は後述します)。formMacros.jsonファイルは、プロジェクトがロードされたときに一度だけ読み込まれます。内容の変更は、プロジェクトをリロードするまでUIに適用されません。もし今 formMacros.json ファイルを作成したのなら、一度プロジェクトを閉じてからもう一度開くと、Form デザイナーに Macros メニューが表示されます。フォームマクロに関する詳しい情報は、こちらをご覧ください。
フォームマクロはクラス
フォームマクロの実装は、クラスの中にあります。マクロ定義の「class」プロパティ(「class」: 「RenameListboxColumns」)で名前を定義しています。マクロに何かをさせるには、そのクラスを作成する必要があります。
それが動作するかどうかを確認するために、以下のコードを追加します。
// Macro invoked in the form designer.
Function onInvoke( : )-> : ("Hello world!") := ("currentPage"; . )$editor Object$return Object
ALERT // Return the modified page object
$returnNew object $editoreditor
マクロメニューの「リストボックスの列の名前を変更」を選択すると、アラートが表示されるはずです。
これは、すべてが正しく設定されていることを確認するための基本的なチェックです。考えられる落とし穴を示すために、コードを次のように変更します。
// Macro invoked in the form designer.
Function onInvoke showHelloWorldMessage() ( : )-> : . = ("currentPage"; . ) ("Hello world!")$editor Object$return Object
ThisshowHelloWorldMessage() // Return the modified page object
$return:New object $editoreditor
Function
ALERT
再度、”Rename Listbox columns” メニューを選択します。
4Dはクラス定義を一度だけ読み込みます。クラスメソッドの実装を変更することはできますが、新しいメソッドやメソッドパラメータの変更は、プロジェクトをリロードするまで利用できません。これは多少不便ですが、すぐに慣れるでしょう。プロジェクトをリロードした後は、コードは問題なく動作します。
機能の追加
さて、フォームマクロの設定方法がわかったところで、機能を追加してみましょう。Hello Worldメッセージを表示するのもいいですが、「Rename Listbox columns」という名前のマクロがもっといろいろなことをするのは理にかなっていると思います。たとえばカラムの名前を変更する。
onInvoke メソッドは$editor という名前のオブジェクトを渡します。このオブジェクトには、現在作業しているフォームに関する情報が含まれています。 currentSelection ($editor.editor.currentSelection) には、選択されたすべてのオブジェクトのオブジェクト 名 が入って います。もし、何も選択していなければ、空っぽになります。
currentPage ( ) には、現在のフォームページ上にあるすべての$editor.editor.currentPageオブジェクトが 含まれま す。 ( . ) には、form$editoreditor.formフォーム全体が含まれています。これらのオブジェクトは、単にフォームのJSON宣言の一部です。オブジェクト構造の概要を知りたい場合は、作業中の form.4DForm ファイルをテキストエディタで開けばよいでしょう。
リストボックスのカラム、カラムヘッダー、フッターのオブジェクト名を変更したいので、主なアプリケーションロジックは次のようになります。
- 選択されたオブジェクトをループします。見つかったリストボックスごとに、次のようにします。
- リストボックスの列をループし、特定のパターンに基づいて各列、列ヘッダー、フッターのオブジェクト名を計算します。
- 計算されたオブジェクト名の1つが既にフォームで使用されている場合、名前の変更処理を中止し、ユーザーにエラーメッセージを表示します。
- そうでない場合は、オブジェクトの名前を変更します。
最初のトピックから始めましょう。
- 選択されたオブジェクトをループ処理します。見つかった各リストボックスについて、前述の手順を実行します。
// Macro invoked in the form designer.
Function onInvoke( : )-> : : : := . ( . . . >0) ( ; This. . ) $editor Object$return Object
var $objectName Text
var $formObject Object
Thiseditor$editoreditor
IfThiseditorcurrentSelectionlength
For each$objectNameeditoreditor
$formObject := . . .[ ] ( . ( ) ) ThiseditorcurrentPageobjects$objectName
IfThisisListbox$formObject
This .( ; ) := ("currentPage"; . . )renameListboxColumns
$objectName $formObject
End if
End for each
End if
// Return the modified page object$returnNew object ThiseditorcurrentPage
このコードは、文章と一致しています(選択されたオブジェクトをループし、リストボックスをチェックし、その列の名前を変更する)。しかし、リストボックスをどのように特定するのでしょうか?コードにブレークポイントを入れて調査してみましょう。
各オブジェクトには、type プロパティがあります。リストボックスの場合、これは「listbox」に設定されています。これは、form.4dform ファイルをテキストエディタで開いたときに表示されるものと同じです。
// Check if the form object is a listbox
Function isListbox( : )-> : $formObject Object$isListbox Boolean
$isListbox :=( . ="listbox")$formObjecttype
とても簡単ですね!もちろん、このワンライナーは、新しいメソッドを宣言する代わりに、直接 に入れることもできますが、およそ30年間オブジェクト指向の開発をしてきて、私は小さなメソッドを作ることに慣れています。理解しやすく、メンテナンスしやすいコードを書くのに役立ちます。onInvoke
リストボックスを識別する方法がわかったので、次のステップをやってみましょう。
- リストボックスの列をループして、特定のパターンに基づいて各列、列ヘッダー、フッターのオブジェクト名を計算します。
// Renames all columns, column headers and footers based on
// the listbox object name.
Function renameListboxColumns( : ; : ) : : : :=1 ( ; . ) $lbxName Text $listbox Object
var $col Object
var $index Integer
var $newObjectName Text
$index
For each$col $listboxcolumns
This .=1 ( ; . ) .
setObjectName( ; +"Col "+ ( )) $col $lbxNameString$index
This .( . ; . +"Footer") setObjectName$colfooter $colname
This .( . ; . +"Header") := +1setObjectName$colheader $colname
$index$index
End for each
オブジェクトの名前を変更しても、オブジェクト名が重複しないようにする必要があります。
- 計算されたオブジェクト名のいずれかがすでにフォームで使用されている場合は、名前の変更処理を中止して、ユーザーにエラーメッセージを表示します。
- そうでない場合は、オブジェクトの名前を変更します。
// Changes the object name of $formObject to the $newObjectName.
// If the new object name is different than the current name
// and form object with that name already exists on any page
// of the form, the processing is aborted and a message is shown in the designer.
Function setObjectName( : ; : ) ( # . ) ( . ( . . ; ) ) ("オブジェクト名 " +" 既に使用されています。 リネームはキャンセルされました。") $formObject Object $newObjectName Text
If$newObjectName$formObjectname
IfThisisObjectNameUsedInFormThiseditorform $newObjectName
ALERT$newObjectName
ABORT
// abort further processing
Else
.
$formObject. :=name$newObjectName
End if
End if
既存のオブジェクト名のチェックは、さらにいくつかのメソッドで行われます。isObjectNameUsedInForm isObjectNameUsedInPage 、そしてisObjectNameUsedInListbox 。これらは、Githubで公開されている最終的なサンプルに含まれています。また、マクロをテストするために使用できるフォームも含まれています。
フォームマクロを日常業務で使う
フォームマクロの実装がひとつのプロジェクトに特化したものであるなら、そのプロジェクトに直接追加するのはまったく理にかなったことです。しかし、このようなマクロはフォームを含むどのようなプロジェクトでも役に立つ可能性があります。もちろん、クラスを各プロジェクトにコピーして、マクロの定義を各 formMacros.json ファイルに追加することもできますが、代わりにコンポーネントを作成するほうが簡単です。
フォームマクロコンポーネントは、4DプロジェクトにformMacros.jsonファイルとその中に定義されたクラスがあるだけのものです。唯一の違いは、コンポーネントをビルドして、プロジェクトの適切なコンポーネントフォルダに配置するか、4Dアプリケーションのコンポーネントフォルダに入れて、デフォルトですべてのプロジェクトで利用できるようにすることです。
もし、マクロを使いたいだけで、実装は気にしないのであれば:GitHubのサンプルでは、コンパイルされたコンポーネントがBuildフォルダに含まれています。