山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他タブコントロール
概要
タブコントロールは、下図のようにタブにより画面(ページ)を切り替えて使用するものです。
これらの表示は、メインダイアログにタブと連動する子ダイアログを貼り付けてそのうち1つのダイアログのみ表示されるというトリッキーな実装が標準的です。
下図では名前タブと属性タブにより切り替えています。
ダイアログにリソースでタブコントロールのみを作成しTabCtrl_InsertItemマクロでタブを追加する下図のようになります。
ダイアログボックスを親とするそれぞれのタブ用の子ダイアログをCreateDialog APIで作成します。
それぞれの子ダイアログはリソースで定義され下図の通りです。
子ウィンドウのスタイルはリソースで
後は、子ダイアログのいずれかの表示を有効にすれば、初期表示は完成します。
タブがクリックされるとTCN_SELCHANGEメッセージが発生するので選択されたタブの表示を有効にしてタブの切り替えを実装します。
子ウィンドウは、タブコントロールの大きさに合わせてプログラムで大きさを変更しています。
各ウィンドウの親子関係は以下の通りです。
""に囲まれた最初の文字列がウィンドウタイトル、次の文字列がウィンドウクラス名です。
これらの表示は、メインダイアログにタブと連動する子ダイアログを貼り付けてそのうち1つのダイアログのみ表示されるというトリッキーな実装が標準的です。
下図では名前タブと属性タブにより切り替えています。
ダイアログにリソースでタブコントロールのみを作成しTabCtrl_InsertItemマクロでタブを追加する下図のようになります。
ダイアログボックスを親とするそれぞれのタブ用の子ダイアログをCreateDialog APIで作成します。
それぞれの子ダイアログはリソースで定義され下図の通りです。
子ウィンドウのスタイルはリソースで
STYLE WS_BORDER | WS_CHILD
のように定義しています。子ウィンドウにタイトルはつけてはいけません。後は、子ダイアログのいずれかの表示を有効にすれば、初期表示は完成します。
タブがクリックされるとTCN_SELCHANGEメッセージが発生するので選択されたタブの表示を有効にしてタブの切り替えを実装します。
子ウィンドウは、タブコントロールの大きさに合わせてプログラムで大きさを変更しています。
各ウィンドウの親子関係は以下の通りです。
""に囲まれた最初の文字列がウィンドウタイトル、次の文字列がウィンドウクラス名です。
メインダイアログ ├"" "SysTabControl32" │ ├ "山田太郎" "Static" │ ├ "男" "Static" │ ├ "子供" "Static" │ ├ "OK" "Button" │ └ "キャンセル" "Button" │ ├"" "#32770" │ └ "山田太郎" "Edit" │ └"" "#32770" ├ "性別" "Button" ├ "男" "Button" ├ "女" "Button" ├ "料金" "Button" ├ "子供" "Button" └ "大人" "Button"
テスト環境
コンパイラ
Visual C++ 2008 Standard 32/64bitVisual C++ 2013 Express 32/64bit
プロジェクトの作成
Win32プロジェクト Windowsアプリケーション実行環境
Windows 8.1 Enterprise 64bitWindows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit
プログラムソースの概要
_tWinMain関数
Windowsから最初に_tWinMain関数が呼び出されます。 DialogBox APIよりメインダイアログボックスを作成します。DlgProc1ダイアログボックスのプロシージャー
メインダイアログボックスのプロシージャーです。case WM_INITDIALOG:
ダイアログボックスの初期化時に呼び出されます。GetDlgItem APIによりタブコントロールのウィンドウハンドルを取得します。
TabCtrl_InsertItemマクロによりタブを追加します。
CreateDialog APIにより子ダイアログを作成します。
子ダイアログの最初の1個目を表示します。
子ダイアログの大きさをタブコントロールに合わせるためGetClientRect APIによりメインダイアログボックスのクライアントサイズを取得し、SendMessage APIによりWM_SIZEメッセージを作成します。
case WM_SIZE:
ダイアログボックスの大きさが変更されたときに呼び出されます。ダイアログボックスの大きさは、ダイアログボックスのスタイルで変更できないようにしていますが、前項のとおり、子ウィンドウの大きさを調整するために、WM_SIZEメッセージの処理を行います。
リソース側でうまく大きさを調整しても環境が変わるとウィンドウ枠の大きさが変わったりするのでプログラムで処理するのが常道です。
通常は、メインダイアログのクライアント領域一杯にタブコントロールは配置されますが、今回は小さくしています。
通常であれば、タブコントロールのクライアント領域の大きさを取得し、TabCtrl_AdjustRectマクロにより子ウィンドウの大きさを算出し、MoveWindow APIにより大きさを変更します。
今回は、タブコントロールのメインウィンドウ上でのクライアント領域の大きさを求めるためにGetClientRect APIを使用しますが、このAPIは左上座標が0,0で右下座標がウィンドウサイズとなります。
今回のケースでは、タブコントロールの左上のクライアント座標が0,0ではないので、メインダイアログの左上座標(0,0)とタブコントロール上での左上座標(0,0)をスクリーン座標に変換し、その差分をメインダイアログボックス上でのタブコントロールのクライアント領域の左上座標としています。
あとは、通常通りTabCtrl_AdjustRectマクロにより子ウィンドウの大きさを算出し差分を加算しています。
case WM_NOTIFY:
case TCN_SELCHANGE:
タブをクリックした場合に発生します。TabCtrl_GetCurSelマクロによりクリックされたタブ番号を取得し、取得されたタブ番号の子ダイアログボックスの表示を有効にします。
case IDOK:
OKプッシュボタンのID番号がIDOKです。 OKプッシュボタンをクリックするとこのメッセージが発生します。 GetDlgItemText APIを呼び出し、エディットボックスの文字列を取得します。 MessageBox APIを呼び出し、取得した文字列を表示します。 EndDialog APIを呼び出し、ダイアログボックスを終了させます。 このAPIの第2引数は、ダイアログボックスが終了し、DialogBox APIが返す値となります。case IDCANCEL:
キャンセルプッシュボタンのID番号がIDCANCELKです。 キャンセルプッシュボタンをクリックするとこのメッセージが発生します。 EndDialog APIを呼び出し、ダイアログボックスを終了させます。 このAPIの第2引数は、ダイアログボックスが終了し、DialogBox APIが返す値となります。Page0Proc関数
子ダイアログ(ページ0)のプロシージャーです。case WM_INITDIALOG:
エディットボックスの初期値を設定しています。case WM_COMMAND:
エディットボックスの内容が変更された時に発生するEN_UPDATEメッセージを処理します。エディットボックスの内容を取得し、親ダイアログボックスのラベルを変更します。
ラベルは親ダイアログの子ウィンドウです。子ダイアログも親ダイアログの子ウィンドウですので、親ウィンドウのハンドルをGetParen APIで取得しGetDlgItem APIを呼び出しています。
Page1Proc関数
子ダイアログ(ページ1)のプロシージャーです。case WM_INITDIALOG:
ラジオボタンの初期値を設定しています。case WM_COMMAND:
ラジオボタンの内容が変更された時に発生すBN_CLICKEDメッセージを処理します。ラジオボタン内容により、親ダイアログボックスのラベルを変更します。
ラベルは親ダイアログの子ウィンドウです。子ダイアログも親ダイアログの子ウィンドウですので、親ウィンドウのハンドルをGetParen APIで取得しGetDlgItem APIを呼び出しています。
プログラムソース
tabctl.cpp
// タブコントロールサンプル
// Visual C++ 2013 32/64bit
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <tchar.h>
#include "resource.h"
// ダイアログボックスプロシージャー
LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
// タブコントロールの0ページ目
LRESULT CALLBACK Page0Proc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
// タブコントロールの1ページ目
LRESULT CALLBACK Page1Proc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
HINSTANCE hInst;
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,
TCHAR* lpszCmdLine, int nCmdShow){
::hInst = hInstance;
DialogBox(hInstance, TEXT("TAB_DLG"), 0, (DLGPROC)DlgProc1);
return (int)0;
}
// ダイアログボックスプロシージャー
LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
static HWND hTab;
static HWND hPage0;
static HWND hPage1;
RECT rc;
TCITEM tc_item;
switch (msg) {
case WM_INITDIALOG:
hTab = GetDlgItem(hDlg, IDC_TABCNTRL);
tc_item.mask = TCIF_TEXT;
tc_item.pszText = _TEXT("名前");
TabCtrl_InsertItem(hTab, 0, &tc_item);
tc_item.pszText = TEXT("属性");
TabCtrl_InsertItem(hTab, 1, &tc_item);
hPage0 = CreateDialog(::hInst, _TEXT("DLG1"), hDlg, (DLGPROC)Page0Proc);
hPage1 = CreateDialog(::hInst, _TEXT("DLG2"), hDlg, (DLGPROC)Page1Proc);
ShowWindow(hPage0, SW_SHOW);
ShowWindow(hPage1, SW_HIDE);
GetClientRect(hDlg, &rc);
SendMessage(hDlg, WM_SIZE, 0, MAKELPARAM(rc.right, rc.bottom));
return TRUE;
case WM_SIZE:{
POINT dlg_pt;
POINT tab_pt;
dlg_pt.x = dlg_pt.y = 0;
ClientToScreen(hDlg, &dlg_pt);
tab_pt.x = tab_pt.y = 0;
ClientToScreen(hTab, &tab_pt);
GetClientRect(hTab, &rc);
TabCtrl_AdjustRect(hTab, FALSE, &rc);
int dx = tab_pt.x - dlg_pt.x;
int dy = tab_pt.y - dlg_pt.y;
MoveWindow(hPage0, rc.left+dx, rc.top+dy, rc.right - rc.left, rc.bottom - rc.top , TRUE);
MoveWindow(hPage1, rc.left+dx, rc.top+dy, rc.right - rc.left, rc.bottom - rc.top, TRUE);
break;
}
case WM_NOTIFY:
switch (((NMHDR *)lParam)->code){
case TCN_SELCHANGE:{ // タブの切り替え
int num=TabCtrl_GetCurSel(hTab);
if (num == 0){
ShowWindow(hPage0, SW_SHOW);
ShowWindow(hPage1, SW_HIDE);
}
else{
ShowWindow(hPage0, SW_HIDE);
ShowWindow(hPage1, SW_SHOW);
}
break;
}
}
return FALSE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:{
EndDialog(hDlg, TRUE);
return TRUE;
}
case IDCANCEL:
EndDialog(hDlg,FALSE);
return FALSE;
default:
return FALSE;
}
default:
return FALSE;
}
return TRUE;
}
// タブコントロールの0ページ目
LRESULT CALLBACK Page0Proc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
TCHAR buf[32];
switch (msg) {
case WM_INITDIALOG:
SetWindowText(GetDlgItem(hDlg, IDC_EDIT1), _TEXT("山田太郎"));
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_EDIT1:
if (HIWORD(wParam) == EN_UPDATE){ // エディットボックスが変更された場合
GetDlgItemText(hDlg, IDC_EDIT1, (TCHAR*)buf, sizeof(buf) / sizeof(TCHAR));
SetWindowText(GetDlgItem(GetParent(hDlg), IDC_LABEL1), buf);
}
break;
default:
return FALSE;
}
default:
return FALSE;
}
return TRUE;
}
// タブコントロールの1ページ目
LRESULT CALLBACK Page1Proc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg) {
case WM_INITDIALOG:
SendMessage(GetDlgItem(hDlg, IDC_RADIOBUTTON101), BM_SETCHECK, BST_CHECKED, 0);
SendMessage(GetDlgItem(hDlg, IDC_RADIOBUTTON111), BM_SETCHECK, BST_CHECKED, 0);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_RADIOBUTTON101:
if (HIWORD(wParam) == BN_CLICKED){
SetWindowText(GetDlgItem(GetParent(hDlg), IDC_LABEL103), _TEXT("男"));
}
return FALSE;
case IDC_RADIOBUTTON102:
if (HIWORD(wParam) == BN_CLICKED){
SetWindowText(GetDlgItem(GetParent(hDlg), IDC_LABEL103), _TEXT("女"));
}
return FALSE;
case IDC_RADIOBUTTON111:
if (HIWORD(wParam) == BN_CLICKED){
SetWindowText(GetDlgItem(GetParent(hDlg), IDC_LABEL113), _TEXT("子供"));
}
return FALSE;
case IDC_RADIOBUTTON112:
if (HIWORD(wParam) == BN_CLICKED){
SetWindowText(GetDlgItem(GetParent(hDlg), IDC_LABEL113), _TEXT("大人"));
}
return FALSE;
default:
return FALSE;
}
default:
return FALSE;
}
return TRUE;
}
resource.h
#define IDC_TABCNTRL 300
#define IDC_EDIT1 200
#define IDC_LABEL1 210
#define IDC_GROUPBOX100 100
#define IDC_RADIOBUTTON101 101
#define IDC_RADIOBUTTON102 102
#define IDC_LABEL103 103
#define IDC_GROUPBOX110 110
#define IDC_RADIOBUTTON111 111
#define IDC_RADIOBUTTON112 112
#define IDC_LABEL113 113
resource.rc
#include <windows.h>
#include "resource.h"
TAB_DLG DIALOG DISCARDABLE 0, 0, 205, 185
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT
CAPTION "タブコントロール"
FONT 9, "MS Shell Dlg"
{
CONTROL "", IDC_TABCNTRL, "SYSTABCONTROL32", WS_CHILD | WS_VISIBLE | TCS_SINGLELINE | TCS_TABS, 7, 7, 120, 120
CONTROL "" , IDC_LABEL1 ,"STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY,134,7,64,14
CONTROL "男", IDC_LABEL103, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 134, 27, 64, 14
CONTROL "子供", IDC_LABEL113, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 134, 48, 64, 14
DEFPUSHBUTTON "OK", IDOK, 9, 154, 50, 14
PUSHBUTTON "キャンセル", IDCANCEL, 70, 154, 50, 14
}
DLG1 DIALOG DISCARDABLE 0, 0, 121, 76
STYLE WS_BORDER | WS_CHILD
FONT 9, "MS Shell Dlg"
{
EDITTEXT IDC_EDIT1, 7, 13, 104, 14, ES_AUTOHSCROLL
}
DLG2 DIALOG DISCARDABLE 0, 0, 129, 147
STYLE WS_BORDER | WS_CHILD
FONT 9, "MS Shell Dlg"
{
CONTROL "性別", IDC_GROUPBOX100, "BUTTON", WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 7, 7, 85, 35
CONTROL "男", IDC_RADIOBUTTON101, "BUTTON", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP, 14, 21, 32, 14
CONTROL "女", IDC_RADIOBUTTON102, "BUTTON", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 53, 21, 32, 14
CONTROL "料金", IDC_GROUPBOX110, "BUTTON", WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 7, 56, 85, 35
CONTROL "子供", IDC_RADIOBUTTON111, "BUTTON", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP, 14, 70, 32, 14
CONTROL "大人", IDC_RADIOBUTTON112, "BUTTON", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 53, 70, 32, 14
}
ソースファイルと実行ファイルのダウンロード
ダウンロード tabctl.zip(38.9kByte)ZIPファイルに含まれるファイル
tabctl.cpp resource.h resource.rc tabctl.exe
Copyright (C) 2012 山本ワールド All Rights Reserved.