스크롤바를 사용하다 보면 32767 unsigned short의 양의값을 넘어가는 경우가
간혹 발생할 수 있다.

그래픽 디자인 프로그램과 같은 것들을 설계할 때야 줌 팩터등을 이용하여 따로 처리하니
별 문제가 없겠지만 간단한 먼가를 만들 때 100000의 값을 써야한다면 먼가 설계를 해야하나?

그렇다면 우선 MSDN을 살펴보자.

CScrollBar::SetScrollRange
Set nMinPos and nMaxPos to 0 to hide standard scroll bars.
Do not call this function to hide a scroll bar while processing a scroll-bar notification message.

If a call to SetScrollRange immediately follows a call to the SetScrollPos member function, set bRedraw in SetScrollPos to 0 to prevent the scroll bar from being redrawn twice.

The difference between the values specified by nMinPos and nMaxPos

must not be greater
than 32,767

. The default range for a scroll-bar control is empty

일단 스크롤바의 SetScrollRange의 인자는 int 값인데, 어쩌구 저쩌구한 이유로 32767이 최대
라고 한다. 인자가 int라면 그렇다면 먼가를 처리하면 최대 양수 20억까지 쓸수 있다는 야근데..



SetScrollRange Function Win32API

 

However, because the SetScrollInfo, SetScrollPos, SetScrollRange, GetScrollInfo,
GetScrollPos, and GetScrollRange

functions support 32-bit scroll bar position data,

there is a way to circumvent the 16-bit barrier of the WM_HSCROLL and WM_VSCROLL


messages. See GetScrollInfo for a description of the technique.

MSDN에서 API의 도움말을 살펴보았다. Remark를 자세히 보다보니 32비트를 쓸 수 있는데
16비트 베리어에 쌓여 있단다.. 그렇다면 위의 두 메시지에서 오는 UINT nPos 가 정수
범위임에도 불구하고 쇼트크기를 가진다면 저걸 않쓰면 가능하지 않을까...


(16비트 베리어라는 의미는 내부적으로 먼가를 처리할 때 아무생각 없이 16비트를 썼다는
내용을 아주 점잔하게 표시한 것으로 추측된다.. 즉, 16비트 머신의 잔제가 남았다는 거겠죠.)

--------------------------------------------------------------------------------------

일단 여기까지 살펴보면 32비트를 쓸 수 있는데도 불구하고, 메시지 WM_VSCROLL과
WM_HSCROLL의 UINT nPOS는 16비트 최대값을 가진다고 결론을 내릴 수있다.
즉, 저걸 않쓰면 된다는 이야기겠고..

그럼 저걸 않쓰고 프로그램을 구성하면 되겠죠..
이전 장에 있던 샘플은 0 부터 100까지 크기를 가지는 샘플이었습니다.
이걸 그럼 위의 지식을 기반으로 0부터 1000000 크기를 가지는 샘플로 바꿔보죠.

#define SCROLL_MIN   0
#define SCROLL_MAX 

1000000


마이스크롤.SetScrollRange(SCROLL_MIN, SCROLL_MAX);
마이스크롤.SetScrollPos(50);
마이스크롤.EnableScrollBar(ESB_ENABLE_BOTH);

요렇게 설정을 해놓고 WM_VSCROLL 핸들러를 다음과 같이 수정해봅니다.
어짜피 이전 샘플에서도 nPos를 쓰는데는 스크롤바를 트래킹할 때만 썻으므로
이 샘플에서도 바뀌는 부분은 거기 밖에는 없습니다.

void CSampleDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    UINT nCurPos;
    if(pScrollBar->GetDlgCtrlID() == IDC_SCROLLBAR5)
    {
        nCurPos = pScrollBar->GetScrollPos();
        switch(nSBCode)
        {
       

// 중간 생략.. 원본과 다른바 없음.

       case SB_THUMBPOSITION:
       {
          SCROLLINFO si = {0};
          si.cbSize = sizeof(SCROLLINFO);
          si.fMask = SIF_TRACKPOS;
          ::GetScrollInfo(pScrollBar->m_hWnd, SB_CTL, &si);

          // 메시지의 nPos 대신에 그냥 트랙포스를 가져다 쓴다.


          pScrollBar->SetScrollPos(si.nTrackPos);
       }
       break;

       case SB_THUMBTRACK:
       {
          SCROLLINFO si = {0};
          si.cbSize = sizeof(SCROLLINFO);
          si.fMask = SIF_TRACKPOS;
          ::GetScrollInfo(pScrollBar->m_hWnd, SB_CTL, &si);

          // 메시지의 nPos 대신에 그냥 트랙포스를 가져다 쓴다.


          pScrollBar->SetScrollPos(si.nTrackPos);
       }
       break;
      // 아래도 생략.. 원본과 같음
    }
 
    CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}

