タブコントロールを作成してみる ACT-1

タブコントロール

今回作成するのはコモンコントロールの1つ『タブコントロール』です。このコントロールは複数のページをウィンドウに表示することができるようにするものです。ユーザーがタブを選択すると、それぞれのページの情報が表示されるようにします。

今回は、右のようなものを作成します。タブコントロールの中の表示領域に描画はしません。タブコントロールを作成して、それぞれのタブが選択されたら、通知メッセージを受け取ってその旨をメッセージで表示するようにするだけです。

タブコントロールの作成手順

タブコントロールの作成手順は以下のようになります。

  1. InitCommonControls 関数でコモンコントロールライブラリを初期化する。
  2. CreateWindowEx 関数で、ウィンドウクラス名を "SysTabControl32" にし、ウィンドウスタイルを WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS にする。
  3. TCITEM 構造体に情報を格納してコントロールに TCM_INSERTITEM メッセージを送信し、新しいタブアイテム(ページ)を追加する。
  4. 親ウィンドウのメッセージ取得設定で、コモンコントロールの TCN_SELCHANGE 通知メッセージを設定する。
  5. TCN_SELCHANGE 通知メッセージが親ウィンドウに送られたら、 TCM_GETCURSEL メッセージを送信して、選択されたタブのインデックスを取得する。

タブコントロールの作成

タブコントロールの作成は CreateWindowEx 関数で行ないます。ウィンドウクラス名を "SysTabControl32" にすることでタブコントロールが作成できます。ウィンドウスタイルに WS_VISIBLE (0x10000000) と WS_CHILD (0x40000000) を指定するのは他のコモンコントロールと同じですが、ここではさらに WS_CLIPCHILDREN (0x02000000) も合わせて指定しておきます。このスタイルは、ウィンドウが子ウィンドウを持つ場合に子ウィンドウが占める領域を除外して描画するというものです。今回はタブコントロール上に子ウィンドウはありませんが、実際にタブコントロール上に描画をするときには子ウィンドウを置くことになるので、ここでも指定しておくことにします。

ページの追加

タブコントロールが作成できたら、新しいページ(タブアイテム)を追加していきます。アイテムを追加するには、 TCITEM 構造体に情報を格納してから TCM_INSERTITEM メッセージをコントロールに送信します。

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

typedef struct tagTCITEM {
    UINT   mask;        // 有効なメンバ
    DWORD  dwState;     // アイテム状態
    DWORD  dwStateMask; // dwStateの有効ビットを示すマスク
    LPTSTR pszText;     // タブの文字列
    int    cchTextMax;  // pszTextのサイズ
    int    iImage;      // イメージインデックス
    LPARAM lParam;      // アプリケーション定義値
} TCITEM, FAR *LPTCITEM;

mask メンバに 0x0001 (TCIF_TEXT) を指定し、 pszText メンバにはタブに表示する文字列を入れた変数のアドレスを格納しておきます。

TCITEM 構造体に必要な情報を格納したら、タブコントロールに TCM_INSERTITEM メッセージを送信します。

#define  TCM_INSERTITEM    0x1307

TCM_INSERTITEM
    wParam = iItem;
    lParam = pitem;

iImageList パラメータには、新しく挿入するタブアイテムの位置のインデックスを指定します。このインデックスは 0 から始まる値です。 pitem パラメータには、情報を格納しておいた TCITEM 構造体のアドレスを指定します。

TCN_SELCHANGE 通知メッセージ

以上の操作でタブコントロールは作成できると思います。あとは、ユーザーがタブをクリックなどで選択したときに送られてくるメッセージの取得設定をします。

選択されているタブが変更されると、タブコントロールは親ウィンドウに TCN_SELCHANGE 通知メッセージを送るようになっています。このメッセージは WM_NOTIFY メッセージの形式で送られてきます。

#define  TCN_SELCHANGE    -551

このメッセージを取得できるように設定するには、以下のようにメッセージ取得設定を行ないます。

set_subclass             ; ウィンドウサブクラス化
set_notify -551          ; 通知メッセージ設定

あとは、ループ中でメッセージが送られてくるのを待つのみです。

コントロールから TCN_SELCHANGE 通知メッセージが送られると、 get_message を実行したときに変数 msgval には以下のように代入されることになります。

msgval : 受け取ったメッセージ情報が格納される配列変数
msgval.0 : メッセージを受け取ったウィンドウのハンドル
msgval.1 : 0x004E (WM_NOTIFY)
msgval.2 : コントロールID (wParam パラメータ)
msgval.3NMHDR 構造体のアドレス (lParam パラメータ)
msgval.4 : コントロールのハンドル
msgval.5 : コントロールID
msgval.6 : -551 (TCN_SELCHANGE 通知コード)

選択されているタブアイテムの取得

