このドキュメントでは、次のトピックについて説明します。
リストビューコントロールは、アイコンまたは列を基準にした形式でデータを表示するために使用できます。 リストビューコントロールは、非常に強力なコントロールです。 ただし、タブ形式でデータを表示し、任意の列の値をその場所で直接編集する場合は、リストビューコントロールではなくテーブルコントロールの使用を検討する必要があります。
Natural のリストビューコントロールは、アイコン、小さいアイコン、リスト、レポートの 4 つのビューモードでデータを表示できます。
アイコンビューモードでは、データは大きいアイコン形式で表示されます。
小さいアイコンのビューモードでは、項目ラベルがアイコンと一緒に表示されます。 アイコンビューでは、項目を任意の位置に表示できます。
リストビューモードでは、小さいアイコンのビューモードと同じように項目が表示されますが、自由に配置することはできません。 代わりに、整列して表示されます。
レポートビューモードでは、項目ごとに 1 行が割り当てられ、項目に関連付けられている他のデータが同じ行の個別の列に表示されます。 通常は、以下の例のように、列ヘッダーも表示されます(リストビューコントロールの[ヘッダーなし(x)]STYLE
フラグを設定することにより、任意で非表示にできます)。
レポートビューでは、列の仕切りをドラッグすることにより、列のサイズを変更できます。 代わりに、列ヘッダー内の列の右側の区切りをダブルクリックすると、列内で最も長いテキストに合わせて列の幅が調整されます。
上記の例は、リストビューコントロール自体、4 個のリストビュー項目、および 6 個のリストビュー列の合計 11 個のダイアログエレメントから構成されていることに注意してください。 リストビュー項目とリストビュー列の両方とも、PARENT
としてリストビューコントロールを持ち、同じ SUCCESSOR
チェーンに格納されます。 リストビュー項目とリストビュー列を混在させることは可能ですが、構成とパフォーマンスの両方の観点から、すべてのリストビュー列がすべてのリストビュー項目の前に来るように設定することをお勧めします。 例えば、空でないリストビューコントロールに最後の列として新しい列をダイナミックに挿入する場合、列の
SUCCESSOR
属性に何も指定しないのではなく、最初のリストビュー項目のハンドルを明示的に設定するか、または新しい列がチェーンの最後、つまりすべてのリストビュー項目の後に配置される NULL-HANDLE
を設定します。
リストビューコントロールに対して作成される最初のリストビュー列には特別な意味があります。この列を(ここでは)プライマリ列と呼びます。 プライマリ列には常にリストビュー項目ラベル(つまり、STRING
属性値)が表示されます。 その他の列には、サブ項目データと呼ばれるものが表示されます。 例えば、"Currency サブ項目" は "Currency" 列に格納されているデータを参照します(上記の例を参照)。 列内の特定の値を参照するには、さらに厳密に指定する必要があります。 例えば、上記の "1277.18" という値は、"Second item" 項目の "Currency サブ項目" として参照されています。 サブ項目は、Natural のダイアログエレメントのタイプではありません。これについては、後で説明します。
リストビュー列が作成されていない場合、レポートビューモードではリストビューコントロールに情報が表示されません。 ただし、表示モードを切り替える唯一の方法は、プログラムでリストビューコントロールの VIEW-MODE
属性値を明示的に変更することです。 したがって、アプリケーションで複数の表示モードをサポートする場合、表示モードを切り替えるメカニズム(コンテキストメニューなど)を用意する必要があります。 実際にはこのような場合、アプリケーションがレポートビューモードへの切り替えを認めないため、通常はユーザーが列のないリストビューコントロールをレポートビューモードで見ることはありません。
リストビュー項目のイメージは、「イメージリストコントロールの操作」で説明しているように、リストビューコントロールにイメージリストコントロールを作成および関連付け、(項目ごとに)必要なイメージを、インデックスおよび/またはイメージハンドルを使用してイメージリストから選択することによって定義できます。
サポートする表示モードに合わせて、イメージリストコントロールの [大きいイメージ(L)]スタイルおよび[小さいイメージ(S)]スタイルを設定する必要があることに注意してください。 アイコンビューモードでは、大きいアイコンを表示できる必要があります。一方、その他の表示モードでは、小さいアイコンを表示できる必要があります。
アイコンビューモードおよび小さいアイコンのビューモードでは、RECTANGLE-X
および/または RECTANGLE-Y
の各属性値を設定することによって、リストビュー項目を(再)配置できます。 項目の作成時に位置が明示的に指定されなかった場合、項目はデフォルトのグリッド間隔を持つ仮想グリッド上に配置されます。デフォルトのグリッド間隔は、リストビューコントロールの
SPACING-X
および SPACING-Y
の各属性値を設定することにより上書きできます。 ARRANGE
アクションを使用するといつでも、論理グリッドを基準にした連続する位置に項目を再整列させたり、最も近くの論理グリッド位置に項目の位置を合わせたりできます。
2 つのアイコンビューモードでは、リストビュー項目の位置は、コントロールのクライアントエリアに対する相対位置(他の表示モードの場合に使用)ではなく、ビュー座標として解釈されることに注意してください。 クライアント座標とは異なり、アイコンビューがスクロールされても、項目のビュー座標は変わりません。 ビュー座標とクライアント座標を変換するには、リストビューコントロールの OFFSET-X
属性および OFFSET-Y
属性を使用する必要があります。これらの属性は、クライアントエリアの基点をビュー座標で返します。
2 つのリストビューコントロールの STYLE
フラグを使用すると、プログラムによって明示的に指定された位置を上書きできることに注意してください。 1 つ目は[自動整列(a)]スタイルフラグで、このスタイルフラグを指定すると、項目が追加または移動されるたびに、仮想グリッド上で項目が自動的に再整列されます。
この場合、明示的に指定された位置は、整列したアイコンリスト内の項目の位置を間接的に指定するにすぎません。 2 つ目は[グリッドに合わせる(r)]スタイルフラグで、このスタイルフラグを指定すると、プログラムによって明示的に指定された項目の位置は、仮想グリッド上の最も近くの整列位置に調整されます。
[自動整列(a)]スタイルが設定されている場合、このスタイルは不要であることに注意してください。
ユーザーはリストビュー項目の位置をドラッグ & ドロップで変更できることに慣れているため、コントロールでもこの機能が自動的に提供されるものと期待している可能性があります。 ただし、この場合は当てはまりません。 アプリケーションでドラッグ & ドロップをサポートする場合は、明示的に処理する必要があります。これについては、次のセクションで説明します。
リストビューモードでは、すでに説明したように、項目は常に列内に表示され、再配置できません。 ただし、隣接する列の間隔は、SPACING
属性を使用して設定できます。
リストビューコントロールでは、表示モードを切り替えるときに項目の位置が記憶されないことに注意してください。 例えば、アイコンビューモードの 1 つから別のモードに切り替え、再度アイコンビューモードに戻ると、アイコンは常に整列しています。 この動作は、以下の例のように、項目の位置を保存およびリストアするプログラムコードを明示的に記述することにより回避できます。
DEFINE SUBROUTINE SAVE-ITEM-POSITIONS #ITEM := #CONTROL.FIRST-CHILD REPEAT WHILE #ITEM <> NULL-HANDLE IF #ITEM.TYPE = LISTVIEWITEM #ITEM.CLIENT-KEY := 'RECTANGLE-X' #ITEM.CLIENT-VALUE := #ITEM.RECTANGLE-X #ITEM.CLIENT-KEY := 'RECTANGLE-Y' #ITEM.CLIENT-VALUE := #ITEM.RECTANGLE-Y END-IF #ITEM := #ITEM.SUCCESSOR END-REPEAT END-SUBROUTINE * DEFINE SUBROUTINE RESTORE-ITEM-POSITIONS #ITEM := #CONTROL.FIRST-CHILD REPEAT WHILE #ITEM <> NULL-HANDLE IF #ITEM.TYPE = LISTVIEWITEM #ITEM.CLIENT-KEY := 'RECTANGLE-X' IF #ITEM.CLIENT-VALUE <> ' ' #ITEM.RECTANGLE-X := VAL(#ITEM.CLIENT-VALUE) END-IF #ITEM.CLIENT-KEY := 'RECTANGLE-Y' IF #ITEM.CLIENT-VALUE <> ' ' #ITEM.RECTANGLE-Y := VAL(#ITEM.CLIENT-VALUE) END-IF END-IF #ITEM := #ITEM.SUCCESSOR END-REPEAT END-SUBROUTINE * DEFINE SUBROUTINE SWITCH-VIEW-MODE IF #VIEW-MODE <> #CONTROL.VIEW-MODE IF #CONTROL.VIEW-MODE = VM-ICON OR #CONTROL.VIEW-MODE = VM-SMALLICON PERFORM SAVE-ITEM-POSITIONS END-IF #CONTROL.VIEW-MODE := #VIEW-MODE IF #VIEW-MODE = VM-ICON OR #VIEW-MODE = VM-SMALLICON PERFORM RESTORE-ITEM-POSITIONS END-IF END-IF END-SUBROUTINE
ローカルデータの定義は以下のように想定されています。
01 #CONTROL HANDLE OF GUI 01 #ITEM HANDLE OF GUI 01 #VIEW-MODE (I4)
実際の表示モードの切り替えは、#VIEW-MODE
を必要な表示モード(ローカルデータエリア NGULKEY1 で定義されている VM-*
定数)に、#CONTROL
をリストビューコントロールのハンドルに設定し、SWITCH-VIEW-MODE
サブルーチンを呼び出すことにより、実行できます。
項目を選択するには、項目をクリックするか(Ctrl キーを押したままクリックすると拡張選択を実行可能)、またはリストビューコントロール内をクリックし、マウスの第 1 ボタンを押したままドラッグして選択リージョンを指定します。 後者の手法は Marquee 選択と呼ばれ、コントロールの[Marquee
選択(m)]STYLE
フラグが設定されている場合にのみ使用できます(デフォルトで設定)。 コントロールの[ホットトラック選択(t)]スタイルフラグが設定されている場合、項目を選択するのにクリックは必要ないことに注意してください。 マウスカーソルを項目の上にしばらく置いておくだけで選択されます。
また、プログラムで項目の SELECTED
属性を設定したりクリアしたりすることにより、項目を選択したり選択解除したりできます。
どちらの場合でも、コントロールの MULTI-SELECTION
属性が TRUE
に設定されている場合は、拡張選択を実行できます。 拡張選択とは、既存の選択をクリアすることなく、選択項目を追加したり、選択済みの項目を選択解除したりするプロセスのことです。これにより、複数の項目を選択できます(まったく選択しないことも可能)。
単一選択リストビューの場合、新しい項目を選択するとそれまで選択されていた項目が暗黙的に選択解除されるという方法のみを使用できます。 この場合、Marquee 選択も使用できません。
最初に(または唯一)選択された項目は(存在する場合)、リストビューコントロールの SELECTED-SUCCESSOR
属性をクエリすると確認できます。何も項目が選択されていない場合は、NULL-HANDLE
が返されます。 次に選択されている項目は(存在する場合)、選択している項目の SELECTED-SUCCESSOR
属性をクエリすると確認できます。 アプリケーションでこの手法を繰り返し使用すると、以下の「ドロップアンドドラッグ」で説明されているように、選択されているすべての項目を列挙できます。
項目が選択または選択解除されるたびに、リストビューコントロールの CLICK
イベントが発生します(抑制されていない場合)。このとき、対応する項目のハンドルがコントロールの ITEM
属性に設定されます。 ごく短時間に連続して多数の項目が選択される場合があるため(Marquee 選択など)、このイベントには時間のかかる処理を記述しないようにします。 例えば、CLICK
イベントハンドラでは単に論理変数を TRUE
に設定してさらに処理を実行する必要があることを指定するのみにとどめ、実際の処理はダイアログの IDLE
イベントハンドラでこのフラグの設定に応じて実行するようにします。 処理が終了したら、このフラグをクリアすることを忘れないようにしてください。
リストビューコントロールの[チェックボックス(c)]スタイルフラグが設定されている場合、各項目と一緒にチェックボックスが表示されます。 項目の CHECKED
属性は、プログラムで項目のチェックステータスを取得および設定するために使用できます。 チェックされている最初の項目は(存在する場合)、リストビューコントロールの CHECKED-SUCCESSOR
属性をクエリすると確認できます。また、チェックされている項目のこの属性をクエリすると、次にチェックされている項目のハンドルが返されます(存在する場合)。これにより、以下に示す、チェックされている項目数をカウントする簡単な例のように、チェックされているすべての項目を列挙できます。
RESET #COUNT #ITEM := #LV-1.CHECKED-SUCCESSOR REPEAT WHILE #ITEM <> NULL-HANDLE ADD 1 TO #COUNT #ITEM := #ITEM.CHECKED-SUCCESSOR END-REPEAT
ローカルデータの定義は以下のように想定されています。
01 #LV-1 HANDLE OF LISTVIEW 01 #ITEM HANDLE OF LISTVIEWITEM 01 #COUNT (I4)
項目が選択または選択解除されるたびに、SUPPRESS-CHECK-EVENT
属性で抑制されていなければ、リストビューコントロールの CHECK
イベントが発生します。このとき、対応する項目のハンドルがコントロールの ITEM
属性に設定されます。
項目をダブルクリックすると、リストビューコントロールの ACTIVATE
イベントが発生します(抑制されていない場合)。 このイベントをアプリケーションで制御する場合、通常は選択された各コントロール上でデフォルトのアクションを実行します。 デフォルトのアクションはユーザー定義で、項目ごとに異なる処理を設定できます。 例えば、テキストファイルを表す項目を有効化すると、エディタ上でそのファイルが開かれます。一方、オーディオファイルを表す項目を有効化すると、そのファイルが再生されます。
1 つ以上の選択された項目に対し、デフォルトでない他のアクションも使用できることに注意してください。ただし、これらのアクションは通常、他のメカニズムによってアクセスされます。 例えば、これらはアプリケーションが表示するコンテキストメニューに(通常、デフォルトのアクションと一緒に)リストされます。
複数選択が可能な場合に(上記参照)Ctrl キーを押したまま項目をダブルクリックすると、ACTIVATE
イベントが発生する前にその項目の選択状態が切り替えられます。
コントロールの[下線ホット(u)]または[下線コールド(U)]の各 STYLE
フラグのどちらかが設定されている場合、項目を 1 回クリックするだけでその項目が有効化されます。
また、ACTIVATE
イベントは、キーボードを使用してトリガすることもできます。 このためには、次の 2 つの方法のいずれかを使用します。
リストビューコントロールの ACCELERATOR
属性に定義されているキーまたはキーの組み合わせを押す。 現時点で、コントロールにフォーカスがなくてもかまいません。
リストビューコントロールにフォーカスがある場合、Enter キーを押す。 この方法は、ダイアログにデフォルトのプッシュボタンが含まれておらず、プッシュボタンに[OK ボタン (O)]の STYLE
フラグが設定されていない場合にのみ機能します。
どちらの場合も、項目が選択されていないと ACTIVATE
イベントは発生しません。
リストビューの各列(リストビューコントロールがレポートビューモードのときに表示)は、リストビュー項目ごとに(最大)1 つの項目データを持ちます。このデータは(存在する場合)、そのリストビュー項目の該当する列に表示されます。 前述のとおり、このデータをサブ項目データと呼びます。
適切にソートできるようにするために(次のセクションを参照)サブ項目データを英数字にする必要はありません。デフォルトは英数字ですが、列の FORMAT
属性でサポートされている定義済みのタイプを使用できます。 また、列の EDIT-MASK
属性を設定することによって、列に編集マスクを適用できます。 ユーザーに表示されるレポートビュー列の値は、関連付けられている編集マスクが適用された(存在する場合)、その列に対するサブ項目の英数字表記になります。 編集マスクが使用されている場合、サブ項目データと表示データ間の変換は、MOVE EDITED
ステートメントと互換性があります。 それ以外の場合、内部データと表示されたデータ間およびその逆の変換は、データを Natural スタックにコピーしたり、Natural スタックからコピーする場合に発生する変換と互換性があります(STACK TOP DATA
および INPUT
ステートメントを参照)。 例えば、数値は、現在の小数点文字を使用して(DC
パラメータで定義されたように)表示されます。必要に応じて、負の場合は先頭にマイナスを付けることができます。日付値は、DTFORM
パラメータで定義されたフォーマットで表示されます。論理値は、真の場合は "X" として、偽の場合は空白として表示されます。
リストビューコントロールの最初に定義された列(プライマリ列)には、常に項目のラベルが表示されるという、特別な意味があります。 したがって、最初の列に対する項目のサブ項目が変更されると、自動的に項目のラベルが変更されます。その逆もまた同様です。 その他のすべての列の場合、サブ項目データを更新する唯一の方法は
SET-SUBITEM-DATA
アクションを使用することです。 このアクションを呼び出す場合、サブ項目データは、列の FORMAT
属性値(デフォルトでは英数字)で指定されている内部データタイプと互換性のあるフォーマットで受け渡す必要があります。
例えば、以下の例のように、リストビューコントロールに列を追加するとします。
PROCESS GUI ACTION ADD WITH PARAMETERS HANDLE-VARIABLE = #LVCOL-DATE TYPE = LISTVIEWCOLUMN STRING = 'Date' PARENT = #LV-1 RECTANGLE-W = 83 STYLE = 'l' FORMAT = FT-DATE EDIT-MASK = 'YYYY/MM/DD' END-PARAMETERS GIVING *ERROR
特定のリストビュー項目 #LVITEM-1
(例)に対する列のサブ項目データを、以下のように現在日付に設定します。
PROCESS GUI ACTION SET-SUBITEM-DATA WITH #LVITEM-1 #LVCOL-DATE *DATX GIVING *ERROR
Natural の時刻値は自動的に日付値に変換されるため、*DATX
の代わりに *TIMX
も使用できることに注意してください。 どちらの場合も、YYYY/MM/DD フォーマットの現在日付として値が表示されます。 プライマリ列の場合、表示される文字列は項目のラベルです。つまり、リストビューコントロールがレポートビューモードでなくても、その変更を見ることができます。
データは表示フォーマットでは渡せないことにも注意してください。 例えば、以下の例は動作しません。
PROCESS GUI ACTION SET-SUBITEM-DATA WITH #LVITEM-1 #LVCOL-DATE '2004/11/03' GIVING *ERROR /* Does NOT work!
ただし、列がプライマリ列の場合は、項目のラベルを設定することにより、間接的に列データを更新できます。 次に例を示します。
#LVITEM-1.STRING := '2004/11/03'
サブ項目データの取得は、SET-SUBITEM-DATA
アクションと同じパラメータを受け渡す GET-SUBITEM-DATA
アクションを呼び出すことによって実現できます。 次に例を示します。
PROCESS GUI ACTION GET-SUBITEM-DATA WITH #LVITEM-1 #LVCOL-DATE #DATE GIVING *ERROR
#DATE
は、以下のように定義されています。
01 #DATE (D)
取得するサブ項目データがない場合があるという事実に注意してください。 例えば、サブ項目データがまだ作成されていない、または削除されている場合(下記参照)があります。 ただし、Natural では、空値はサポートされていません。 したがって、デフォルトでは、空値が返されると
Natural によって受け取りフィールドはリセットされます(RESET
ステートメントを参照) ただし、リストビュー列にデフォルト値が設定されている場合、このデフォルト値が代わりに返されます。 列のデフォルト値を設定するには、リストボックス項目のハンドルの代わりに NULL-HANDLE
を指定して SET-SUBITEM-DATA
を呼び出します。 例えば、正数のみを使用できる数値列 #LVCOL-NUM
に対し、以下のようにデフォルト値を -1 に設定しようとする場合があります。
#NUM := -1 PROCESS GUI ACTION SET-SUBITEM-DATA WITH NULL-HANDLE #LVCOL-NUM #NUM GIVING *ERROR
#NUM
は、任意の符号付き数値フォーマット(I2 など)です。
デフォルト値を使用すると、プログラムで使用できない値を指定できます。 必要に応じて、デフォルト値に明示的に値が入力されないように、プログラムを変更する必要があります。
SET-SUBITEM-DATA
および GET-SUBITEM-DATA
の両アクションでは、単一のステートメントで(特定の項目に対する)複数列のサブ項目データをそれぞれ設定または取得できます。 次に例を示します。
PROCESS GUI ACTION SET-SUBITEM-DATA WITH #LVITEM-1 #LVCOL-NUM #NUM #LVCOL-DATE #DATE GIVING *ERROR
つまり、オペランドのペア [列ハンドル, 受け取りフィールド] を複数指定できます。
サブ項目データを削除するには、DELETE-SUBITEM-DATA
アクションを使用します(これにより、データが設定されていなかったかのように内部的に空値が格納されます)。 次に例を示します。
PROCESS GUI ACTION DELETE-SUBITEM-DATA WITH #LVITEM-1 #LVCOL-DATE GIVING *ERROR
ここでもまた、複数の列ハンドルを指定することにより、特定の項目に対する複数のサブ項目を単一のステートメントで削除できます。
PROCESS GUI ACTION DELETE-SUBITEM-DATA WITH #LVITEM-1 #LVCOL-DATE #LVCOL-NUM GIVING *ERROR
リストビュー項目が削除されると、その項目に関連付けられていたサブ項目データ(存在する場合)も一緒に削除されます。 リストビュー列を残したままリストビューコントロール内のすべての項目を削除する場合、CLEAR
アクションを使用します。
PROCESS GUI ACTION CLEAR WITH #LV-1 GIVING *ERROR
#LV-1
は、リストビューコントロールのハンドルです。
リストビュー列のサブ項目データのソートは、ユーザーが列ヘッダーをクリックするか(リストビューコントロールの[ヘッダーなし(x)]および[ソートヘッダーなし(y)]の各 STYLE
フラグが設定されていない場合)、またはプログラムで SORT-ITEMS
アクションを呼び出すことによって実現できます。 SORT-ITEMS アクションを使用する場合、リストビュー列ではなくリストビューコントロール自体のハンドルを渡すことによって、有効な列がない場合でも項目がソートされます。 詳細については、SORT-ITEMS
アクションのドキュメントを参照してください。
リストビューコントロールの列ヘッダー内の列をクリックすることによるソートは通常、Natural によって暗黙的に実行されます。 ただし、ソートが実行される前に、Natural によってそのリストビュー列の CLICK
イベントが起動されます(抑制されていない場合)。 このイベントから復帰すると、列がすでに必要な方向にソートされているかどうかが Natural によってチェックされます。すでにソート済みの場合、これ以上アクションは実行されません。 つまり、ソート方向のルールに従っている限り(つまり、列が昇順でソートされている場合に降順を指定する、またはその逆)、Natural
の代わりにアプリケーションでソートを実行できます。 ソートオプション(大文字と小文字の区別など)をダイナミックに指定する必要がある場合や、ソート後にアプリケーション固有のコードを実行する必要がある場合、これは便利です。 明示的にソートを実行する、列の
CLICK
イベントハンドラ例を以下に示します。
#CONTROL := *CONTROL T1. SETTIME IF #CONTROL.SORTED AND NOT #CONTROL.DESCENDING PROCESS GUI ACTION SORT-ITEMS WITH #CONTROL TRUE GIVING *ERROR ELSE PROCESS GUI ACTION SORT-ITEMS WITH #CONTROL GIVING *ERROR END-IF COMPRESS 'Sort took' *TIMD(T1.) 'tenths of a second' INTO #DLG$WINDOW.STATUS-TEXT
ただし、ほとんどの場合は、Natural の暗黙的なソートを使用すれば十分です。
英数字データの場合、ソート列の[大文字/小文字の区別なし(i)]および[単語比較(w)]の各 STYLE
フラグによって、値を比較する方法が変わります。 ソートを明示的に実行する場合、対応するオプションパラメータを SORT-ITEMS
アクションに指定すると、これらのデフォルト値は上書きされます。 これらのオプションの詳細については、このアクションのドキュメントを参照してください。
欠損値("空値")は、低く評価されます。 つまり、この値は、列を降順でソートすると列の下部に、昇順でソートすると列の上部に表示されます。 さらに、2 つの列エントリが同じ場合は、該当する 2 つの項目のそれまでの相対位置が保持されます。
リストビューコントロールがアイコンビューモードの 1 つを使用している場合、項目をソートすると項目が再整列されることに注意してください。 したがって、アイコンビューモードのどちらかで明示的に項目の位置を指定している場合、ソートコマンドを無効にすることをお勧めします。
また、リストビューコントロールも SORTED
属性を持っています。これにより、新しい項目は、項目リストの最後に追加されるのではなく、コントロールの DESCENDING
属性の値に応じて昇順または降順のソート位置に挿入されます。 この機能を期待どおりに動作させるには、あらかじめ適切な方向で項目がソートされている必要があります。 例えば、項目のラベル(つまり、項目の STRING
属性)が変更された場合、必要に応じて、ソートされた順序をリストが維持していることをアプリケーション自体で確認する必要があります。 これを実行する方法の例については、次のセクションで紹介します。
SORTED
属性は、アイコンビューモードのどちらかで表示されている項目の位置には影響しないことに注意してください。 ただし、SORT-ITEMS
アクションを使用してソートを明示的に実行すると、ソートされた順序で項目が再整列されます。 ソートを回避できず、かつ、アイコンビューモードで明示的に項目の位置を指定している場合、ソート前にアイコンの位置を明示的に保存しておき、ソート後に元の位置に戻す必要があります。
次に例を示します。
PERFORM SAVE-ITEM-POSITIONS PROCESS GUI ACTION SORT-ITEMS WITH #CONTROL GIVING *ERROR IF #CONTROL.VIEW-MODE = VM-ICON OR #CONTROL.VIEW-MODE = VM-SMALLICON PERFORM RESTORE-ITEM-POSITIONS END-IF
#CONTROL
はリストビューコントロールのハンドルです。また、項目の位置を保存およびリストアするために前述の例で定義したサブルーチンが使用されています。 アイコンが(元の位置で)再描画されるため、画面がちらつく場合があることに注意してください。 したがって、可能であれば、アイコンビューモードの
1 つでリストビューコントロールを使用している間は、ソートが実行されないようにしてください。 これを実行する方法の例については、以下のラベル編集についてのセクションを参照してください。
リストビューコントロールのラベル編集プロセスは、ツリービューコントロールと同じです。 このため、この内容の詳細については、「ツリービューおよびリストビューコントロールでのラベル編集」を参照してください。
SORTED
属性が設定されている場合でも、ラベル編集の操作が完了した後は、項目の順序付けが自動的に再実行されることはありません。 これが必要な場合は、以下の例に従って操作してください。 まず、後で使用する変数をいくつか定義します。
01 #SORTOBJ HANDLE OF GUI 01 #SORT (L) 01 #AUTO-ARRANGE (I4)
また、リストビューコントロールの名前は #LV-1
であるとします。
リストビューコントロールの AFTER-EDIT
イベントでは、(まだ完了していない)編集プロセスに影響があるため、非同期で順序付けを再実行できません。 代わりに、#SORT
フラグを設定し、編集プロセスの完了後に順序付けを再実行するよう指定します。
#SORT := TRUE
項目の順序付けの再実行を実行するかどうかを判断するために、項目がすでにソートされているかどうかを確認する必要があります。 これは、リストビューに列がある場合はプライマリ列の SORTED
属性と DESCENDING
属性、および列がない場合はリストビューコントロール自体のこれらの属性をクエリすることによって実行できます。 適切なオブジェクトハンドルをダイアログの AFTER-OPEN
イベントで設定します。
IF #LV-1.COLUMN-COUNT <> 0 #SORTOBJ := #LV-1.FIRST-CHILD ELSE #SORTOBJ := #LV-1 END-IF * EXAMINE #LV-1.STYLE FOR 'a' GIVING NUMBER #AUTO-ARRANGE IF #LV-1.SORTED AND #AUTO-ARRANGE <> 0 AND (#LV-1.VIEW-MODE = VM-ICON OR #LV-1.VIEW-MODE = VM-SMALLICON) #SORT := TRUE END-IF
また、リストビューコントロールの[自動整列(a)]STYLE
フラグの設定を取得します。 このフラグが設定されている場合、アイコンの位置を修正する必要がないため、コントロールがアイコンビューモードであっても項目をソートできます。 さらに、コントロールがソートされている場合、まず項目の順序付けを再実行するように指定します。
これは、コントロールの SORTED
フラグが設定されている場合、アイコンビューモードで項目を挿入しても項目の位置が更新されないという(前述の)事実に基づいています。 したがって、この場合、アプリケーションで初期ソート自体を実行する必要があります。
項目の順序付け再実行の実際の作業は、ダイアログの IDLE
イベントで非同期的に行われます。
IF #SORT IF #AUTO-ARRANGE <> 0 OR (#LV-1.VIEW-MODE <> VM-ICON AND #LV-1.VIEW-MODE <> VM-SMALLICON) IF #SORTOBJ.SORTED PROCESS GUI ACTION SORT-ITEMS WITH #SORTOBJ #SORTOBJ.DESCENDING GIVING *ERROR END-IF RESET #SORT END-IF END-IF
アイコンビューモードのリストビューコントロールに対しては、自動整列が設定されている場合にのみソートが実行されることに注意してください。 自動整列が設定されていない場合、#SORT
フラグはリセットされず、コントロールがアイコンビューモード以外のモードに切り替えられて最初の IDLE
イベントが発生するまで、順序付けの再実行は保留されます。
コントロールで単一のコンテキストメニューを使用する場合、表示するコンテキストメニューのハンドルをコントロールの CONTEXT-MENU
属性に設定してそのままにしておきます。 ただし、特定のコントロールで複数のコンテキストメニューを表示する必要があることがよくありますが、このアプローチでは柔軟性が足りません。
上記の問題に対応するために、CONTEXT-MENU
イベントが導入されました(上記の同じ名前の属性と混同しないように注意してください)。 このイベント(無効にされていない場合)は、CONTEXT-MENU
属性が評価される直前にターゲットのコントロールで発生します。これによって、アプリケーションは最初に、適切なコンテキストメニューのハンドルにこの属性をダイナミックに設定できます。
例として、ダイアログエディタで 2 つのコンテキストメニューを定義したとします。1 つは項目に関連するコマンドが組み込まれている #CTXMENU-ITEMS
で、もう 1 つは汎用コマンド(リストビューコントロールの表示モードの切り替えなど)が組み込まれている #CTXMENU-DEFAULT
です。 この場合、次の CONTEXT-MENU
イベントを使用することができます。
#CONTROL := *CONTROL IF #CONTROL.SELECTED-SUCCESSOR <> NULL-HANDLE #CONTROL.CONTEXT-MENU:= #CTXMENU-ITEMS ELSE #CONTROL.CONTEXT-MENU := #CTXMENU-DEFAULT END-IF
ローカルデータの定義は以下のように想定されています。
01 #CONTROL HANDLE OF GUI
この例では、項目が占めている位置をユーザーが右クリックすると、コンテキストメニュー #CTXMENU-ITEMS
が表示されます。それ以外の場合は、#CTXMENU-DEFAULT
が表示されます。
この手法をさらに活用して、選択された項目のタイプに固有のコンテキストメニューを表示することもできます。
ドラッグ & ドロップは、他のウィンドウとのデータ転送だけでなく、リストビューコントロール内の項目を再配置するために使用できます。 この 2 つのケースに特に違いはありません。 再配置の場合、ドラッグを開始したリストビューコントロール内でドロップ操作を行うという点が特殊なだけです。
ドラッグ & ドロップをサポートするための基本的な手法については、「クリップボードおよびドラッグ & ドロップの使用」に記載されています。 特に、コントロール内の項目を再配置をするだけの場合であっても、ドラッグ & ドロップを開始するよう Natural に通知するために、ドラッグ & ドロップクリップボードにデータを転送する必要があることに注意してください。 次に、リストビューコントロール固有の注意点があります。項目を再配置するとリストビューコントロールの基点が変更され、リストビューコントロールの表示エリアの近い方の上隅の座標がデフォルトの基点の (0, 0) で始まらなくなる場合があります。
以下の例は、次の操作をサポートするためにリストビューコントロールでドラッグ & ドロップを使用するコードを示しています。
リストビュー項目を再配置する。
別のアプリケーション(ワードパッドなど)からリストビュー項目にテキストをドラッグ & ドロップしてラベルを変更する。
まず最初に、ドラッグ & ドロップモードをリストビューコントロールに適切に設定します。 ダイアログエディタの[リストビューコントロール属性]ウィンドウで、[ドラッグモード]選択ボックスを"[移動]"に、[ドロップモード]選択ボックスを"[コピー + 移動]"に設定します。 これにより、コントロールの DRAG-MODE
属性および DROP-MODE
属性がそれぞれ DM-MOVE
および DM-COPYMOVE
に、ダイアログの生成ソースコード内で設定されます。
次に、以下で使用される必要なローカルの変数を定義する必要があります。
01 #CONTROL HANDLE OF GUI 01 #DROP-ITEM HANDLE OF GUI 01 #ITEM HANDLE OF GUI 01 #SELECTED (L) 01 #AVAIL (L) 01 #X (I4) 01 #Y (I4) 01 #ORIG-X (I4) 01 #ORIG-Y (I4)
これを行った後、必要なイベントハンドラを記述することができます。 処理を開始する論理的な場所は、BEGIN-DRAG
イベントです。
#CONTROL := *CONTROL * PROCESS GUI ACTION INQ-CLICKPOSITION WITH #CONTROL #ORIG-X #ORIG-Y GIVING *ERROR * ADD #CONTROL.OFFSET-X TO #ORIG-X ADD #CONTROL.OFFSET-Y TO #ORIG-Y * PROCESS GUI ACTION SET-CLIPBOARD-DATA WITH 'DUMMYPRIVFMT' 0 GIVING *ERROR
このコードは以下の 3 つの部分で構成されています。
クリック位置をクライアント座標で取得する。
クリック位置をビュー座標に変換する(前述のとおり、リストビューウィンドウの基点は常に "(0, 0)" とは限りません)。
ダミーデータをドラッグ & ドロップクリップボードに転送する。転送しないと、Natural はドラッグ & ドロップ操作を開始しません。 使用するのはダミーデータのみであるため、他のアプリケーションで認識されないように、ここではプライベートクリップボードフォーマットを選択します。
次に、DRAG-ENTER
イベントのハンドラを用意します。
PROCESS GUI ACTION INQ-DRAG-DROP WITH 1x #CONTROL GIVING *ERROR * IF #CONTROL = *CONTROL IF #CONTROL.VIEW-MODE = VM-ICON OR #CONTROL.VIEW-MODE = VM-SMALLICON #CONTROL.SUPPRESS-DRAG-DROP-EVENT := NOT-SUPPRESSED ELSE #CONTROL.SUPPRESS-DRAG-DROP-EVENT := SUPPRESSED END-IF ELSE #CONTROL := *CONTROL PROCESS GUI ACTION INQ-FORMAT-AVAILABLE WITH CF-TEXT #AVAIL GIVING *ERROR IF #AVAIL #CONTROL.SUPPRESS-DRAG-DROP-EVENT := NOT-SUPPRESSED ELSE #CONTROL.SUPPRESS-DRAG-DROP-EVENT := SUPPRESSED END-IF END-IF
上記のコードは、以下の 3 つの部分で構成されています。
INQ-DRAG-DROP
アクションを呼び出し、ドラッグソースコントロールのハンドルを確認します。
ドラッグソースが現在のコントロールの場合、ドラッグソースとドロップターゲットは同じです。 つまり、これは項目の再配置のケースです。 項目の再配置はアイコンビューモードの 1 つでのみ実行可能なため、この場合は DRAG-DROP
イベントを抑制せずにドロップを実行可能にします。 それ以外の場合、このイベントを無効にしてドロップを禁止し、ドロップアンドドラッグカーソルが表示され"ない"ようにします。 この場合、ドラッグ & ドロップクリップボードにデータを何も転送しないことで、ドラッグがまったく発生しないようにすることもできたことに注意してください。 ただし、この例では示されていませんが、ソースコントロールがリストビューモードまたはレポートビューモードであっても、1
つ以上の項目をリストビューコントロールから別のウィンドウにドラッグする必要があることはよくあります。その場合、上記のコードを基本として使用できます。
ドラッグソースとドロップターゲットが異なる場合、別のウィンドウからのデータ転送を試みます。 この場合、必要なフォーマット CF-TEXT でデータを使用できるかどうかを確認し、使用可能な場合はドロップを可能にします。 そうでない場合、ドロップは禁止されます。
外部データをリストビューコントロール上でドラッグしているときにドロップの強調表示を行うには、DRAG-OVER
イベントハンドラを使用します。
PROCESS GUI ACTION INQ-DRAG-DROP WITH 1X #CONTROL 1X #X #Y GIVING *ERROR * IF #CONTROL <> *CONTROL #CONTROL := *CONTROL IF #CONTROL.SUPPRESS-DRAG-DROP-EVENT = NOT-SUPPRESSED PROCESS GUI ACTION INQ-ITEM-BY-POSITION WITH #CONTROL #X #Y #ITEM GIVING *ERROR IF #ITEM <> #DROP-ITEM IF #DROP-ITEM <> NULL-HANDLE #DROP-ITEM.SELECTED := #SELECTED END-IF #DROP-ITEM := #ITEM IF #DROP-ITEM <> NULL-HANDLE #SELECTED := #DROP-ITEM.SELECTED #DROP-ITEM.SELECTED := TRUE END-IF END-IF END-IF END-IF
上述したコードにより、次の処理が実行されます。
INQ-DRAG-DROP
アクションが呼び出され、ドラッグソースコントロールのハンドルおよび現在のドラッグの位置が判断されます。
ドラッグソースとドロップターゲットが同じ場合(項目の再配置)、ドロップの強調表示は不要なため、これ以上アクションは実行されません。 ドラッグソースとドロップターゲットが異なる場合、INQ-ITEM-BY-POSITION
アクションを使用して、現在のドロップ位置にあるリストビュー項目(存在する場合)を確認します。
現在のターゲット項目("ドロップ項目")を #DROP-ITEM
に保存します。 ドロップ項目が変わるたびに、まず新しいドロップ項目の現在の選択状態を #SELECTED
に保存します。その後、項目の SELECTED
属性を TRUE
に設定することにより、項目を選択してドロップの強調表示を行います。 また、元のドロップ項目(存在する場合)の選択状態は、前に保存した値に戻されます。
ドロップを実行できない場合(つまり、SUPPRESS-DRAG-DROP-EVENT
属性が SUPPRESSED
に設定されている場合)、ドロップの強調表示は適用されないことに注意してください。
実際のドロップを実行するために、DRAG-DROP
イベントハンドラが提供されています。
PROCESS GUI ACTION INQ-DRAG-DROP WITH 1x #CONTROL 1x #X #Y GIVING *ERROR * IF #CONTROL = *CONTROL ADD #CONTROL.OFFSET-X TO #X ADD #CONTROL.OFFSET-Y TO #Y SUBTRACT #ORIG-X FROM #X SUBTRACT #ORIG-Y FROM #Y #ITEM := #CONTROL.SELECTED-SUCCESSOR REPEAT WHILE #ITEM <> NULL-HANDLE ADD #X TO #ITEM.RECTANGLE-X ADD #Y TO #ITEM.RECTANGLE-Y #ITEM := #ITEM.SELECTED-SUCCESSOR END-REPEAT ELSE IF #DROP-ITEM <> NULL-HANDLE PROCESS GUI ACTION GET-CLIPBOARD-DATA WITH CF-TEXT #DROP-ITEM.STRING GIVING *ERROR #DROP-ITEM.SELECTED := #SELECTED RESET #DROP-ITEM END-IF END-IF
上述したコードにより、次の処理が実行されます。
INQ-DRAG-DROP
アクションが呼び出され、ドラッグソースコントロールのハンドルおよび現在のドラッグの位置が判断されます。
このイベントハンドラは、ドラッグソースとドロップターゲットが同じ場合(項目の再配置)と異なる場合(外部ソースからのドラッグ)を区別します。
項目の再配置の場合、ドロップ位置をビュー座標に変換して基点からの変位を "(x, y)" で取得します。その後、この変位を選択された各項目に適用して移動させます。
外部ソースからのドラッグの場合、クリップボードから取得したテキストデータを現在のドロップ項目(存在する場合)の STRING
属性に直接設定して、ラベルを更新します。 ドラッグ & ドロップクリップボードでテキストを使用できるかどうかの確認は、すでに DRAG-ENTER
イベントで実行して、使用できない場合はドロップおよび関連付けられている DRAG-DROP
イベントが禁止されているため、最初に実行する必要はありません。 ラベルの更新後は、ドロップ項目の選択状態を元(ドラッグ前の選択状態)に戻し、次のドラッグ操作(もしあれば)に備えて #DROP-ITEM
をリセットします。
最後に、ユーザーがドラッグ操作をキャンセルした場合、またはドロップを実行せずにリストビューコントロールの境界線の外に出た場合は、DRAG-LEAVE
イベントハンドラを使用します。
IF #DROP-ITEM <> NULL-HANDLE #DROP-ITEM.SELECTED := #SELECTED RESET #DROP-ITEM END-IF
上記のコードは、ドロップ項目の選択状態を元に戻して、ドロップの強調表示(行われている場合)を単にクリアしています。 他のイベントハンドラのロジックと合わせるために、#DROP-ITEM
をリセットして、ドロップ項目がないことを示します。 DRAG-LEAVE
イベントはドロップでは呼び出されないため、このコードは DRAG-DROP
イベントの最後に実行されるコードと基本的に同じコードになっています。 したがって、ドロップの強調表示のリセットはこの両方で行う必要があります。