헉.. 그냥 않쓰면 되네.. ㅜㅜ;;;
그럼 수고하세요..

scroll02.zip
0.03MB

 

 

지금까지 진행한 내용을 바탕으로 간단한 색상보기 샘플을 제작해 보겠습니다.
실행한 모양은 아래 처럼 됩니다.

설정 조건
1스크롤바를 3 만들고 모든 범위는 0 - 255 까지이다.
2각각 좌측부터 RedGreen, Blue 삼원색을 제어한다.
3오른쪽 사각영역에 주어진 RGB 컬러대로 색칠을 해준다.

-----------------------------------------------------------------------------------
허데 파일의 내용.. 발췌

// Dialog Data
    
//{{AFX_DATA(CSampleDlg)
    
enum { IDD = IDD_SAMPLE_DIALOG };
    
CScrollBar m_scroll3;
    
CScrollBar m_scroll2;
    
CScrollBar m_scroll;
    
//}}AFX_DATA

    
// 색칠할 사각형 영역
    
CRect m_rect;
    
// 색상값
    
COLORREF m_rgb;
    
void ChangeColorBox(CScrollBarpBarUINT nPos);

-----------------------------------------------------------------------------------
소스 파일의 내용.. 발췌

// RGB 단위 값의 최소/최대
#define SCROLL_MIN  0
#define SCROLL_MAX  255

// COLORREF 부분값을 변경하기 위한 제작된 매크로
#define SetRValue(rgbr)      ((rgb&=0xFFFFFF00)|=(BYTE)r)
#define SetGValue(rgbg)      ((rgb&=0xFFFF00FF)|=((BYTE)g)<<8)
#define SetBValue(rgbb)      ((rgb&=0xFF00FFFF)|=((BYTE)b)<<16)

BOOL CSampleDlg::OnInitDialog()
{
    
CDialog::OnInitDialog();

    
// 스크롤바와 연결된 컨트롤 3개에 대하여 모두 기본값들을 설정한다.
    
m_scroll.SetScrollRange(SCROLL_MINSCROLL_MAX);
    
m_scroll.SetScrollPos(0);
    
m_scroll.EnableScrollBar(ESB_ENABLE_BOTH);

    
m_scroll2.SetScrollRange(SCROLL_MINSCROLL_MAX);
    
m_scroll2.SetScrollPos(0);
    
m_scroll2.EnableScrollBar(ESB_ENABLE_BOTH);

    
m_scroll3.SetScrollRange(SCROLL_MINSCROLL_MAX);
    
m_scroll3.SetScrollPos(0);
    
m_scroll3.EnableScrollBar(ESB_ENABLE_BOTH);

    
// 화면에 그릴 영역의 사각 크기를 구한다.
    
GetDlgItem(IDC_STATIC1)->GetWindowRect(m_rect);
    
ScreenToClient(m_rect);

    
// 기본 색상은 검은색이다.
    
m_rgb = RGB(0,0,0);

    
return TRUE;
}

// 정해진 색상으로 지정된 위치에 색칠한다.
void CSampleDlg::OnPaint()
{
    
CPaintDC dc(this);
    
dc.FillSolidRect(m_rectm_rgb);
}

// 스크롤 이벤트 핸들러
void CSampleDlg::OnVScroll(UINT nSBCodeUINT nPosCScrollBarpScrollBar)
{
    
if(!pScrollBar || !pScrollBar->m_hWnd)
        
return;

    
UINT nCurPos = pScrollBar->GetScrollPos();
    
switch(nSBCode)
    {
    
case SB_BOTTOM:
        
pScrollBar->SetScrollPos(SCROLL_MAX);
        
break;

    
// 중간 생략...

     
case SB_TOP:
        
pScrollBar->SetScrollPos(SCROLL_MIN);
        
break;
    }

    
// 색상값에 변경이 있으면 다시 색칠을 해야지..
   
ChangeColorBox(pScrollBarnCurPos);

    
CDialog::OnVScroll(nSBCodenPospScrollBar);
}

