第100章 SCROLLINFO構造体


今回は、SCROLLINFO構造体を使ったスクロールの実際について解説します。
左の図でスクロールつまみの大きさを見てください。テキストは第0行から 第10行までの11行あります。クライアント領域に表示されているのは第 2行から8行までの7行です。全体の11分の8が表示されていることになります。 スクロールつまみの大きさも、これに比例しています。ウィンドウを大きくすると つまみも大きくなります。また、すべての行が表示されるとスクロールバーは 消えてなくなります。


これらの機能は16ビット時代にはありませんでした。どのように実現しているかと いうと、これはSCROLLINFO構造体と、SetScrollInfo関数で行います。

typedef struct tagSCROLLINFO { // si UINT cbSize; UINT fMask; int nMin; int nMax; UINT nPage; int nPos; int nTrackPos; } SCROLLINFO; typedef SCROLLINFO FAR *LPSCROLLINFO;

cbSizeメンバはこの構造体のサイズです。
fMaskは取得するスクロールバーパラメータで次の中から指定します。
SIF_DISABLENOSCROLLはスクロールバーが不要の時は除去せずに使用不能にします。
SIF_PAGEはスクロールページをnPageに指定した値にします。
SIF_POSはスクロール位置をnPosで指定した値にします。
SIF_RANGEはスクロール範囲をnMinとnMaxにします。
これらの値は複数選択できます。
nMin, nMaxは最小、最大スクロール位置を指定します。
nPageはページサイズを指定します。この値がページアップ・ページダウンのスクロール量と なります。
nPosはつまみの位置です。

int SetScrollInfo( HWND hwnd, // スクロールバーを持っているウィンドウのハンドル int fnBar, // スクロールバーフラグ LPSCROLLINFO lpsi, // SCROLLINFO構造体へのポインタ BOOL fRedraw // 再描画フラグ );

SCROLLINFO構造体に値をセットしてこの関数を実行するとスクロールバーに反映されます。 fnBarにはSB_HORZまたはSB_VERTがきます。また、スクロールバーを子供ウィンドウコントロール として作った場合はSB_CTLになります。この場合hwndはスクロールバーのハンドルとなります。
fRedrawをTRUEにすると、スクロールバーの再描画が実行されます。
戻り値は現在のつまみ位置を表します。

では、サンプルを見てみましょう。

// 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; }

いつもとかわりませんが、全テキストの行数をMAXLINEでdefineしています。 この値を変えると全テキストの行数を簡単に変えることができるのでいろいろ実験して みてください。LINESIZEは1行分の高さです。この値は本来はGetTextMetrics関数 (第77章参照)で 取得すべきでしょうが簡単のために自分で適当な値を設定しています。

//ウィンドウ・クラスの登録 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)); }

これは、いつもと同じです。

//ウィンドウの生成 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; }

ウィンドウスタイルにWS_VSCROLLを加えて垂直スクロールバーを付けます。

//ウィンドウプロシージャ 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; }

ウィンドウサイズが変更されたら(WM_SIZE)当然、SCROLLINFO構造体の 各メンバの値も変更する必要があります。

スクロール関係のメッセージの処理は基本的には前章と同じです。 今回は、スクロールアップ、ダウンのスクロール量を1ページ分としました。 (これが普通)

WM_PAINTメッセージの処理は十分注意してください。 ScrollWindow関数と矛盾のない処理にしないと画面がめちゃくちゃになります。 これは、結構慣れるまで苦労します。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Jan/16/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。