ステータスバー


情報を表示

現在のウィンドウズアプリケーションの多くは
コントロールの意味をユーザーに知らせる手段として、ステータスバーを用います

メニューアイテムの意味や、ボタンなどの意味をステータスバーに表示し
それを押すとどのようなことが起こるのかを事前に知らせることができます
複雑な機能を持つアプリケーションには、必須の機能といえるでしょう

ステータスバーを作成するには CreateStatusWindow() 関数を使います
もちろん、コモンコントロールなので InitCommonControls() を先に使ってください
HWND CreateStatusWindow(
	LONG style , LPCTSTR lpszText ,
	HWND hwndParent , UINT wID
);
style には、ウィンドウスタイルを指定します
少なくとも WS_CHILD フラグを含まなければなりません
通常のウィンドウスタイルに加え、以下の定数を組み合わせて指定できます

定数解説
CCS_BOTTOM クライアント領域の最下部に配置
CCS_TOP クライアント領域の最上部に配置
SBARS_SIZEGRIP 右端にサイズグリップを取り入れる
最上部に配置した場合は無効

lpszText は、ステータスバーに表示する文字列を指定します
hwndParent には、ステータスバーの親ウィンドウのハンドルを指定します
wID には、このステータスバーウィンドウの識別 ID を指定します
関数が成功すれば、ステータスバーウィンドウのハンドル、失敗すれば NULL が返ります
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND hStatus;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();
		hStatus = CreateStatusWindow(
			WS_CHILD | WS_VISIBLE |
			CCS_BOTTOM | SBARS_SIZEGRIP ,
			TITLE , hWnd , 1
		);
		return 0;
	case WM_SIZE:
		SendMessage(hStatus , msg , wp , lp);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


どうでしょうか、見事にそれっぽいステータスバーを表示できました
しかし、この時点ではステータスバー内の文字は何も変化しません

バーに文字列を表示させるには、ステータスバーの仕組みをもう少し知る必要があります
実は、ステータスバーは複数のパーツに分けることができます
これによって、パーツごとに異なる種類の情報を表示させることができるのです

パーツを分割するには SB_SETPARTS メッセージを送信します
WPARAM には、設定するパーツの数を 255 以下の数値で指定します
LPARAM は、パーツの数と等しい数の要素を持つ数値配列のポインタを指定します

数値配列のそれぞれの要素には、パーツの右端の位置を表す座標を格納しておきます
この座標はクライアント座標で、-1 を指定すればバーの右端までを範囲とします
それぞれのパーツは、数値配列の昇順に配置されていきます
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND hStatus;
	static int iRight[] = { 100 , 200 , -1 };

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();
		hStatus = CreateStatusWindow(
			WS_CHILD | WS_VISIBLE |
			CCS_BOTTOM | SBARS_SIZEGRIP ,
			TITLE , hWnd , 1
		);
		SendMessage(hStatus , SB_SETPARTS , 3 , (LPARAM)iRight);
		return 0;
	case WM_SIZE:
		SendMessage(hStatus , msg , wp , lp);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムは、3つのパーツにわかれたステータスバーを表示します
最初のパーツは、もっとも左の位置から100ピクセルまでの矩形
次のパーツは、100ピクセルから200ピクセルまで
最後のパーツは、200ピクセルから一番右までの範囲に設定されています

ステータスバーに文字列をセットするには、SB_SETTEXT メッセージを送ります
WPARAM には、文字列を設定するパーツを 0 から数えたインデックスで指定します
255 を指定した場合は、分割されていない単純なステータスバーと認識されます

この値と、論理和で以下の値を組み合わせることができます
以下のフラグは、テキストの描画タイプを指定するものです

定数解説
0 くぼみのある境界線を持つデフォルトの描画
SBT_NOBORDERS 境界線無し
SBT_OWNERDRAW 親ウィンドウによって描画される
SBT_POPOUT 盛り上がりのある境界線
SBT_RTLREADING Window 95 :
ヘブライ語、またはアラビア語に対応
右から左にテキストを表示

LPARAM には、設定する文字列へのポインタを指定します
ただし、SBT_OWNERDRAW が指定されている場合、追加情報を指定します
親ウィンドウは WM_DRAWITEM を処理してテキストを描画しなければいけません
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND hStatus;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();
		hStatus = CreateStatusWindow(
			WS_CHILD | WS_VISIBLE |
			CCS_BOTTOM | SBARS_SIZEGRIP , TITLE , hWnd , 1
		);
		return 0;
	case WM_LBUTTONDOWN:
		SendMessage(hStatus , SB_SETTEXT ,
			255 , (LPARAM)TEXT("Magical nyan nyan TARUTO"));
		return 0;
	case WM_RBUTTONDOWN:
		SendMessage(hStatus , SB_SETTEXT ,
			255 , (LPARAM)TEXT("Tokyo mew mew"));
		return 0;
	case WM_SIZE:
		SendMessage(hStatus , msg , wp , lp);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムは、クライアント領域をクリックすると
ステータスバーに適当な文字列が表示されるという仕組みになっています