// 색상값이 변경되면 어떤 스크롤바가 값이 얼마인지 계산을 해서
// RGB 다시 계산한  색칠한다.
void CSampleDlg::ChangeColorBox(CScrollBarpBarUINT nPos)
{
    
UINT nID = pBar->GetDlgCtrlID();
    
switch(nID)
    {
    
case IDC_SCROLLBAR5:
        
SetRValue(m_rgbnPos);
        
break;
    
case IDC_SCROLLBAR6:
        
SetGValue(m_rgbnPos);
        
break;
    
case IDC_SCROLLBAR7:
        
SetBValue(m_rgbnPos);
        
break;
    }

    
CClientDC dc(this);
    
dc.FillSolidRect(m_rectm_rgb);
}

93.zip
0.03MB

이렇게 간단하게 나마 스크롤바에 대하여 공부해 보았습니다.
실상 알고 보면 크게 어렵거나 한 부분은 거의 없습니다. 자료가 부족할 뿐 ^^;

다음에는 리스트 컨트를을 진행해 보도록 하겠습니다.
즐거운 한주 되세요.. ^^;

스크롤바에서 정보를 설정하거나 가져올  사용되는 구조체가 SCROLLINFO 이다.
winuser.h  정의되어 있으며 구조는 다음과 같다.

typedef struct tagSCROLLINFO
{
    
UINT   cbSize;
    
UINT    fMask;
    
int       nMin;
    
int       nMax;
    
UINT    nPage;
    
int       nPos;
    
int        nTrackPos;
}   
SCROLLINFOFAR *LPSCROLLINFO;
typedef SCROLLINFO CONST FAR *LPCSCROLLINFO;

 구조체는 GetScrollInfo / SetScrollInfo 함수를 이용하여 현재 스크롤바의 상태나 정보를
얻어오거나 설정할  사용된다.

cbSize     :   구조체의 크기
fMask       : 스크롤바의 속성을 결정짓는 플래그
   
SIF_ALL                         모든 속성의 조합
   
SIF_DISABLENOSCROLL  스클로바 전체 비활성화
   
SIF_PAGE                       페이지 사이즈
   
SIF_POS                         스크롤 박스의 위치
   
SIF_RANGE                     스크롤 범위의 최소/최대
   
SIF_TRACKPOS               드래깅 상태의 스크롤박스 현재 위치
nMin         : 스크롤범위의 최소값
nMax        :  스크롤 범위의 최대값
nPage       : 전체 스크롤바의 크기에 대한 이동막대기(Thumb) 절대 크기값
nPos         : 스크롤 박스의 현재값
nTrackPos : 드래깅 상태의 스크롤 박스의 현재값

여기서 살펴보면 나머지 다른 속성들은 직관적으로 이해할  있는 범위의 설명이다.
그런데 nPage라는 도대체 어떤 놈일까???

이전 예제를 이용하여 알아보도록 하자.
다음 그림은 기본적인 값이 이전 예제와 동일하고 page값이 디폴트로 0 샘플이다.

가운데 이동 막대기(Thumb) 크기는 기본값이다.
아래의 그림은 다음과 같은 코드를 추가하여 page 전체 범위의 1/2 50으로  샘플이다.

그림이 조금 달라보이지 않는가 페이지라고 하는 개념은 워드나 아래한글에서...
말하는 문서 전체의 페이지와 비슷한 개념이다.

위에 2번째 그림으로 스크롤바 정보를 예측해보면.. 아마도 현재 보이는 스크롤바의 2 정도
화면 크기가 아닐까..  0-100 사이이니까.. 현재 화면에 보이는 크기가 50정도..
만약 page 20으로 설정한다면 스크롤바크기의 1/5 되고.. 대략 시각적으로 보면
화면 기준으로 전체 다섯페이지 정도가 되겠구나 라고.. 예측할  있다.

전체 범위의 크기와 현재 화면상에 보여지는 스크롤바의 크기의 비율을 적당해 계산해서..
화면이 리사이징될 때마다 다시 계산해서 넣어주면...
화면의 크기와 스크롤바의 이동막대기 크기를 보고 대략 유추할  있게 된다.

