これらの機能は16ビット時代にはありませんでした。どのように実現しているかと
いうと、これはSCROLLINFO構造体と、SetScrollInfo関数で行います。
cbSizeメンバはこの構造体のサイズです。typedef struct tagSCROLLINFO { // si UINT cbSize; UINT fMask; int nMin; int nMax; UINT nPage; int nPos; int nTrackPos; } SCROLLINFO; typedef SCROLLINFO FAR *LPSCROLLINFO;
SCROLLINFO構造体に値をセットしてこの関数を実行するとスクロールバーに反映されます。 fnBarにはSB_HORZまたはSB_VERTがきます。また、スクロールバーを子供ウィンドウコントロール として作った場合はSB_CTLになります。この場合hwndはスクロールバーのハンドルとなります。int SetScrollInfo( HWND hwnd, // スクロールバーを持っているウィンドウのハンドル int fnBar, // スクロールバーフラグ LPSCROLLINFO lpsi, // SCROLLINFO構造体へのポインタ BOOL fRedraw // 再描画フラグ );
では、サンプルを見てみましょう。
いつもとかわりませんが、全テキストの行数をMAXLINEでdefineしています。 この値を変えると全テキストの行数を簡単に変えることができるのでいろいろ実験して みてください。LINESIZEは1行分の高さです。この値は本来はGetTextMetrics関数 (第77章参照)で 取得すべきでしょうが簡単のために自分で適当な値を設定しています。// scroll02.cpp #define STRICT #include <windows.h> #define LINESIZE 20 //1行の高さ #define MAXLINE 10 //行数 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "scroll02"; //ウィンドウクラス int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
これは、いつもと同じです。//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); }
ウィンドウスタイルにWS_VSCROLLを加えて垂直スクロールバーを付けます。//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるスクロールバー",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW | WS_VSCROLL, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL,//親ウィンドウのハンドル、親を作るときはNULL NULL,//メニューハンドル、クラスメニューを使うときはNULL hInst,//インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
ウィンドウサイズが変更されたら(WM_SIZE)当然、SCROLLINFO構造体の 各メンバの値も変更する必要があります。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id, i, len, dy; HDC hdc; PAINTSTRUCT ps; char *str_org = "%2d: 第%2d行目 猫でもわかるスクロールバー"; char str[256]; static SCROLLINFO si; static int dispno, pos; RECT rc; switch (msg) { case WM_SIZE: GetClientRect(hWnd, &rc); dispno = rc.bottom / LINESIZE; si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; si.nMin = 0; si.nMax = MAXLINE; si.nPage = dispno; si.nPos = pos; SetScrollInfo(hWnd, SB_VERT, &si, TRUE); break; case WM_VSCROLL: switch (LOWORD(wp)) { case SB_LINEUP: dy = -1; break; case SB_LINEDOWN: dy = 1; break; case SB_PAGEUP: dy = -1 * si.nPage; break; case SB_PAGEDOWN: dy = si.nPage; break; case SB_THUMBTRACK: dy = HIWORD(wp) - si.nPos; break; default: dy = 0; break; } dy = max(-1 * si.nPos, min(dy, si.nMax - si.nPos)); if (dy != 0) { si.nPos += dy; pos = si.nPos; SetScrollInfo(hWnd, SB_VERT, &si, TRUE); ScrollWindow(hWnd, 0, -dy * LINESIZE, NULL, NULL); UpdateWindow(hWnd); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); for (i = 0 ;i <= dispno; i++) { if (i + si.nPos <= MAXLINE) { len = wsprintf(str, str_org, i+si.nPos, i+si.nPos); TextOut(hdc, 10, i*LINESIZE, str, len); } } EndPaint(hWnd, &ps); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }
スクロール関係のメッセージの処理は基本的には前章と同じです。 今回は、スクロールアップ、ダウンのスクロール量を1ページ分としました。 (これが普通)
WM_PAINTメッセージの処理は十分注意してください。 ScrollWindow関数と矛盾のない処理にしないと画面がめちゃくちゃになります。 これは、結構慣れるまで苦労します。
Update Jan/16/1998 By Y.Kumei