バージョン 6.3.3
 —  プログラミングガイド  —

リストビューコントロールの操作

このドキュメントでは、次のトピックについて説明します。


はじめに

リストビューコントロールは、アイコンまたは列を基準にした形式でデータを表示するために使用できます。 リストビューコントロールは、非常に強力なコントロールです。 ただし、タブ形式でデータを表示し、任意の列の値をその場所で直接編集する場合は、リストビューコントロールではなくテーブルコントロールの使用を検討する必要があります。

Top of page

ビューモード

Natural のリストビューコントロールは、アイコン、小さいアイコン、リスト、レポートの 4 つのビューモードでデータを表示できます。

アイコンビューモードでは、データは大きいアイコン形式で表示されます。

小さいアイコンのビューモードでは、項目ラベルがアイコンと一緒に表示されます。 アイコンビューでは、項目を任意の位置に表示できます。

リストビューモードでは、小さいアイコンのビューモードと同じように項目が表示されますが、自由に配置することはできません。 代わりに、整列して表示されます。

レポートビューモードでは、項目ごとに 1 行が割り当てられ、項目に関連付けられている他のデータが同じ行の個別の列に表示されます。 通常は、以下の例のように、列ヘッダーも表示されます(リストビューコントロールの[ヘッダーなし(x)]STYLE フラグを設定することにより、任意で非表示にできます)。

レポートビューでは、列の仕切りをドラッグすることにより、列のサイズを変更できます。 代わりに、列ヘッダー内の列の右側の区切りをダブルクリックすると、列内で最も長いテキストに合わせて列の幅が調整されます。

上記の例は、リストビューコントロール自体、4 個のリストビュー項目、および 6 個のリストビュー列の合計 11 個のダイアログエレメントから構成されていることに注意してください。 リストビュー項目とリストビュー列の両方とも、PARENT としてリストビューコントロールを持ち、同じ SUCCESSOR チェーンに格納されます。 リストビュー項目とリストビュー列を混在させることは可能ですが、構成とパフォーマンスの両方の観点から、すべてのリストビュー列がすべてのリストビュー項目の前に来るように設定することをお勧めします。 例えば、空でないリストビューコントロールに最後の列として新しい列をダイナミックに挿入する場合、列の SUCCESSOR 属性に何も指定しないのではなく、最初のリストビュー項目のハンドルを明示的に設定するか、または新しい列がチェーンの最後、つまりすべてのリストビュー項目の後に配置される NULL-HANDLE を設定します。

リストビューコントロールに対して作成される最初のリストビュー列には特別な意味があります。この列を(ここでは)プライマリ列と呼びます。 プライマリ列には常にリストビュー項目ラベル(つまり、STRING 属性値)が表示されます。 その他の列には、サブ項目データと呼ばれるものが表示されます。 例えば、"Currency サブ項目" は "Currency" 列に格納されているデータを参照します(上記の例を参照)。 列内の特定の値を参照するには、さらに厳密に指定する必要があります。 例えば、上記の "1277.18" という値は、"Second item" 項目の "Currency サブ項目" として参照されています。 サブ項目は、Natural のダイアログエレメントのタイプではありません。これについては、後で説明します。

リストビュー列が作成されていない場合、レポートビューモードではリストビューコントロールに情報が表示されません。 ただし、表示モードを切り替える唯一の方法は、プログラムでリストビューコントロールの VIEW-MODE 属性値を明示的に変更することです。 したがって、アプリケーションで複数の表示モードをサポートする場合、表示モードを切り替えるメカニズム(コンテキストメニューなど)を用意する必要があります。 実際にはこのような場合、アプリケーションがレポートビューモードへの切り替えを認めないため、通常はユーザーが列のないリストビューコントロールをレポートビューモードで見ることはありません。

Top of page

項目イメージの設定

リストビュー項目のイメージは、「イメージリストコントロールの操作」で説明しているように、リストビューコントロールにイメージリストコントロールを作成および関連付け、(項目ごとに)必要なイメージを、インデックスおよび/またはイメージハンドルを使用してイメージリストから選択することによって定義できます。