이것이 스크롤바의 page값의 의미이다.

아래의 코드는 스크롤바의 페이지값을 재설정하는 코드이다.

SCROLLINFO info = {sizeof(SCROLLINFO), SIF_PAGE0};
info.nPage = 50;
m_scroll.SetScrollInfo(&info);

115.zip
0.03MB

스크롤바를 달아만 놨었다.. 요지부동.. ㅎㅎ

스크롤바 컨트롤을 상속받아서 내부에서 처리하지 않는이상... 다이알로그 등에 올려놓고 쓰려면
WM_VSCROLL  WM_HSCROLL  메시지를 처리해서 하나 하나 직접 동작시켜 주어야 한다.

현재 길쭉한 나무 막대기 모양으로 만들었으니WM_VSCROLL 메시지를 이용하여 스크롤바가
원하는 동작을 하도록 만들어 보자.


그림 처럼 클래스 위저드를 이용하여 메시지 핸들러를 추가한다.

그전에 알아두어야 할것이winuser.h  디파인 되어있는 스크롤바의 동작에 관한 디파인이다.
우리가 사용할 WM_VSCROLL 메시지 핸들러의 정의부를 살펴보면 첫번째 인자에 전달되는
값이기도 하기 때문에  알고 있어야 한다.

afx_msg void OnVScroll(UINT nSBCodeUINT nPosCScrollBarpScrollBar);

아래에서 디파인이 같은 것들이 존재하는 이유는 결국 같은 동작이지만 수직 스크롤과 수평
스크롤을 구분해 보여주기 위하여 나누어 놓은것이다.
#define SB_LINEUP             0
#define SB_LINELEFT          0
#define SB_LINEDOWN         1
#define SB_LINERIGHT         1
#define SB_PAGEUP            2
#define SB_PAGELEFT         2
#define SB_PAGEDOWN        3
#define SB_PAGERIGHT        3
#define SB_THUMBPOSITION 4
#define SB_THUMBTRACK     5
#define SB_TOP                    6
#define SB_LEFT                  6
#define SB_BOTTOM             7
#define SB_RIGHT                 7
#define SB_ENDSCROLL        8

그러므로 수직 스크롤바에 대하여 설명하면 수평은 자동이해.. ^^;
#define SB_LINEUP              0 - 위의 버튼 누름
#define SB_LINEDOWN         1 - 아래 버튼 누름
#define SB_PAGEUP            2 - 버튼과 이동막대기(thumb)사이의 상단 공간 누름
#define SB_PAGEDOWN        3 - 버튼과 이동막대기(thumb)사이의 하단 공간 누름
#define SB_THUMBPOSITION 4 - 이동 막대기의 최종위치
#define SB_THUMBTRACK     5 - 이동 막대기가 계속 이동하고 있음
#define SB_TOP                    6 - 크기가 변하거나 업데이트  .. min 설정
#define SB_BOTTOM             7 - 크기가 변하거나 업데이트  .. max 설정
#define SB_ENDSCROLL        8 - 한단계의 스크롤 동작을 마침

아주 간단하게 동작에 대해 설명했는데.. 6,7,8번을 제외하고는 이해하는데  문제가 없다.
SB_TOPSB_BOTTOM 레인지를 다시 설정하거나 크기를 변경하거나   발생하는
것이라는데 거의 사용하지 않는다.

스크롤시에 버튼을  누르고 있거나 이동 막대기를 잡고 왔다 갔다 하다가 마지막에 마우스
버튼을  띠는 순간 SB_ENDSCROLL 발생한다마우스를 누르고 이리 저리 움직이면
내부 타이머에 의하여 연속적으로 이벤트가 발생하는데 이벤트가 떨어지면 전체적인
하나의 동작으 완료되었음을   있게된다.

보통  쓰이지는 않는데.. 연속적인 동작에는 관심없고 마지막 최종 동작에만 관심이 있을 경우
 이벤트를 이용하여 최종 포지션을 읽어와서 먼가 작업을 한다던가   주고 쓰인다.


아래는 실제로 이벤트 핸들러 내부에 동작을 구현해본 코드이다.
1버튼을 누를 경우는  아래로 +-1 이동하고
2이동막대기를 움직일 때는 움직인 만큼
3페이지 영역을 누를 때는 +-5 이동하도록 구현하였다.

