現行のCPictureにスクロール機能を実装しようと調査したら、いろいろ問題があったので、書いていく。
まず、メニュー付きのウィンドウにスクロールバー付けて、水平方向スクロールバーの位置、垂直方向スクロールバーの位置をOnPaint時に描画するだけの場合。
こうなっている。
ここにCWindowオブジェクトで子ウィンドウを配置する。
MainApplication.cppで、
CWindow::RegisterClass(hInstance, _T("Child"));
ウィンドウクラス"Child"を登録。
MainWindow.cppで、
m_pChild = new CWindow();
RECT rc;
rc.left = 50;
rc.right = 150;
rc.top = 250;
rc.bottom = 350;
m_pChild->Create(_T("Child"), _T(""), WS_BORDER | WS_CHILD | WS_VISIBLE | WS_VSCROLL, rc, hwnd, (HMENU)(WM_APP + 1), lpCreateStruct->hInstance);
SCROLLINFO scrVertChild = { 0 };
scrVertChild.cbSize = sizeof(SCROLLINFO);
scrVertChild.nMin = 0;
scrVertChild.nMax = 480 - 1;
scrVertChild.nPage = 100;
scrVertChild.nPos = 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.cbSize = sizeof(SCROLLINFO);
scrVert.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
GetScrollInfo(m_hWnd, SB_VERT, &scrVert);
int iMaxPos = scrVert.nMax + 1 - scrVert.nPage;
switch (nSBCode) {
case SB_TOP:
scrVert.nPos = scrVert.nMin;
break;
case SB_BOTTOM:
scrVert.nPos = scrVert.nMax;
break;
case SB_LINEUP:
if (scrVert.nPos > scrVert.nMin) {
scrVert.nPos--;
}
break;
case SB_LINEDOWN:
if (scrVert.nPos < iMaxPos) {
scrVert.nPos++;
}
break;
case SB_PAGEUP:
{
int after = scrVert.nPos - scrVert.nPage;
if (after >= scrVert.nMin) {
scrVert.nPos -= scrVert.nPage;
}
else {
scrVert.nPos = scrVert.nMin;
}
}
break;
case SB_PAGEDOWN:
{
int after = scrVert.nPos + scrVert.nPage;
if (after <= iMaxPos) {
scrVert.nPos += scrVert.nPage;
}
else {
scrVert.nPos = scrVert.nMax;
}
}
break;
case SB_THUMBPOSITION:
{
int before = scrVert.nPos;
int after = nPos;
scrVert.nPos = after;
}
break;
default:
break;
}
SetScrollInfo(m_hWnd, SB_VERT, &scrVert, TRUE);
InvalidateRect(m_hWnd, NULL, TRUE);
UpdateWindow(m_hWnd);
HDC hDC = GetDC(m_hWnd);
static int i = 0;
TCHAR tszI[16] = { 0 };
_stprintf(tszI, _T("i = %d"), i);
BOOL b = TextOut(hDC, 0, 0, tszI, _tcslen(tszI));
i++;
ReleaseDC(m_hWnd, hDC);
}
WM_VSCROLLが発生するたびに、iをインクリメントしてそれを描画する。
親ウィンドウの垂直方向スクロールバーを動かすたびに左上のiが増えていく。
子ウィンドウの垂直方向スクロールバーを動かすたびに、子ウィンドウ内の左上のiが増えていく。
今度は、CPictureオブジェクトの子ウィンドウを置いてみる。
MainWindow.cppで、
m_pPicture = new CPicture();
m_pPicture->Create(_T("CPicutre"), WS_BORDER | WS_VSCROLL | SS_BITMAP, 150, 250, 100, 100, hwnd, (HMENU)(WM_APP + 2), lpCreateStruct->hInstance);
SCROLLINFO scrVertPicture = { 0 };
scrVertPicture.cbSize = sizeof(SCROLLINFO);
scrVertPicture.nMin = 0;
scrVertPicture.nMax = 480 - 1;
scrVertPicture.nPage = 100;
scrVertPicture.nPos = 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.cbSize = sizeof(SCROLLINFO);
scrVert.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
GetScrollInfo(m_hWnd, SB_VERT, &scrVert);
int iMaxPos = scrVert.nMax + 1 - scrVert.nPage;
switch (nSBCode) {
case SB_TOP:
scrVert.nPos = scrVert.nMin;
break;
case SB_BOTTOM:
scrVert.nPos = scrVert.nMax;
break;
case SB_LINEUP:
if (scrVert.nPos > scrVert.nMin) {
scrVert.nPos--;
}
break;
case SB_LINEDOWN:
if (scrVert.nPos < iMaxPos) {
scrVert.nPos++;
}
break;
case SB_PAGEUP:
{
int after = scrVert.nPos - scrVert.nPage;
if (after >= scrVert.nMin) {
scrVert.nPos -= scrVert.nPage;
}
else {
scrVert.nPos = scrVert.nMin;
}
}
break;
case SB_PAGEDOWN:
{
int after = scrVert.nPos + scrVert.nPage;
if (after <= iMaxPos) {
scrVert.nPos += scrVert.nPage;
}
else {
scrVert.nPos = scrVert.nMax;
}
}
break;
case SB_THUMBPOSITION:
{
int before = scrVert.nPos;
int after = nPos;
scrVert.nPos = after;
}
break;
default:
break;
}
SetScrollInfo(m_hWnd, SB_VERT, &scrVert, TRUE);
InvalidateRect(m_hWnd, NULL, TRUE);
UpdateWindow(m_hWnd);
HDC hDC = GetDC(m_hWnd);
static int j = 0;
TCHAR tszJ[16] = { 0 };
_stprintf(tszJ, _T("j = %d"), j);
BOOL b = TextOut(hDC, 0, 0, tszJ, _tcslen(tszJ));
j++;
ReleaseDC(m_hWnd, hDC);
}
こうして、DynamicWindowProcに、
case WM_VSCROLL:
{
OnVScroll(LOWORD(wParam), HIWORD(wParam));
}
break;
これも追加。
しかし、CPictureの場合、スクロールバーのつまみが動かない。
『動いても元に戻る』ではなく『まったく動かせない』のである。
原因はCStaticCoreつまり"STATIC"ウィンドウクラスのウィンドウだからである。
"STATIC"ウィンドウクラスの場合、WM_VSCROLLのメッセージは来ないし、そもそもスクロールバー自体、配置は出来ても動かせない。
CCustomControlでも他のウィンドウクラスなら動くのか。
CEditCoreつまり"EDIT"ウィンドウクラスのウィンドウで試してみる。
MainWindow.cppに、
m_pEdit = new CEditCore();
RECT rc3;
rc3.left = 250;
rc3.right = 350;
rc3.top = 250;
rc3.bottom = 350;
m_pEdit->Create(_T("Edit1"), WS_BORDER | WS_VSCROLL, rc3, hwnd, (HMENU)(WM_APP + 3), lpCreateStruct->hInstance);
SCROLLINFO scrVertEdit = { 0 };
scrVertEdit.cbSize = sizeof(SCROLLINFO);
scrVertEdit.nMin = 0;
scrVertEdit.nMax = 480 - 1;
scrVertEdit.nPage = 100;
scrVertEdit.nPos = 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