通知メッセージの中には選択されたアイテムについての情報が含まれていないので、どのタブアイテムが選択されたのかを自分で取得する必要があります。選択されているアイテムを取得するには、タブコントロールに TCM_GETCURSEL メッセージを送信します。

#define  TCM_GETCURSEL    0x130B

TCM_GETCURSEL
    wParam = 0;
    lParam = 0;

このメッセージを送信すると、 SendMessage 関数の戻り値として選択されているアイテムの位置のインデックスが返ります。

サンプルスクリプト

さて、実際にスクリプトを書いてみます。

    #include "llmod.as"
    #include "hsgetmsg.as"

    #module ;######## タブコントロール操作モジュール ###############

    ;===============================================================
    ; CreateTabCtl  タブコントロール作成
    ;===============================================================
    #deffunc CreateTabCtl int, int, int, int
    mref cx, 0                  ; x座標
    mref cy, 1                  ; y座標
    mref sx, 2                  ; 幅
    mref sy, 3                  ; 高さ
    mref stt, 64                ; stat
    mref bmscr, 67              ; 描画中ウィンドウのBMSCR構造体

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

    ; タブコントロールの作成
    pm.0 = cx, cy, sx, sy       ; 座標、サイズ
    pm.4 = 0x52000000           ; WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS
    pm.5 = 0                    ; 親ウィンドウ(0のとき描画中ウィンドウ)
    pm.6 = 0                    ; 拡張ウィンドウスタイル
    _makewnd pm, "SysTabControl32"
    stt = pm                    ; タブコントロールのハンドル
    return

    ;===============================================================
    ; AddTabItem  タブアイテム追加
    ;===============================================================
    #deffunc AddTabItem int, str, int
    mref hTab, 0                ; タブコントロールのハンドル
    mref setText, 33            ; 表示する文字列
    mref idx, 2                 ; インデックス

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

    ; TCITEM 構造体
    tcitem.0 = 0x0001           ; TCIF_TEXT
    getptr tcitem.3, szText

    ; TCM_INSERTITEM メッセージ送信
    pm = hTab, 0x1307, idx
    getptr pm.3, tcitem
    sendmsg pm
    return

    ;===============================================================
    ; GetTabSel  選択タブアイテム取得
    ;===============================================================
    #deffunc GetTabSel int
    mref hTab, 0                ; タブコントロールのハンドル

    ; TCM_GETCURSEL メッセージ送信
    pm = hTab, 0x130B, 0, 0
    sendmsg pm
    return

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


    #define WM_NOTIFY         0x004E

    ; 負の数は10進数で #define 定義できないので16進形式で定義
    ; (#define TCN_SELCHANGE     -551)
    #define TCN_SELCHANGE     0xFFFFFDD9

    ; タブコントロールの作成
    screen 0, 250, 200
    CreateTabCtl 0, 0, winx, winy
    hTab = stat                 ; コントロールのハンドル

    ; タブアイテム(ページ)の追加
    AddTabItem hTab, "タブ1", 0
    AddTabItem hTab, "タブ2", 1
    AddTabItem hTab, "タブ3", 2

    ; ウィンドウのサブクラス化
    set_subclass                ; サブクラス化
    set_notify TCN_SELCHANGE    ; WM_NOTIFY 形式通知の取得設定

    ; メッセージパラメータ用変数
    dup msg,  msgval.1          ; メッセージが格納される変数
    dup wprm, msgval.2          ; wParamパラメータが格納される変数
    dup lprm, msgval.3          ; lParamパラメータが格納される変数
    dup nmhdr, msgval.4         ; NMHDR 構造体が格納される変数

*mainloop
    get_message msgval
    if msgval {
        if (msg == WM_NOTIFY) & (nmhdr == hTab) : gosub *onTabNotify
    } else {
        wait 10
    }
    goto *mainloop

*onTabNotify
    ;--------------- タブコントロールからの WM_NOTIFY ---------------
    if nmhdr.2 == TCN_SELCHANGE {
        ; TCN_SELCHANGE通知が送られたとき
        GetTabSel hTab
        idx = stat + 1
        dialog "タブ"+idx+"が選択されました。"
    }
    return

スクリプトを実行させると、タブを選択したときにメッセージが表示されますね。

ところで、上のスクリプトを見て気付いたかもしれませんが、HSPでは #define で負の数に定数名を定義させることができません。(少なくとも HSP Ver 2.55 では。) そこで、負の数に定数名を付けるテクニックとして、16進数で定義するというものがあります。例えば、 TCN_SELCHANGE 通知メッセージはコードが -551 (16進数では0xFFFFFDD9)であるので、これを #define で定義するには

#define TCN_SELCHANGE     0xFFFFFDD9

とすればよいのです。16進数はWindows標準の電卓で簡単に求められます。(「電卓の種類」メニューで『関数電卓』を選択)


次回は、ちょっとした裏技的手法によりタブコントロールの中に描画をしてみたいと思います。