現行のCPictureにスクロール機能を実装しようと調査したら、いろいろ問題があったので、書いていく。
まず、メニュー付きのウィンドウにスクロールバー付けて、水平方向スクロールバーの位置、垂直方向スクロールバーの位置をOnPaint時に描画するだけの場合。
こうなっている。
ここにCWindowオブジェクトで子ウィンドウを配置する。
MainApplication.cppで、
CWindow::RegisterClass(hInstance, _T("Child")); // ウィンドウクラス"Child"の登録.
ウィンドウクラス"Child"を登録。
MainWindow.cppで、
// 子ウィンドウの作成. m_pChild = new CWindow(); // CWindowオブジェクトm_pChild作成. RECT rc; // RECT構造体rc. rc.left = 50; // 左50 rc.right = 150; // 右150 rc.top = 250; // 上250 rc.bottom = 350; // 下350 m_pChild->Create(_T("Child"), _T(""), WS_BORDER | WS_CHILD | WS_VISIBLE | WS_VSCROLL, rc, hwnd, (HMENU)(WM_APP + 1), lpCreateStruct->hInstance); // Createでウィンドウクラス"Child"のウィンドウ作成. // 子ウィンドウの垂直方向スクロールバーの初期化. SCROLLINFO scrVertChild = { 0 }; // 垂直方向スクロール情報scrVertChildを{0}で初期化. scrVertChild.cbSize = sizeof(SCROLLINFO); // sizeofで構造体サイズ指定. scrVertChild.nMin = 0; // 最小値は0. scrVertChild.nMax = 480 - 1; // 最大値は479. scrVertChild.nPage = 100; // ページサイズは100. scrVertChild.nPos = 0; // 現在位置は0. scrVertChild.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; // ページ, レンジ, 位置をセット. SetScrollInfo(m_pChild->m_hWnd, SB_VERT, &scrVertChild, TRUE); // スクロール情報をセット.
垂直方向スクロールバー付きの子ウィンドウを生成し、スクロールバーの初期化もしておく。
Window.cppで、
// 垂直方向スクロールバーイベント時. void CWindow::OnVScroll(UINT nSBCode, UINT nPos) { // 垂直方向スクロールバー情報を取得. SCROLLINFO scrVert = { 0 }; // 垂直方向スクロール情報scrVertを{0}で初期化. scrVert.cbSize = sizeof(SCROLLINFO); // sizeofで構造体サイズ指定. scrVert.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; // ページ, レンジ, 位置を取得. GetScrollInfo(m_hWnd, SB_VERT, &scrVert); // GetScrollInfoでscrVertを取得. // つまみの最大位置を計算. int iMaxPos = scrVert.nMax + 1 - scrVert.nPage; // セットした最大値 + 1が大きさで, そこからページ数を引くと, つまみの最大の位置. // 通知コード処理 switch (nSBCode) { // 通知コードが格納されているので, それで判定する. // 1番上 case SB_TOP: // 1番上にセット. scrVert.nPos = scrVert.nMin; // 現在位置を1番上にセット. break; // 抜ける. // 1番下 case SB_BOTTOM: // 1番下にセット. scrVert.nPos = scrVert.nMax; // 現在位置を1番下にセット. break; // 抜ける. // 1列上 case SB_LINEUP: // 1列上に戻す. if (scrVert.nPos > scrVert.nMin) { // scrVert.nPosがscrVert.nMinより大きい場合. scrVert.nPos--; // 1戻る. } break; // 抜ける. // 1列下 case SB_LINEDOWN: // 1列下に進める. if (scrVert.nPos < iMaxPos) { // scrVert.nPosがiMaxPosより小さい場合. scrVert.nPos++; // 1進む. } break; // 抜ける. // 1ページ上 case SB_PAGEUP: // SB_PAGEUPブロック. { // 1ページ戻る. int after = scrVert.nPos - scrVert.nPage; // 現在位置から1ページ分引く. if (after >= scrVert.nMin) { // 下限を超えてなければ. scrVert.nPos -= scrVert.nPage; // 1ページ分マイナス. } else { // 下限を超えたら. scrVert.nPos = scrVert.nMin; // 最小値に. } } // 抜ける. break; // breakで抜ける. // 1ページ下 case SB_PAGEDOWN: // SB_PAGEDOWNブロック. { // 1ページ進む. int after = scrVert.nPos + scrVert.nPage; // 現在位置から1ページ分足す. if (after <= iMaxPos) { // 上限を超えてなければ. scrVert.nPos += scrVert.nPage; // 1ページ分プラス. } else { // 上限を超えたら. scrVert.nPos = scrVert.nMax; // 最大値に. } } // 抜ける. break; // breakで抜ける. // スクロールつまみが離された時. case SB_THUMBPOSITION: // SB_THUMBPOSITIONブロック. { // 離された位置をセット. int before = scrVert.nPos; // 以前. int after = nPos; // 以後. scrVert.nPos = after; // HIWORD(wParam)に離された位置が格納されているのでscrVert.nPosにセット. } // 抜ける. break; // breakで抜ける. // それ以外. default: // 抜ける. break; // breakで抜ける. } // scrVert.nPosをhwndのSB_VERTにセット. SetScrollInfo(m_hWnd, SB_VERT, &scrVert, TRUE); // SetScrollInfoで現在のscrVert.nPosをm_hWndにセット. InvalidateRect(m_hWnd, NULL, TRUE); // InvalidateRectで無効領域を作成.(NULLなので全体が無効領域.) UpdateWindow(m_hWnd); // UpdateWindowでウィンドウの更新. // WM_VSCROLLが発生した時に来たことを示す. HDC hDC = GetDC(m_hWnd); // hDC取得. static int i = 0; // スタティック変数iを0で初期化. TCHAR tszI[16] = { 0 }; // 文字列バッファtszI(長さ16)を{0}で初期化. _stprintf(tszI, _T("i = %d"), i); // iを文字列tszIに変換. BOOL b = TextOut(hDC, 0, 0, tszI, _tcslen(tszI)); // tszIを描画. i++; // iをインクリメント. ReleaseDC(m_hWnd, hDC); // hDC解放. }
WM_VSCROLLが発生するたびに、iをインクリメントしてそれを描画する。
親ウィンドウの垂直方向スクロールバーを動かすたびに左上のiが増えていく。
子ウィンドウの垂直方向スクロールバーを動かすたびに、子ウィンドウ内の左上のiが増えていく。
今度は、CPictureオブジェクトの子ウィンドウを置いてみる。
MainWindow.cppで、
// ピクチャーコントロールのウィンドウ作成. m_pPicture = new CPicture(); // newでCPictureオブジェクトを作成し, ポインタm_pPictureに格納. /* RECT rc2; // RECT構造体rc2. rc2.left = 150; // 左150 rc2.right = 250; // 右250 rc2.top = 250; // 上250 rc2.bottom = 350; // 下350 */ //m_pPicture->Create(_T(""), WS_BORDER | WS_VSCROLL | SS_BITMAP, rc2, hwnd, (HMENU)(WM_APP + 2), lpCreateStruct->hInstance); // Createでピクチャーコントロールのウィンドウ作成. m_pPicture->Create(_T("CPicutre"), WS_BORDER | WS_VSCROLL | SS_BITMAP, 150, 250, 100, 100, hwnd, (HMENU)(WM_APP + 2), lpCreateStruct->hInstance); // Createでピクチャーコントロールのウィンドウ作成. // ピクチャーの垂直方向スクロールバーの初期化. SCROLLINFO scrVertPicture = { 0 }; // 垂直方向スクロール情報scrVertPictureを{0}で初期化. scrVertPicture.cbSize = sizeof(SCROLLINFO); // sizeofで構造体サイズ指定. scrVertPicture.nMin = 0; // 最小値は0. scrVertPicture.nMax = 480 - 1; // 最大値は479. scrVertPicture.nPage = 100; // ページサイズは100. scrVertPicture.nPos = 0; // 現在位置は0. scrVertPicture.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; // ページ, レンジ, 位置をセット. SetScrollInfo(m_pPicture->m_hWnd, SB_VERT, &scrVertPicture, TRUE); // スクロール情報をセット.
垂直方向スクロールバー付きCPictureウィンドウを配置し、スクロール情報も初期化。
CPictureの場合、親クラスはCStaticCore、そのさらに親クラスはCCsutomControlなので、そちらのWM_VSCROLL処理などを書き換える。
CustomControl.cppで、
// 垂直方向スクロールバーイベント時. void CCustomControl::OnVScroll(UINT nSBCode, UINT nPos) { // 垂直方向スクロールバー情報を取得. SCROLLINFO scrVert = { 0 }; // 垂直方向スクロール情報scrVertを{0}で初期化. scrVert.cbSize = sizeof(SCROLLINFO); // sizeofで構造体サイズ指定. scrVert.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; // ページ, レンジ, 位置を取得. GetScrollInfo(m_hWnd, SB_VERT, &scrVert); // GetScrollInfoでscrVertを取得. // つまみの最大位置を計算. int iMaxPos = scrVert.nMax + 1 - scrVert.nPage; // セットした最大値 + 1が大きさで, そこからページ数を引くと, つまみの最大の位置. // 通知コード処理 switch (nSBCode) { // 通知コードが格納されているので, それで判定する. // 1番上 case SB_TOP: // 1番上にセット. scrVert.nPos = scrVert.nMin; // 現在位置を1番上にセット. break; // 抜ける. // 1番下 case SB_BOTTOM: // 1番下にセット. scrVert.nPos = scrVert.nMax; // 現在位置を1番下にセット. break; // 抜ける. // 1列上 case SB_LINEUP: // 1列上に戻す. if (scrVert.nPos > scrVert.nMin) { // scrVert.nPosがscrVert.nMinより大きい場合. scrVert.nPos--; // 1戻る. } break; // 抜ける. // 1列下 case SB_LINEDOWN: // 1列下に進める. if (scrVert.nPos < iMaxPos) { // scrVert.nPosがiMaxPosより小さい場合. scrVert.nPos++; // 1進む. } break; // 抜ける. // 1ページ上 case SB_PAGEUP: // SB_PAGEUPブロック. { // 1ページ戻る. int after = scrVert.nPos - scrVert.nPage; // 現在位置から1ページ分引く. if (after >= scrVert.nMin) { // 下限を超えてなければ. scrVert.nPos -= scrVert.nPage; // 1ページ分マイナス. } else { // 下限を超えたら. scrVert.nPos = scrVert.nMin; // 最小値に. } } // 抜ける. break; // breakで抜ける. // 1ページ下 case SB_PAGEDOWN: // SB_PAGEDOWNブロック. { // 1ページ進む. int after = scrVert.nPos + scrVert.nPage; // 現在位置から1ページ分足す. if (after <= iMaxPos) { // 上限を超えてなければ. scrVert.nPos += scrVert.nPage; // 1ページ分プラス. } else { // 上限を超えたら. scrVert.nPos = scrVert.nMax; // 最大値に. } } // 抜ける. break; // breakで抜ける. // スクロールつまみが離された時. case SB_THUMBPOSITION: // SB_THUMBPOSITIONブロック. { // 離された位置をセット. int before = scrVert.nPos; // 以前. int after = nPos; // 以後. scrVert.nPos = after; // HIWORD(wParam)に離された位置が格納されているのでscrVert.nPosにセット. } // 抜ける. break; // breakで抜ける. // それ以外. default: // 抜ける. break; // breakで抜ける. } // scrVert.nPosをhwndのSB_VERTにセット. SetScrollInfo(m_hWnd, SB_VERT, &scrVert, TRUE); // SetScrollInfoで現在のscrVert.nPosをm_hWndにセット. InvalidateRect(m_hWnd, NULL, TRUE); // InvalidateRectで無効領域を作成.(NULLなので全体が無効領域.) UpdateWindow(m_hWnd); // UpdateWindowでウィンドウの更新. // WM_VSCROLLが発生した時に来たことを示す. HDC hDC = GetDC(m_hWnd); // hDC取得. static int j = 0; // スタティック変数jを0で初期化. TCHAR tszJ[16] = { 0 }; // 文字列バッファtszJ(長さ16)を{0}で初期化. _stprintf(tszJ, _T("j = %d"), j); // jを文字列tszJに変換. BOOL b = TextOut(hDC, 0, 0, tszJ, _tcslen(tszJ)); // tszJを描画. j++; // jをインクリメント. ReleaseDC(m_hWnd, hDC); // hDC解放. }
こうして、DynamicWindowProcに、
// 垂直スクロールバーがスクロールされた時. case WM_VSCROLL: // 垂直スクロールバーがスクロールされた時.(uMsgがWM_VSCROLLの時.) // WM_VSCROLLブロック { // OnVScrollに任せる. OnVScroll(LOWORD(wParam), HIWORD(wParam)); // OnVScrollに任せる. } // 既定の処理へ向かう. break; // breakで抜けて, 既定の処理(DefWindowProc)へ向かう.
これも追加。
しかし、CPictureの場合、スクロールバーのつまみが動かない。
『動いても元に戻る』ではなく『まったく動かせない』のである。
原因はCStaticCoreつまり"STATIC"ウィンドウクラスのウィンドウだからである。
"STATIC"ウィンドウクラスの場合、WM_VSCROLLのメッセージは来ないし、そもそもスクロールバー自体、配置は出来ても動かせない。
CCustomControlでも他のウィンドウクラスなら動くのか。
CEditCoreつまり"EDIT"ウィンドウクラスのウィンドウで試してみる。
MainWindow.cppに、
// エディットコアコントロールのウィンドウ作成. m_pEdit = new CEditCore(); // newでCEditCoreオブジェクトを作成し, ポインタをm_pEditに格納. RECT rc3; // RECT構造体rc3. rc3.left = 250; // 左250 rc3.right = 350; // 右350 rc3.top = 250; // 上250 rc3.bottom = 350; // 下350 m_pEdit->Create(_T("Edit1"), WS_BORDER | WS_VSCROLL, rc3, hwnd, (HMENU)(WM_APP + 3), lpCreateStruct->hInstance); // Createでエディットコアコントロール"Edit1"のウィンドウ作成. // エディットコアの垂直方向スクロールバーの初期化. SCROLLINFO scrVertEdit = { 0 }; // 垂直方向スクロール情報scrVertEditを{0}で初期化. scrVertEdit.cbSize = sizeof(SCROLLINFO); // sizeofで構造体サイズ指定. scrVertEdit.nMin = 0; // 最小値は0. scrVertEdit.nMax = 480 - 1; // 最大値は479. scrVertEdit.nPage = 100; // ページサイズは100. scrVertEdit.nPos = 0; // 現在位置は0. scrVertEdit.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; // ページ, レンジ, 位置をセット. SetScrollInfo(m_pEdit->m_hWnd, SB_VERT, &scrVertEdit, TRUE); // スクロール情報をセット.
垂直方向スクロールバー付きCEditCoreウィンドウを配置。
ちょっと面倒だが、CEditCoreの中でリターン連打を繰り返して、スクロールバーのつまみを表示する。
スクロールバーのつまみを動かすとjが増えるので、WM_VSCROLLが来ているのがわかる。
ということで、CPictureはCStaticCoreベースで作ると、スクロールバー処理を実装できない。
そこで、CPictureはCUserControlベースに変更する。
Picture.hは、
継承元をCStaticCoreからCUserControlに。
Picture.cppは、
そして、
こうする。
MainApplication.cppで、
今の段階では、CPictureでCreateを実装してないので、CUserControlのCreateが呼ばれる。
なのでウィンドウクラスもCUserControl::RegisterClassで登録する。
これで仮ではあるが、CPictureでもスクロールバーが効くようになった。
Test/winapi/CPicture/CPicture_1/src/CPicture at master · bg1bgst333/Test · GitHub