ツリービューを作成してみる ACT-1

ツリービューコントロール

今回はコモンコントロールの1つであるツリービューコントロールを作成してみることにしましょう。ツリービューは、階層的なリストを表示しているウィンドウのことです。エクスプローラーのウィンドウの左側にあるフォルダのツリーがまさに代表的なツリービューです。

ツリービューのそれぞれのアイテムは子アイテムを持つことができます。子アイテムを持つアイテムは、 + や - のボタンを持たせることができ、これを押すことで子アイテムのリストを開いたり閉じたりすることができます。

ツリービューの作成手順

ツリービューを作成する手順は、以下のようになります。

  1. InitCommonControls 関数を呼び出す。
  2. クラス名を "SysTreeView32" として CreateWindowEx 関数を呼び出し、ツリービューを作成する。
  3. アイコンイメージを表示する場合は、イメージリストを作成して TVM_SETIMAGELIST メッセージを送信する。
  4. TVINSERTSTRUCT 構造体に必要な情報を格納して、ツリービューに TVM_INSERTITEM メッセージを送信し、新しいアイテムを追加する。

コモンコントロールライブラリ初期化

まず最初に InitCommonControls 関数を呼び出してコモンコントロールライブラリを初期化します。これはすべてのコモンコントロールについて共通です。

コントロールの作成

ツリービューを作成するには、 CreateWindowEx 関数を呼び出すときにウィンドウクラス名を "SysTreeView32" とします。このとき、ウィンドウスタイルとして WS_CHILD (0x40000000) および WS_VISIBLE (0x10000000) を指定します。その他に、ツリービュー固有のスタイルを組み合わせて指定することができます。例えば、親アイテムの横に + や - のボタンを表示するには TVS_HASBUTTONS スタイル (0x0001) を、アイテムを線でつなぎたければ TVS_HASLINES スタイル (0x0002) をそれぞれ指定します。

イメージリストの設定

各アイテムの隣にアイコンイメージを表示させたい場合には、それらのイメージをあらかじめイメージリストとして作成しておく必要があります。

イメージリストの作成については、『イメージリストを使ってみる』の項を参照してください。

イメージリストの準備ができたら、 TVM_SETIMAGELIST メッセージを送信することによって、イメージリストをリストビューにセットします。

#define  TVM_SETIMAGELIST    0x1109

TVM_SETIMAGELIST
    wParam = iImage;
    lParam = himl;

iImage パラメータには、通常は 0 (TVSIL_NORMAL) を指定します。 himl パラメータには、設定するイメージリストのハンドルを指定しておきます。

このメッセージを送った後からでも、イメージリストに新しいイメージを追加していくことができます。

アイテムの追加

次はアイテムを追加していきます。アイテムを追加するには TVINSERTSTRUCT 構造体に情報を格納してから TVM_INSERTITEM メッセージを送信します。

TVINSERTSTRUCT 構造体は以下のように定義されています。

typedef struct tagTVINSERTSTRUCT {
    HTREEITEM  hParent;      // 親アイテムのハンドル
    HTREEITEM  hInsertAfter; // 挿入位置を表すアイテムのハンドル
    TVITEM     item;         // 追加するアイテムについての構造体
} TVINSERTSTRUCT, FAR *LPTVINSERTSTRUCT;

ツリービューに表示されるアイテムはすべて固有の識別値を持っており、この識別値はアイテムのハンドルと呼ばれます。 hParent メンバには、新しく追加するアイテムの親アイテムのハンドルを指定します。これによって、新しいアイテムがどのアイテムの子アイテムとして追加されるかが指定されます。アイテムをツリービューのルート(一番上の階層)に追加する場合には、 hParent メンバに 0xFFFF0000 (TVI_ROOT) または 0 (NULL) のいずれか指定します。

hInsertAfter メンバには、挿入位置を表すアイテムのハンドルを指定します。新しいアイテムは、ここで指定されたアイテムの次の位置に挿入されることになります。また、リスト中の最初の位置に挿入する場合には 0xFFFF0001 (TVI_FIRST) を、最後の位置に挿入する場合には 0xFFFF0002 (TVI_LAST) を、アルファベット順にソートする場合には 0xFFFF0003 (TVI_SORT) をそれぞれ指定することができます。

