CPicture(スクロール処理実装調査)

現行の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が増えていく

親ウィンドウの垂直方向スクロールバーを動かすたびに左上のiが増えていく。

子ウィンドウの垂直方向スクロールバーを動かすたびに、子ウィンドウ内の左上の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の場合、スクロールバーのつまみが動かない。
しかし、CPictureの場合、スクロールバーのつまみが動かない。

しかし、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の中でリターン連打を繰り返して、スクロールバーのつまみを表示する。
ちょっと面倒だが、CEditCoreの中でリターン連打を繰り返して、スクロールバーのつまみを表示する。

ちょっと面倒だが、CEditCoreの中でリターン連打を繰り返して、スクロールバーのつまみを表示する。

スクロールバーのつまみを動かすとjが増えるので、WM_VSCROLLが来ているのがわかる。
スクロールバーのつまみを動かすとjが増えるので、WM_VSCROLLが来ているのがわかる。

スクロールバーのつまみを動かすとjが増えるので、WM_VSCROLLが来ているのがわかる。
ということで、CPictureはCStaticCoreベースで作ると、スクロールバー処理を実装できない。
そこで、CPictureはCUserControlベースに変更する。
Picture.hは、

継承元をCStaticCoreからCUserControlに。
Picture.cppは、

そして、

こうする。
MainApplication.cppで、

今の段階では、CPictureでCreateを実装してないので、CUserControlのCreateが呼ばれる。
なのでウィンドウクラスもCUserControl::RegisterClassで登録する。

これで仮ではあるが、CPictureでもスクロールバーが効くようになった。
これで仮ではあるが、CPictureでもスクロールバーが効くようになった。

これで仮ではあるが、CPictureでもスクロールバーが効くようになった。

Test/winapi/CPicture/CPicture_1/src/CPicture at master · bg1bgst333/Test · GitHub