void CSampleDlg::OnVScroll(UINT nSBCodeUINT nPosCScrollBarpScrollBar)
{
    
if(!pScrollBar || !pScrollBar->m_hWnd)
        
return;

    
if(pScrollBar->GetDlgCtrlID() == IDC_SCROLLBAR5)
    {
        
UINT nCurPos = pScrollBar->GetScrollPos();
        
switch(nSBCode)
        {
        
case SB_BOTTOM:
            
pScrollBar->SetScrollPos(SCROLL_MAX);
            
break;

        
case SB_ENDSCROLL:
            
break;

        
case SB_LINEDOWN:
            
nCurPos += 1;
            
if(nCurPos > SCROLL_MAX)
                
nCurPos = SCROLL_MAX;
            
pScrollBar->SetScrollPos(nCurPos);
            
break;

        
case SB_LINEUP:
            
nCurPos -= 1;
            
if(nCurPos < SCROLL_MIN)
                
nCurPos = SCROLL_MIN;
            
pScrollBar->SetScrollPos(nCurPos);
            
break;

        
case SB_PAGEDOWN
            
nCurPos += 5;
            
if(nCurPos > SCROLL_MAX)
                
nCurPos = SCROLL_MAX;
            
pScrollBar->SetScrollPos(nCurPos);
            
break;

        
case SB_PAGEUP:
            
nCurPos -= 5;
            
if(nCurPos < SCROLL_MIN)
                
nCurPos = SCROLL_MIN;
            
pScrollBar->SetScrollPos(nCurPos);
            
break;

        
case SB_THUMBPOSITION:
             
pScrollBar->SetScrollPos(nPos);
            
break;

        
case SB_THUMBTRACK:
            
pScrollBar->SetScrollPos(nPos);
            
break;

        
case SB_TOP:
             
pScrollBar->SetScrollPos(SCROLL_MIN);
            
break;
        }
    }

    
CDialog::OnVScroll(nSBCodenPospScrollBar);
}

112.zip
0.03MB

스크롤바만큼 심플한 프로퍼티를 가진 컴포넌트도 보기 힘들것이다.


속성이라곤 Align이라는 속성 딸랑 하나에.. 그것도 거의.. 전혀 사용되지 않는 속성이다.
None :
당연히 아무것도 없다.
Top/Left :
수직 스크롤바 일 경우 너비를 맘대로 정해도, 왼쪽 모서리에 기본 너비로 고정됨.
Bottom/Right :
수직 스크롤바 일경우 오른쪽 아래로 너비 고정됨.

아래 그림을 보자, 리소스 디자인 폼에서 디자인한 화면이다.

이걸 수행하면 아래 화면과 같은 실행화면이 나온다.


그림에서 보이는 사각 테두리는 이해를 돕기 위하여 넣은 외곽선이다.
무엇때문에 있는 속성인지는 몰라도 필요하니까 존재하겠지만, 거의 사용되지 않는
속성임에는 틀림없다. -_-;;;


, 다음으로 이벤트를 확인해보자.
없다 -_-;

그렇다면 이제 사용하면 되는 것인가?
글쎄 -_-?


일단 바로 위의 그림에서 오른쪽 2개를 제거하고, CScrollBar m_scroll 로 연결한다음
#define SCROLL_MIN  0
#define SCROLL_MAX  100

BOOL CSampleDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    //
스크롤바의 최소, 최대값을 설정한다.
    m_scroll.SetScrollRange(SCROLL_MIN, SCROLL_MAX);
   
    //
초기 스크롤바의 포지션을 설정한다. 임시로 50 넣어보았다.
    m_scroll.SetScrollPos(50);

    //
스크롤바에 달린 양측 버튼의 활성화 여부.
    m_scroll.EnableScrollBar(ESB_ENABLE_BOTH);
    return TRUE;
}

처럼 설정해주고, 열심히 스크롤바를 눌러보자.
아무런 움직임도 없다 -_-;
그나마 가운데 막대기 드래그 하면 원하는 곳에 갔다가 놓으면 다시 재자리 -_-;;;


스크롤바는 있는 그대로 사용할 수 없는 컨트롤인것이다... ~
다음장에서는 스크롤바를 써보자.. ~

+ Recent posts