item メンバは TVITEM 構造体です。この構造体には、新しく追加するアイテムの情報を格納しておきます。この構造体は入れ子の状態で含まれていることに注意してください。 TVITEM 構造体は以下のように定義されています。

typedef struct tagTVITEM {
    UINT       mask;           // 有効メンバを示すフラグ
    HTREEITEM  hItem;          // アイテムのハンドル
    UINT       state;          // アイテムの状態・イメージ
    UINT       stateMask;      // state のフラグ
    LPTSTR     pszText;        // アイテムの文字列
    int        cchTextMax;     // アイテムの文字列の長さ
    int        iImage;         // イメージのインデックス(非選択時)
    int        iSelectedImage; // イメージのインデックス(選択時)
    int        cChildren;      // 子アイテムを持つかどうかのフラグ
    LPARAM     lParam;         // アイテムの持つ32ビット値
} TVITEM, *LPTVITEM;

mask メンバは、この構造体の有効メンバを示すフラグです。今回は文字列とイメージを設定するので、0x0001 (LVIF_TEXT) と 0x0002 (TVIF_IMAGE) と 0x0020 (TVIF_SELECTEDIMAGE) を合わせて指定しておきます。

iItem メンバには、新しいアイテムを追加する位置のインデックスを指定します。

iSubItem メンバには 0 を指定します。

pszText メンバには表示する文字列を入れた文字列変数のアドレスを指定します。

iImage メンバには表示するイメージのイメージリスト中におけるインデックスを指定します。選択時のイメージも設定する場合には iSelectedImage メンバにイメージのインデックスを指定します。

他のメンバはここでは関係ないので、どんな値が入っていても無視されます。(今回はとりあえず 0 にしておきます。)

TVINSERTSTRUCT 構造体に情報を入れたら、リストビューに TVM_INSERTITEM メッセージを送信します。

#define  TVM_INSERTITEM    0x1100

TVM_INSERTITEM
    wParam = 0;
    lParam = lpis;

lpis パラメータには、新しいアイテムの情報を格納した TVINSERTSTRUCT 構造体のアドレスを指定します。アイテムが追加されると、戻り値として作成されたアイテムのハンドルが返ります。失敗すると、戻り値として 0 (NULL) が返ります。

サンプルスクリプト

さて、実際にスクリプトを書いてみます。今回使用する画像は以下のものです。この画像のサイズは横 (16×4) ピクセル、縦 16 ピクセルで、背景に使われている色の各輝度は、赤 166 (0xA6)、緑 202(0xCA)、青240(0xF0) であるので、背景色を透過させるようにするには、 ImageList_AddMasked 関数のマスク色を指定する引数に 0x00F0CAA6 を指定します。

(treeicon.bmp)

ビットマップファイルのダウンロード