ここまでの内容を理解していれば、メニュー関連の情報も表示できますが
メニューヘルプを作るのであれば MenuHelp() 関数を使うと便利です
この関数は、文字列リソースとメニューメッセージを関連付けさせます
void MenuHelp(
	UINT uMsg ,
	WPARAM wParam , LPARAM lParam ,
	HMENU hMainMenu , HINSTANCE hInst ,
	HWND hwndStatus , LPUINT lpwIDs
);
uMsg には、メッセージ値を指定します
必ず WM_MENUSELECT か WM_COMMAND になるでしょう
メニューヘルプをステータスバーに表示するのは、この瞬間であるべきです
wParam と lParam には、メッセージの情報を指定します

hMainMenu は、アプリケーションのメインメニューのハンドルを指定します
hInst は、文字列リソースが格納されているモジュールのインスタンスハンドルです
hwndStatus には、ステータスウィンドウのウィンドウハンドルを指定します

lpwIDs には、メニューハンドルと文字列リソースの識別値を一つの組合せとした
数値配列へのポインタを指定します

メニューヘルプは、選択されているメニューアイテムの ID と文字列の ID を比較し
メニューに対応した文字列リソースをステータスバーに表示します

lpwIDs は、「メニューハンドル , 文字列リソース ID」を一つの組合せとした配列で
関数は、選択されたメニューへのハンドルに対応する配列を検索し
等しいものが見つかれば、対応する文字列リソースを表示します
メニューハンドルを NULL に指定すれば、文字列リソース ID だけを検索します
/*resource.h*/
#define IDM_RENA 10000
#define IDM_YUKI 10001
#define IDM_MIMI 10002
/*リソーススクリプト*/
#include "resource.h"
KITTY MENU {
	POPUP "Kitty on your lap (&K)" {
		MENUITEM "RENA" , IDM_RENA
		MENUITEM "YUKI" , IDM_YUKI
		MENUITEM "MIMI" , IDM_MIMI
	}
}

STRINGTABLE {
	IDM_RENA "口が悪くて手が早い、勝手気ままでまさに「猫」。"
	IDM_YUKI "思慮深く、清楚でけなげ。"
	IDM_MIMI "「ごしゅじんさまだ〜いすきっ!」と飛びついてきそうな甘えん坊。"
}
#include <windows.h>
#include <commctrl.h>
#include "resource.h"
#define TITLE TEXT("Kitty on your lap")

UINT ID[] = {
	NULL , IDM_RENA ,
	NULL , IDM_YUKI ,
	NULL , IDM_MIMI
};

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND hStatus;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();
		hStatus = CreateStatusWindow(
			WS_CHILD | WS_VISIBLE |
			CCS_BOTTOM | SBARS_SIZEGRIP ,
			TITLE , hWnd , 1
		);
		return 0;
	case WM_MENUSELECT:
		MenuHelp(msg , wp , lp , GetMenu(hWnd) ,
			(HINSTANCE)GetWindowLong(hWnd , GWL_HINSTANCE) ,
			hStatus , ID);
		return 0;
	case WM_SIZE:
		SendMessage(hStatus , msg , wp , lp);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= TEXT("KITTY");
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


メニューアイテムを選択し、WM_MENUSELECT が発行されると
あとは MenuHelp() 関数が文字列リソースとメニューの ID の関連付けを調べ
対応する文字列をステータスバーに表示してくれるというプログラムです


CreateStatusWindow()

HWND CreateStatusWindow(
	LONG style , LPCTSTR lpszText ,
	HWND hwndParent , UINT wID
);
ステータスバーを作成します

style - ウィンドウスタイルを指定します
lpszText - ステータスバーに表示する文字列を指定します
hwndParent - ステータスバーの親ウィンドウのハンドルを指定します
wID - このステータスバーウィンドウの識別 ID を指定します

戻り値 - ステータスバーウィンドウのハンドル、失敗すれば NULL

style には、WS_CHILD を必ず指定しなければいけません
この他に、論理和で以下のスタイルを組み合わせることができます

定数解説
CCS_BOTTOM クライアント領域の最下部に配置
CCS_TOP クライアント領域の最上部に配置
SBARS_SIZEGRIP 右端にサイズグリップを取り入れる
最上部に配置した場合は無効

MenuHelp()

void MenuHelp(
	UINT uMsg ,
	WPARAM wParam , LPARAM lParam ,
	HMENU hMainMenu , HINSTANCE hInst ,
	HWND hwndStatus , LPUINT lpwIDs
);
メニューに関連した文字列をステータスバーに表示します

uMsg - メッセージ値を指定します
wParam - メッセージの追加情報を指定します
lParam - メッセージの追加情報を指定します
hMainMenu - アプリケーションのメインメニューのハンドルを指定します
hInst - 文字列リソースが格納されているインスタンスハンドルを指定します
hwndStatus - ステータスウィンドウのウィンドウハンドルを指定します
lpwIDs - メニューとリソースの識別値を組合せとした配列へのポインタを指定します



前のページへ戻る次のページへ