Scroll Bar で画像をスクロール

ウインドウサイズより大きな画像にスクロールバーを設定します。
ウインドウに表示しきれない画像やテキストは、スクロールバーを付けて表示するのが常識です。
EditControl や ListControl を使うと自動的にスクロール機能が備わっているのですが、どうしても自分でスクロール設定を行わなければならないことも良くあります。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. BmpName[] に表示する BMP 画像ファイルの名前を設定して下さい。
    g_hmdc が画像のコンパティブル DC で、Width と Height が画像の幅と高さです。
    si_v と si_h が縦と横のスクロールバー構造体です。
        char        BmpName[128]= "c:\\data\\geo.bmp";
        HDC         g_hmdc;             //画像のコンパティブルDC
        long        Width,Height;       //画像サイズ
    
        //スクロール制御領域
        SCROLLINFO  si_v;
        SCROLLINFO  si_h;
        
  2. WinMain() はいつもと同じです。
    メッセージループに入る前に BMP 画像をロードして g_hmdc と Width,Height を設定して下さい。
    g_hmdc の開放はプログラムの終了時に行います。
        ShowWindow(hWnd, nCmdShow);
        g_hmdc= LoadBMP(・・・);  //BMP 画像を入力
        UpdateWindow(hWnd);
        
  3. CALLBACK 関数です。
    Windows のサイズを取得する RECT 構造体と、幅と高さを計算するために dy,dx,W,H,M を定義します。
        LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {
            HDC             hdc;
            PAINTSTRUCT     ps;
            RECT            rc;
            int             dy,dx,W,H,M;
        
  4. WM_DESTROY: では、プログラムの終了前に g_hmdc を開放して下さい。
        case WM_DESTROY:
            DeleteDC(g_hmdc);
            PostQuitMessage(0);
            break;
        
  5. WM_PAINT: で画像を表示します。
    W に表示する画像の幅を、H に高さを設定します。
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            GetWindowRect(hWnd, &rc);
            W= rc.right-rc.left;
            if (W>Width)   W= Width;
            H= rc.bottom-rc.top;
            if (H>Height)  H= Height;
            BitBlt(hdc,0,0,W,H,g_hmdc,si_h.nPos,si_v.nPos,SRCCOPY);
            EndPaint(hWnd, &ps);
            break;
        
  6. WM_SIZE: では Window のサイズと画像のサイズを比較してスクロールバーを設定します。
    SIF_PAGE フラグを ON にして nPage にページの大きさを設定します。
    SIF_RANGE フラグを ON にして nMax と nMin にスクロールの範囲を設定します。
    SIF_POS フラグを ON にして nPos に位置を設定します。
    nPage > (nMax - nMin + 1) のとき、スクロールバーは表示されません。
    SetScrollInfo() で縦と横のスクロールバーを表示します。
        case WM_SIZE:
            // 縦スクロールの設定
            M= Height-HIWORD(lParam);   //画像の高さ-ウインドウの高さ
            if (M<0)  M= 0;
            si_v.cbSize = sizeof(SCROLLINFO);
            si_v.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
            si_v.nMin = 0;
            si_v.nMax = M;
            si_v.nPage = 16;
            si_v.nPos = 0;
            // 横スクロールの設定
            si_h= si_v;
            M= Width-LOWORD(lParam);    //画像の幅-ウインドウの幅
            if (M<0)  M= 0;
            si_h.nMax = M;
            SetScrollInfo(hWnd, SB_VERT, &si_v, TRUE);
            SetScrollInfo(hWnd, SB_HORZ, &si_h, TRUE);
            break;
        
  7. WM_VSCROLL: で縦スクロールバーの操作を検出します。
    スクロールバーの操作情報に基づき dy に縦方向の移動量を格納します。
    dy から nPos を計算して、縦のスクロールバーを設定します。
    ScrollWindow() 関数を実行すると表示中のウインドウがスクロールされます。
    このとき注意しなければならないことは、スクロールによって失われる部分の描画です。
    一般にウインドウの更新領域は InvalidateRect() で設定しますが、ScrollWindow() を使うと スクロールによって失われた部分に更新領域が設定されます。
    但し、更新領域の再描画はプログラマの責任で行わなければなりません。
    ScrollWindow() を使ってみれば解かりますが、プログラムを作成する上では便利な関数とは言えません。
        case WM_VSCROLL:
            switch (LOWORD(wParam))
            {   case SB_LINEUP:     dy = -1;    break;
                case SB_LINEDOWN:   dy =  1;    break;
                case SB_PAGEUP:     dy = -1*si_v.nPage;             break;
                case SB_PAGEDOWN:   dy = si_v.nPage;                break;
                case SB_THUMBTRACK: dy = HIWORD(wParam)-si_v.nPos;  break;
                default:            dy =  0;    break;
            }
            dy = max(-1*si_v.nPos, min(dy,si_v.nMax-si_v.nPos));
            if (dy != 0)
            {   si_v.nPos += dy;
                SetScrollInfo(hWnd, SB_VERT, &si_v, TRUE);
                ScrollWindow(hWnd, 0, -dy, NULL, NULL);
            }
            break;
        
  8. スクロールバーが操作されたときの詳細情報です。
    SB_LINEUP: UPボタンをプレス
    SB_LINEDOWN: DOWNボタンをプレス
    SB_PAGEUP: ツマミの上をプレス
    SB_PAGEDOWN: ツマミの下をプレス
    SB_THUMBTRACK:ツマミをドラッグ
  9. WM_HSCROLL: で横スクロールバーの操作を検出します。
    横スクロールバーの説明は、縦の操作に準じます。
        case WM_HSCROLL:
            switch (LOWORD(wParam))
            {   case SB_LINEUP:     dx = -1;    break;
                case SB_LINEDOWN:   dx =  1;    break;
                case SB_PAGEUP:     dx = -1*si_h.nPage;             break;
                case SB_PAGEDOWN:   dx = si_h.nPage;                break;
                case SB_THUMBTRACK: dx = HIWORD(wParam)-si_h.nPos;  break;
                default:            dx =  0;    break;
            }
            dx = max(-1*si_h.nPos, min(dx,si_h.nMax-si_h.nPos));
            if (dx != 0)
            {   si_h.nPos += dx;
                SetScrollInfo(hWnd, SB_HORZ, &si_h, TRUE);
                ScrollWindow(hWnd, -dx, 0, NULL, NULL);
            }
            break;
        

超初心者のプログラム入門(Win32API C++)