[WinAPI] CScrollBar 사용법 – 2-2. 횡스크롤 만들기

안녕하세요. 저번 글까지는 스크롤을 만드는 것까지 했는데, 이번 글에서는 약간의 설명과 코드를 바로 첨부해 드리겠습니다.

코드를 하기 앞서 dc와 dcTemp, 그리고 스크롤로 표현 하는 방법을 설명해드리겠습니다.
최종적으로 사용자가 보는 dc화면이 있습니다.

이 dc에서 CRect rect를 이용하여 크기를 구합니다.
그리고 가로 크기는 10000, 세로는 dc 와 같은 dcTemp를 생성해줍니다.

그리고 dcTemp에 0, 100, 200, … , 9900 같이, 100 위치마다 해당 값을 표시해줍니다.

어제 코드에 보면 스크롤을 설정할 때 10000이라는 크기를 줬습니다. dcTemp크기와 같은 값으로 설정해야 계산하기 편합니다.

SCROLLINFO scrInfo;
// ...
scrInfo.nMax = 10000;	// 스크롤 최대값
// ...
m_ctlHScroll.SetScrollInfo( &scrInfo ); // 스크롤바 정보 설정

스크롤도 0~10000의 값을 가지고 있고 화면 크기도 0~10000이니까 스크롤의 위치에 따라 dcTemp에서 dc로 BitBlt 하시면 됩니다.

이제 OnPaint에 코딩해보도록 하겠습니다. 아래와 같이 입력해주세요.

void CDisplay::OnPaint()
{
	CPaintDC dc( this ); // device context for painting
						 // TODO: 여기에 메시지 처리기 코드를 추가합니다.
						 // 그리기 메시지에 대해서는 CWnd::OnPaint()을(를) 호출하지 마십시오.

	// 크기 구하기
	CRect rect;
	GetClientRect( &rect );
	
	// 화면 그리기
	CDC dcTemp;
	dcTemp.CreateCompatibleDC( &dc );
	HBITMAP hbmp = ::CreateCompatibleBitmap( dc, 10000, rect.Height() - 20 ); // 가로 10000 크기로 생성
	HBITMAP hbmpOld = (HBITMAP)dcTemp.SelectObject( hbmp );

	// 100마다 텍스트 출력
	dcTemp.PatBlt( 0, 0, 10000, rect.Height(), WHITENESS );
	for ( int iX = 0 ; iX < 10000 ; iX += 100 )
	{
		CString sX;
		sX.Format( _T( "%d" ), iX );
		dcTemp.TextOut( iX, (rect.Height() / 2), sX );
	}

	SCROLLINFO scrInfo;
	int iSrcX = 0;
	if ( NULL == m_ctlHScroll.GetSafeHwnd() )
	{
		CRect rectHScroll;
		rectHScroll.SetRect( rect.left, rect.top, rect.right, rect.bottom );
		m_ctlHScroll.Create(
			WS_CHILD | WS_VISIBLE | SBS_HORZ | SBS_BOTTOMALIGN // 속성
			, rectHScroll // 위치
			, this // 부모 윈도우
			, 0 // 스크롤 막대의 컨트롤 ID입니다
		);
		m_ctlHScroll.ShowScrollBar( TRUE );

		scrInfo.cbSize = sizeof( scrInfo );
		scrInfo.fMask = SIF_ALL;
		scrInfo.nMin = 0; // 스크롤 최소값
		scrInfo.nMax = 10000;	// 스크롤 최대값
		scrInfo.nPage = rect.Width();	// 페이지 번호
		scrInfo.nTrackPos = 0; // 드래깅 상태의 트랙바 위치
		scrInfo.nPos = 0; // 트랙바 위치
		m_ctlHScroll.SetScrollRange( scrInfo.nMin, scrInfo.nMax ); // 범위 설정
		m_ctlHScroll.SetScrollPos( scrInfo.nPos ); // 위치 설정
		m_ctlHScroll.SetScrollInfo( &scrInfo ); // 스크롤바 정보 설정
	}
	else
	{
		if ( FALSE != m_ctlHScroll.GetScrollInfo( &scrInfo ) )
		{
			iSrcX = scrInfo.nPos; // 현재 스크롤 위치 받아옴
		}
	}


	dc.BitBlt( 0, 0, rect.Width(), rect.Height(), &dcTemp, iSrcX, 0, SRCCOPY ); // 더블 버퍼링

	dcTemp.SelectObject( hbmpOld );
	::DeleteObject( hbmp );

	dcTemp.DeleteDC();
}

OnEraseBkgnd에 아래와 같이 코딩해주세요. return TRUE;는 덮어씌우기를 하는겁니다.

BOOL CDisplay::OnEraseBkgnd( CDC* pDC )
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.

	return TRUE;
}

HScroll 함수 OnHScroll에 아래와 같이 코딩합니다.

void CDisplay::OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar )
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.

	SCROLLINFO scrInfo;
	memset( &scrInfo, 0, sizeof(scrInfo) );
	if ( FALSE != m_ctlHScroll.GetScrollInfo( &scrInfo ) )
	{
		switch ( nSBCode )
		{
			case SB_LINERIGHT:
				scrInfo.nPos+=100;
				break;
			case SB_LINELEFT:
				scrInfo.nPos-=100;
				break;
			case SB_THUMBTRACK:
			case SB_PAGEDOWN:
				scrInfo.nPos = nPos;
				break;

		}

		m_ctlHScroll.SetScrollInfo( &scrInfo );

		this->Invalidate();
	}

	CWnd::OnHScroll( nSBCode, nPos, pScrollBar );
}

마우스 휠 이벤트가 발생했을 때 처리하는 함수 OnMouseWheel에 아래와 같이 코딩합니다.

BOOL CDisplay::OnMouseWheel( UINT nFlags, short zDelta, CPoint pt )
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if ( zDelta > 0 )
	{
		SendMessage( WM_HSCROLL, SB_LINELEFT, NULL );
	}
	else
	{
		SendMessage( WM_HSCROLL, SB_LINERIGHT, NULL );
	}

	return CWnd::OnMouseWheel( nFlags, zDelta, pt );
}

간혹 마우스에 아래와 같이 횡스크롤이 있는데, 이 스크롤에 대한 처리 함수 OnMouseHWheel에 아래와 같이 코딩합니다.

void CDisplay::OnMouseHWheel( UINT nFlags, short zDelta, CPoint pt )
{
	// 이 기능을 사용하려면 Windows Vista 이상이 있어야 합니다.
	// _WIN32_WINNT 기호는 0x0600보다 크거나 같아야 합니다.
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if ( zDelta > 0 )
	{
		SendMessage( WM_HSCROLL, SB_LINERIGHT, NULL );
	}
	else
	{
		SendMessage( WM_HSCROLL, SB_LINELEFT, NULL );
	}

	CWnd::OnMouseHWheel( nFlags, zDelta, pt );
}

실행 시켜 확인해 봅니다.

이상으로 CScrollbar를 이용하여 사용자 정의 클래스를 만들어봤습니다.