Thumb : 스크롤바의 중간에 있으며, 마우스로 클릭하여, 스크롤이 가능한 버튼.
스크롤 되는 페이지의 크기에 따라, 크기가 변한다.
typedef struct tagSCROLLINFO {
UINT cbSize;
UINT fMask;
int nMin;//스크롤되는 최소값
int nMax;//스크롤되는 최대값
UINT nPage;//1페이지의 크기
int nPos;//현재위치
int nTrackPos;//마으스클릭한 상태로 이동중일때, 현재 위치. 읽기만 가능.
} SCROLLINFO, *LPCSCROLLINFO;
윈도우 좌표계는 논리좌표와 디바이스 좌표 2가지가 있고,
논리좌표는 스크롤 개념을 포함한 상대좌표를 말한다.
디바이스 좌표는 모니터나 윈도우의 좌측상단이 (0,0)이고 우측하단이 (right,bottom)인 절대 좌표이다.
화면은 픽셀단위좌표계를 사용할 수도 있고, cm, mm 같은 실세계좌표계를 사용할 수 도 있다.
cm,mm 좌표계는 모니터의 종류, 해상도, 크기 정보로, 실제 모니터에 표시되는 크기를 계산해주는것이다.
(가로/세로 비율만 곱해주면 된다.)
(따라서, 모니터의 종류나 해상도 등의 정보가 없을때는 안맞을것이다.)
스크롤바 정보를 설정하는 함수
int SetScrollInfo(
_In_ HWND hwnd,
_In_ int fnBar,//SB_CTL, SB_HORZ, SB_VERT
_In_ LPCSCROLLINFO lpsi,
_In_ BOOL fRedraw
);
BOOL SetScrollRange( HWND hWnd, int nBar, int nMinPos, int nMaxPos, BOOL bRedraw );
int SetScrollPos( HWND hWnd, int nBar, int nPos, BOOL bRedraw );
윈도우 메세지
WM_HSCROLL, WM_VSCROLL
lParam=스크롤바 윈도우 핸들 HWND
HIWORD(wParam) : 현재위치(16비트 값이므로, 32비트 범위는 사용 불가.)
LOWORD(wParam) : SB_LINExxxx, SB_PAGExxxx
수평일때, SB_LINELEFT(왼쪽 화살표), SB_LINERIGHT(오른쪽 화살표), SB_PAGELEFT, SB_PAGERIGHT,
SB_THUMBPOSITION(마우스 드래그 완료), SB_THUMBTRACK(마우스 드래그 중)
수직일때, SB_LINEUP(윗쪽 화살표), SB_LINEDOWN(아랫쪽 화살표), SB_PAGEUP, SB_PAGEDOWN
예제] 참고용으로만 사용.
HWND hscroll_H;
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
HWND hscroll;
switch(msg)
{
case WM_CREATE:
hscroll_H=CreateWindow("scrollbar",0,WS_CHILD | WS_VISIBLE | SBS_HORZ,
10,10,200,20,
hWnd,(HMENU)ID_SCRRED,g_hInst,0);
SetScrollRange(hscroll_H,SB_CTL,0,255,TRUE);
SetScrollPos(hscroll_H,SB_CTL,0,TRUE);
return 0;
case WM_HSCROLL:
hscroll=(HWND)lParam;
switch(hscroll)
{
case SB_LINELEFT:
TempPos=max(0,TempPos-1);
break;
case SB_LINERIGHT:
TempPos=min(255,TempPos+1);
break;
case SB_PAGELEFT:
TempPos=max(0,TempPos-10);
break;
case SB_PAGERIGHT:
TempPos=min(255,TempPos+10);
break;
case SB_THUMBTRACK:
TempPos=HIWORD(wParam);
break;
}
SetScrollPos((HWND)lParam,SB_CTL,TempPos,TRUE);
InvalidateRect(hWnd,0,FALSE);
return 0;
}
return(DefWindowProc(hWnd,msg,wParam,lParam));
}
MFC 코드
CScrollView는 사용하기는 편하지만, Win32로 작성할때는 골칫거리다.
void CScrollView::SetScrollSizes(int nMapMode, SIZE sizeTotal,
const SIZE& sizePage, const SIZE& sizeLine)
{
ASSERT(sizeTotal.cx >= 0 && sizeTotal.cy >= 0);
ASSERT(nMapMode > 0);
ASSERT(nMapMode != MM_ISOTROPIC && nMapMode != MM_ANISOTROPIC);
int nOldMapMode = m_nMapMode;
m_nMapMode = nMapMode;
m_totalLog = sizeTotal;
//BLOCK: convert logical coordinate space to device coordinates
{
CWindowDC dc(NULL);
ASSERT(m_nMapMode > 0);
dc.SetMapMode(m_nMapMode);
// total size
m_totalDev = m_totalLog;
dc.LPtoDP((LPPOINT)&m_totalDev);
m_pageDev = sizePage;
dc.LPtoDP((LPPOINT)&m_pageDev);
m_lineDev = sizeLine;
dc.LPtoDP((LPPOINT)&m_lineDev);
if (m_totalDev.cy < 0)
m_totalDev.cy = -m_totalDev.cy;
if (m_pageDev.cy < 0)
m_pageDev.cy = -m_pageDev.cy;
if (m_lineDev.cy < 0)
m_lineDev.cy = -m_lineDev.cy;
} // release DC here
// now adjust device specific sizes
ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);
if (m_pageDev.cx == 0)
m_pageDev.cx = m_totalDev.cx / 10;
if (m_pageDev.cy == 0)
m_pageDev.cy = m_totalDev.cy / 10;
if (m_lineDev.cx == 0)
m_lineDev.cx = m_pageDev.cx / 10;
if (m_lineDev.cy == 0)
m_lineDev.cy = m_pageDev.cy / 10;
if (m_hWnd != NULL)
{
// window has been created, invalidate now
UpdateBars();
if (nOldMapMode != m_nMapMode)
Invalidate(TRUE);
}
}
void CScrollView::UpdateBars()
{
// UpdateBars may cause window to be resized - ignore those resizings
if (m_bInsideUpdate)
return; // Do not allow recursive calls
// Lock out recursion
m_bInsideUpdate = TRUE;
// update the horizontal to reflect reality
// NOTE: turning on/off the scrollbars will cause 'OnSize' callbacks
ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);
CRect rectClient;
BOOL bCalcClient = TRUE;
// allow parent to do inside-out layout first
CWnd* pParentWnd = GetParent();
if (pParentWnd != NULL)
{
// if parent window responds to this message, use just
// client area for scroll bar calc -- not "true" client area
if ((BOOL)pParentWnd->SendMessage(WM_RECALCPARENT, 0,
(LPARAM)(LPCRECT)&rectClient) != 0)
{
// use rectClient instead of GetTrueClientSize for
// client size calculation.
bCalcClient = FALSE;
}
}
CSize sizeClient;
CSize sizeSb;
if (bCalcClient)
{
// get client rect
if (!GetTrueClientSize(sizeClient, sizeSb))
{
// no room for scroll bars (common for zero sized elements)
CRect rect;
GetClientRect(&rect);
if (rect.right > 0 && rect.bottom > 0)
{
// if entire client area is not invisible, assume we have
// control over our scrollbars
EnableScrollBarCtrl(SB_BOTH, FALSE);
}
m_bInsideUpdate = FALSE;
return;
}
}
else
{
// let parent window determine the "client" rect
GetScrollBarSizes(sizeSb);
sizeClient.cx = rectClient.right - rectClient.left;
sizeClient.cy = rectClient.bottom - rectClient.top;
}
// enough room to add scrollbars
CSize sizeRange;
CPoint ptMove;
CSize needSb;
// get the current scroll bar state given the true client area
GetScrollBarState(sizeClient, needSb, sizeRange, ptMove, bCalcClient);
if (needSb.cx)
sizeClient.cy -= sizeSb.cy;
if (needSb.cy)
sizeClient.cx -= sizeSb.cx;
// first scroll the window as needed
ScrollToDevicePosition(ptMove); // will set the scroll bar positions too
// this structure needed to update the scrollbar page range
SCROLLINFO info;
info.fMask = SIF_PAGE|SIF_RANGE;
info.nMin = 0;
// now update the bars as appropriate
EnableScrollBarCtrl(SB_HORZ, needSb.cx);
if (needSb.cx)
{
info.nPage = sizeClient.cx;
info.nMax = m_totalDev.cx-1;
if (!SetScrollInfo(SB_HORZ, &info, TRUE))
SetScrollRange(SB_HORZ, 0, sizeRange.cx, TRUE);
}
EnableScrollBarCtrl(SB_VERT, needSb.cy);
if (needSb.cy)
{
info.nPage = sizeClient.cy;
info.nMax = m_totalDev.cy-1;
if (!SetScrollInfo(SB_VERT, &info, TRUE))
SetScrollRange(SB_VERT, 0, sizeRange.cy, TRUE);
}
// remove recursion lockout
m_bInsideUpdate = FALSE;
}