サポートする表示モードに合わせて、イメージリストコントロールの [大きいイメージ(L)]スタイルおよび[小さいイメージ(S)]スタイルを設定する必要があることに注意してください。 アイコンビューモードでは、大きいアイコンを表示できる必要があります。一方、その他の表示モードでは、小さいアイコンを表示できる必要があります。

Top of page

項目の配置

アイコンビューモードおよび小さいアイコンのビューモードでは、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 サブルーチンを呼び出すことにより、実行できます。

Top of page

項目の選択

項目を選択するには、項目をクリックするか(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 属性に設定されます。

Top of page

項目の有効化

項目をダブルクリックすると、リストビューコントロールの ACTIVATE イベントが発生します(抑制されていない場合)。 このイベントをアプリケーションで制御する場合、通常は選択された各コントロール上でデフォルトのアクションを実行します。 デフォルトのアクションはユーザー定義で、項目ごとに異なる処理を設定できます。 例えば、テキストファイルを表す項目を有効化すると、エディタ上でそのファイルが開かれます。一方、オーディオファイルを表す項目を有効化すると、そのファイルが再生されます。 1 つ以上の選択された項目に対し、デフォルトでない他のアクションも使用できることに注意してください。ただし、これらのアクションは通常、他のメカニズムによってアクセスされます。 例えば、これらはアプリケーションが表示するコンテキストメニューに(通常、デフォルトのアクションと一緒に)リストされます。

複数選択が可能な場合に(上記参照)Ctrl キーを押したまま項目をダブルクリックすると、ACTIVATE イベントが発生する前にその項目の選択状態が切り替えられます。

コントロールの[下線ホット(u)]または[下線コールド(U)]の各 STYLE フラグのどちらかが設定されている場合、項目を 1 回クリックするだけでその項目が有効化されます。

また、ACTIVATE イベントは、キーボードを使用してトリガすることもできます。 このためには、次の 2 つの方法のいずれかを使用します。

  1. リストビューコントロールの ACCELERATOR 属性に定義されているキーまたはキーの組み合わせを押す。 現時点で、コントロールにフォーカスがなくてもかまいません。

  2. リストビューコントロールにフォーカスがある場合、Enter キーを押す。 この方法は、ダイアログにデフォルトのプッシュボタンが含まれておらず、プッシュボタンに[OK ボタン (O)]の STYLE フラグが設定されていない場合にのみ機能します。

どちらの場合も、項目が選択されていないと ACTIVATE イベントは発生しません。

Top of page

リストビュー列とサブ項目

リストビューの各列(リストビューコントロールがレポートビューモードのときに表示)は、リストビュー項目ごとに(最大)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 は、リストビューコントロールのハンドルです。

Top of page

ソート

リストビュー列のサブ項目データのソートは、ユーザーが列ヘッダーをクリックするか(リストビューコントロールの[ヘッダーなし(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 つでリストビューコントロールを使用している間は、ソートが実行されないようにしてください。 これを実行する方法の例については、以下のラベル編集についてのセクションを参照してください。

Top of page

ラベル編集

リストビューコントロールのラベル編集プロセスは、ツリービューコントロールと同じです。 このため、この内容の詳細については、「ツリービューおよびリストビューコントロールでのラベル編集」を参照してください。

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 イベントが発生するまで、順序付けの再実行は保留されます。

Top of page

マルチプルコンテキストメニュー

コントロールで単一のコンテキストメニューを使用する場合、表示するコンテキストメニューのハンドルをコントロールの 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 が表示されます。

この手法をさらに活用して、選択された項目のタイプに固有のコンテキストメニューを表示することもできます。

Top of page

ドラッグ & ドロップ

ドラッグ & ドロップは、他のウィンドウとのデータ転送だけでなく、リストビューコントロール内の項目を再配置するために使用できます。 この 2 つのケースに特に違いはありません。 再配置の場合、ドラッグを開始したリストビューコントロール内でドロップ操作を行うという点が特殊なだけです。

ドラッグ & ドロップをサポートするための基本的な手法については、「クリップボードおよびドラッグ & ドロップの使用」に記載されています。 特に、コントロール内の項目を再配置をするだけの場合であっても、ドラッグ & ドロップを開始するよう Natural に通知するために、ドラッグ & ドロップクリップボードにデータを転送する必要があることに注意してください。 次に、リストビューコントロール固有の注意点があります。項目を再配置するとリストビューコントロールの基点が変更され、リストビューコントロールの表示エリアの近い方の上隅の座標がデフォルトの基点の (0, 0) で始まらなくなる場合があります。

以下の例は、次の操作をサポートするためにリストビューコントロールでドラッグ & ドロップを使用するコードを示しています。

  1. リストビュー項目を再配置する。

  2. 別のアプリケーション(ワードパッドなど)からリストビュー項目にテキストをドラッグ & ドロップしてラベルを変更する。

まず最初に、ドラッグ & ドロップモードをリストビューコントロールに適切に設定します。 ダイアログエディタの[リストビューコントロール属性]ウィンドウで、[ドラッグモード]選択ボックスを"[移動]"に、[ドロップモード]選択ボックスを"[コピー + 移動]"に設定します。 これにより、コントロールの 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 つの部分で構成されています。

  1. クリック位置をクライアント座標で取得する。

  2. クリック位置をビュー座標に変換する(前述のとおり、リストビューウィンドウの基点は常に "(0, 0)" とは限りません)。

  3. ダミーデータをドラッグ & ドロップクリップボードに転送する。転送しないと、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 つの部分で構成されています。

  1. INQ-DRAG-DROP アクションを呼び出し、ドラッグソースコントロールのハンドルを確認します。

  2. ドラッグソースが現在のコントロールの場合、ドラッグソースとドロップターゲットは同じです。 つまり、これは項目の再配置のケースです。 項目の再配置はアイコンビューモードの 1 つでのみ実行可能なため、この場合は DRAG-DROP イベントを抑制せずにドロップを実行可能にします。 それ以外の場合、このイベントを無効にしてドロップを禁止し、ドロップアンドドラッグカーソルが表示され"ない"ようにします。 この場合、ドラッグ & ドロップクリップボードにデータを何も転送しないことで、ドラッグがまったく発生しないようにすることもできたことに注意してください。 ただし、この例では示されていませんが、ソースコントロールがリストビューモードまたはレポートビューモードであっても、1 つ以上の項目をリストビューコントロールから別のウィンドウにドラッグする必要があることはよくあります。その場合、上記のコードを基本として使用できます。

  3. ドラッグソースとドロップターゲットが異なる場合、別のウィンドウからのデータ転送を試みます。 この場合、必要なフォーマット 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

上述したコードにより、次の処理が実行されます。

  1. INQ-DRAG-DROP アクションが呼び出され、ドラッグソースコントロールのハンドルおよび現在のドラッグの位置が判断されます。

  2. ドラッグソースとドロップターゲットが同じ場合(項目の再配置)、ドロップの強調表示は不要なため、これ以上アクションは実行されません。 ドラッグソースとドロップターゲットが異なる場合、INQ-ITEM-BY-POSITION アクションを使用して、現在のドロップ位置にあるリストビュー項目(存在する場合)を確認します。

  3. 現在のターゲット項目("ドロップ項目")を #DROP-ITEM に保存します。 ドロップ項目が変わるたびに、まず新しいドロップ項目の現在の選択状態を #SELECTED に保存します。その後、項目の SELECTED 属性を TRUE に設定することにより、項目を選択してドロップの強調表示を行います。 また、元のドロップ項目(存在する場合)の選択状態は、前に保存した値に戻されます。

  4. ドロップを実行できない場合(つまり、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

上述したコードにより、次の処理が実行されます。

  1. INQ-DRAG-DROP アクションが呼び出され、ドラッグソースコントロールのハンドルおよび現在のドラッグの位置が判断されます。

  2. このイベントハンドラは、ドラッグソースとドロップターゲットが同じ場合(項目の再配置)と異なる場合(外部ソースからのドラッグ)を区別します。

  3. 項目の再配置の場合、ドロップ位置をビュー座標に変換して基点からの変位を "(x, y)" で取得します。その後、この変位を選択された各項目に適用して移動させます。

  4. 外部ソースからのドラッグの場合、クリップボードから取得したテキストデータを現在のドロップ項目(存在する場合)の 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 イベントの最後に実行されるコードと基本的に同じコードになっています。 したがって、ドロップの強調表示のリセットはこの両方で行う必要があります。

Top of page