サンプルスクリプト main.as

    #include "llmod.as"

    #module ;###### ビットマップオブジェクト作成モジュール #########

    ;===============================================================
    ; 描画中ウィンドウのイメージからビットマップオブジェクト(DIB)作成
    ; CreateDIB  p1, p2, p3, p4
    ;     p1 : HSPウィンドウx座標
    ;     p2 : HSPウィンドウy座標
    ;     p3 : 幅
    ;     p4 : 高さ
    ;   stat : ビットマップのハンドルが返る
    ;===============================================================
    #deffunc CreateDIB int, int, int, int
    mref px, 0              ; HSPウィンドウx座標
    mref py, 1              ; HSPウィンドウy座標
    mref sx, 2              ; xサイズ
    mref sy, 3              ; yサイズ
    mref stt, 64            ; stat
    mref bmscr, 67          ; 描画中ウィンドウのBMSCR構造体

    ; DIBセクションオブジェクト作成
    pm = bmscr.4, sx, sy
    dllproc "CreateCompatibleBitmap", pm, 3, D_GDI@
    hbitmap = stat          ; ビットマップオブジェクトのハンドル

    ; メモリデバイスコンテキスト作成
    pm = bmscr.4
    dllproc "CreateCompatibleDC", pm, 1, D_GDI@
    hdcMemory = stat        ; メモリデバイスコンテキストのハンドル

    ; ビットマップをデバイスコンテキストに選択
    pm = hdcMemory, hbitmap
    dllproc "SelectObject", pm, 2, D_GDI@

    ; HSPウィンドウからビットマップにイメージをコピー
    pm = hdcMemory, 0, 0, sx, sy, bmscr.4, px, py, $CC0020
    dllproc "BitBlt", pm, 9, D_GDI@

    ; デバイスコンテキストを削除
    dllproc "DeleteDC", hdcMemory, 1, D_GDI@

    stt = hbitmap           ; ビットマップオブジェクトのハンドル
    return

    #global ;############# モジュール終わり ########################

    #module ;######### イメージリスト操作モジュール ################

    ;===============================================================
    ; イメージリストの作成
    ; CreateImageList  p1, p2, p3, p4
    ;     p1 : イメージの幅
    ;     p2 : イメージの高さ
    ;     p3 : イメージリストのタイプ
    ;     p4 : イメージの数
    ;   stat : イメージリストのハンドルが返る
    ;===============================================================
    #deffunc CreateImageList int, int, int, int
    mref sx, 0              ; イメージの幅
    mref sy, 1              ; イメージの高さ
    mref flags, 2           ; イメージリストのタイプ
    mref num, 3             ; イメージの数

    ; イメージリストの作成
    pm.0 = sx
    pm.1 = sy
    pm.2 = flags
    pm.3 = num
    pm.4 = 0
    dllproc "ImageList_Create", pm, 5, D_COMCTL@
    return

    ;===============================================================
    ; イメージリストに描画中ウィンドウのイメージ追加
    ; AddImageList  p1, p2, p3, p4, p5
    ;     p1 : イメージリストのハンドル
    ;     p2 : HSPウィンドウx座標
    ;     p3 : HSPウィンドウy座標
    ;     p4 : 幅
    ;     p5 : 高さ
    ;     p6 : マスクに用いる色
    ;   stat : イメージのインデックスが返る
    ;===============================================================
    #deffunc AddImageList int, int, int, int, int, int
    mref himl, 0            ; イメージリストのハンドル
    mref cx, 1              ; x座標
    mref cy, 2              ; y座標
    mref sx, 3              ; xサイズ
    mref sy, 4              ; yサイズ
    mref crmask, 5          ; マスク生成に用いる色
    mref stt, 64            ; stat

    ; DIBオブジェクトの作成
    CreateDIB  cx, cy, sx, sy
    hbitmap = stat          ; ビットマップハンドル

    ; ビットマップをイメージリストに追加
    pm.0 = himl
    pm.1 = hbitmap
    pm.2 = crmask
    dllproc "ImageList_AddMasked", pm, 3, D_COMCTL@
    index = stat            ; 最初のイメージのインデックス

    ; ビットマップオブジェクトを削除
    dllproc "DeleteObject", hbitmap, 1, D_GDI@

    stt = index             ; イメージのインデックスをstatに格納
    return

    ;===============================================================
    ; イメージリストの破棄
    ; DestroyImageList  p1
    ;     p1 : イメージリストのハンドル
    ;===============================================================
    #deffunc DestroyImageList int
    mref himl, 0            ; イメージリストのハンドル

    ; イメージリストを破棄
    pm.0 = himl
    dllproc "ImageList_Destroy", pm, 1, D_COMCTL@
    return

    #global ;############# モジュール終わり ########################

    #module ;########### ツリービュー操作モジュール ##################

    ;===============================================================
    ; 描画中ウィンドウにツリービュー作成
    ; CreateTreeView  p1, p2, p3, p4
    ;     p1 : x座標
    ;     p2 : y座標
    ;     p3 : 幅
    ;     p4 : 高さ
    ;   stat : ツリービューのハンドルが返る
    ;===============================================================
    #deffunc CreateTreeView int, int, int, int
    mref cx, 0              ; x
    mref cy, 1              ; y
    mref sx, 2              ; 幅
    mref sy, 3              ; 高さ
    mref stt, 64            ; stat

    ; コモンコントロールライブラリ初期化
    dllproc "InitCommonControls", pm, 0, D_COMCTL@

    ; ツリービュー作成
    pm = cx, cy, sx, sy     ; 座標、サイズ
    pm.4 = 0x50800007       ; WS_VISIBLE | WS_BORDER | WS_CHILD
                            ;  | TVS_HASBUTTONS | TVS_HASLINES
                            ;  | TVS_LINESATROOT
    pm.5 = 0, 0
    _makewnd pm, "SysTreeView32"
    stt = pm                ; ツリービューのハンドル
    return

    ;===============================================================
    ; ツリービューにイメージリストを設定
    ; SetTreeImageList  p1, p2
    ;     p1 : ツリービューのハンドル
    ;     p2 : イメージリストのハンドル
    ;===============================================================
    #deffunc SetTreeImageList  int, int
    mref hTree, 0           ; ツリービューのハンドル
    mref himl, 1            ; イメージリストのハンドル

    ; TVM_SETIMAGELIST メッセージ送信
    pm = hTree, 0x1109, 0, himl
    sendmsg pm
    return

    ;===============================================================
    ; ツリービューにアイテム追加
    ; AddTreeItem  p1, p2, p3, p4, p5
    ;     p1 : ツリービューのハンドル
    ;     p2 : 表示するテキスト
    ;     p3 : イメージのインデックス
    ;     p4 : 親アイテムのハンドル
    ;     p5 : 挿入位置のアイテムハンドルまたは以下の値
    ;          $FFFF0001 : リストの最初の位置
    ;          $FFFF0002 : リストの最後の位置
    ;          $FFFF0003 : リストをアルファベット順にソート
    ;===============================================================
    #deffunc AddTreeItem  int, str, int, int, int
    mref hTree, 0           ; ツリービューのハンドル
    mref setText, 33        ; 表示するテキスト
    mref iImage, 2          ; イメージのインデックス
    mref hParent, 3         ; 親アイテムのハンドル
    mref hIns, 4            ; 挿入位置のアイテムハンドル

    dim bufText, 256
    bufText = setText           ; いったん別の変数に移しておく

    ; TVINSERTSTRUCT 構造体
    dim tvins, 12
    tvins.0 = hParent           ; 親アイテムのハンドル
    tvins.1 = hIns              ; 挿入位置のアイテムハンドル
    tvins.2 = 0x0023            ; TVIF_TEXT | TVIF_IMAGE
                                ;  | TVIF_SELECTEDIMAGE
    getptr tvins.6, bufText     ; 文字列のアドレス
    tvins.8 = iImage            ; イメージインデックス(非選択時)
    tvins.9 = iImage            ; イメージインデックス(選択時)

    ; TVM_INSERTITEM メッセージ送信
    pm = hTree, 0x1100, 0
    getptr pm.3, tvins
    sendmsg pm

    return

    #global ;############# モジュール終わり ########################


    ; イメージリスト作成 (16×16×4, 24bit, マスクあり)
    CreateImageList 16, 16, 25, 4
    himl = stat                 ; イメージリストのハンドル

    ; イメージリスト作成用バッファウィンドウ
    buffer 2,,,0
    picload "treeicon.bmp"      ; ビットマップの読み込み

    ; イメージリストにイメージを追加
    AddImageList himl, 0, 0, 16*4, 16, 0x00F0CAA6

    ; ツリービュー作成
    gsel 0
    CreateTreeView  0, 0, winx, winy
    hTree = stat                ; ツリービューのハンドル

    ; ツリービューのイメージリストを設定
    SetTreeImageList hTree, himl

    ; ツリービューのアイテム追加
    AddTreeItem  hTree,"親アイテム1",0,0,$FFFF0002
    hRoot.0 = stat
    AddTreeItem  hTree,"親アイテム2",0,0,$FFFF0002
    hRoot.1 = stat
    AddTreeItem  hTree, "子アイテム1", 1, hRoot.0, $FFFF0002
    hChild.0 = stat
    AddTreeItem  hTree, "子アイテム2", 1, hRoot.0, $FFFF0002
    hChild.1 = stat
    AddTreeItem  hTree, "子アイテム3", 1, hRoot.1, $FFFF0002
    AddTreeItem  hTree, "子アイテム4", 1, hRoot.1, $FFFF0002
    AddTreeItem  hTree, "孫アイテム1", 2, hChild.0, $FFFF0002
    AddTreeItem  hTree, "孫アイテム2", 2, hChild.1, $FFFF0002

    stop