본문 바로가기
C++ 200제/코딩 IT 정보

mfc 다이얼로그 스크롤바(PictureControl) 만들기 (프로그래밍 팁)

by vicddory 2018. 5. 28.

mfc 다이얼로그 스크롤바(PictureControl) 만들기 (프로그래밍 팁)


첨부 파일

실행 파일 - Test_ScrollBar.exe [링크]

프로젝트 - Test_ScrollBar.zip [링크]


신입 시절에 만들었던 MFC 프로그래밍 팁 하나 공유합니다.


C#과는 다르게 MFC 다이얼로그 스크롤바가 없습니다. 그래서 스크롤바가 필요하다면 별도로 프로그래밍해야 합니다. 이 포스트는 스크롤바가 필요했던 제가 구현한 내용을 담고 있습니다. 저는 MFC 다이얼로그 스크롤바 구현을 위해, 다이얼로그에 PictureControl을 추가했습니다.

PictureControl의 크기만큼 스크롤바가 갱신되도록 구현하였죠. 전체 소스와 자세한 사항들은 첨부된 프로젝트 파일을 참조하세요.


그리고, 다이얼로그 그래픽이 갱신 안 되는 버그가 있긴 한데, 귀찮아서 안 고쳤습니다.


MFC 스크롤바 - 예제 메인MFC 스크롤바 - 예제 메인


텍스트 박스 안의 숫자를 바꾸면 픽쳐 컨트롤의 크기가 변경됩니다.


그리고, 변경된 크기만큼 MFC 다이얼로그 스크롤바도 갱신이 됩니다.


MFC 스크롤바 - 픽처 컨트롤러MFC 스크롤바 - 픽처 컨트롤러


MFC 스크롤바 - 70 by 70일 때 스크롤바MFC 스크롤바 - 70 by 70일 때 스크롤바


스크롤바를 끝까지 당겨보면, 픽쳐 컨트롤의 경계도 잘 보입니다.


픽처 컨트롤이 다 보여야 MFC 다이얼로그 스크롤바에 의미도 있지요.


MFC 스크롤바 - 픽처 컨트롤의 경계MFC 스크롤바 - 픽처 컨트롤의 경계

MFC 프로그래밍 팁 - 소스 살펴보기


우선, 다이얼로그의 속성 3개를 추가합니다.


1
2
3
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnSize(UINT nType, int cx, int cy);
cs


그리고 OnSize() 함수부터 살펴봅니다.


참고로, 관련 프로그래밍 소스는 구글링해서 얻었습니다. 화면 배율에 따른 MFC 다이얼로그 스크롤바 갱신은 구현되지 않아, 저는 그 부분을 따로 구현했습니다.


근데 링크가 기억나질 않네요.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void CTest_ScrollBarDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialog::OnSize(nType, cx, cy);
 
    CRect rect;
    GetDlgItem(IDC_PCC_USERAREA)->GetClientRect(rect);
 
    ViewWidth_ = rect.Width() + 45;
    ViewHeight_ = rect.Height() + 120;
 
    int HScrollMax = 0;
    HPageSize_ = 0;
 
    if(cx < ViewWidth_)
    {
        HScrollMax = ViewWidth_ - 1;
        HPageSize_ = cx;
        HScrollPos_ = min(HScrollPos_,ViewWidth_ - HPageSize_ - 1);
    }
 
    SCROLLINFO si;
    si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
    si.nMin = 0;
    si.nMax = HScrollMax;
    si.nPos = HScrollPos_;
    si.nPage = HPageSize_;
    SetScrollInfo(SB_HORZ, &si, TRUE);
 
    int VScrollMax = 0;
    VPageSize_ = 0;
 
    if( cy < ViewHeight_)
    {
        VScrollMax = ViewHeight_ - 1;
        VPageSize_ = cy;
        VScrollPos_ = min(VScrollPos_,ViewHeight_ - VPageSize_ - 1);
    }
 
    si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
    si.nMin = 0;
    si.nMax = VScrollMax;
    si.nPos = VScrollPos_;
    si.nPage = VPageSize_;
    SetScrollInfo(SB_VERT,&si,TRUE);
}
cs


현재 다이얼로그 크기를 기반으로 스크롤바가 움직일 범위를 먼저 설정하게 되어 있습니다.


이후엔, 그 크기를 기반으로 MFC 다이얼로그 스크롤바의 크기를 재조정합니다.


다음의 OnVScroll() 함수와 OnHScroll() 함수는 거의 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void CTest_ScrollBarDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    int delta;
 
    switch(nSBCode)
    {
    case SB_LINEUP:
        delta = -m_nBasic;
        break;
    case SB_PAGEUP:
        delta = -VPageSize_;
        break;
    case SB_THUMBTRACK:
        delta = static_cast<int>(nPos) - VScrollPos_;
        break;
    case SB_PAGEDOWN:
        delta = VPageSize_;
        break;
    case SB_LINEDOWN:
        delta = m_nBasic;
        break;
    default:
        return;
    }
 
    int scrollpos = VScrollPos_ + delta;
    int maxpos = ViewHeight_ - VPageSize_;
 
    if (scrollpos < 0)
        delta = -VScrollPos_;
    else
        if(scrollpos > maxpos)
            delta = maxpos - VScrollPos_;
 
    if(delta != 0)
    {
        VScrollPos_ += delta;
        SetScrollPos(SB_VERT, VScrollPos_, TRUE);
        ScrollWindow(0-delta);
    }
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void CTest_ScrollBarDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    m_nHdelta = 0;
 
    switch(nSBCode)
    {
    case SB_LINELEFT:
        m_nHdelta = -m_nBasic;
        break;
    case SB_PAGELEFT:
        m_nHdelta = -HPageSize_;
        break;
    case SB_THUMBTRACK:
        m_nHdelta = static_cast<int>(nPos) - HScrollPos_;
        break;
    case SB_PAGERIGHT:
        m_nHdelta = HPageSize_;
        break;
    case SB_LINERIGHT:
        m_nHdelta = m_nBasic;
        break;
    default:
        return;
    }
 
    int scrollpos = HScrollPos_ + m_nHdelta;
    int maxpos = ViewWidth_ - HPageSize_;
 
    if( scrollpos < 0)
        m_nHdelta = -HScrollPos_;
    else
        if(scrollpos > maxpos)
            m_nHdelta = maxpos - HScrollPos_;
 
    if(m_nHdelta != 0)
    {
        HScrollPos_ += m_nHdelta;
        SetScrollPos(SB_HORZ, HScrollPos_,TRUE);
        ScrollWindow(-m_nHdelta, 0);
    }
}
cs


마지막으론, 제일 중요한 버튼 이벤트입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void CTest_ScrollBarDlg::OnBnClickedButton1()
{
    UpdateData(TRUE);
 
    if(m_nEditX > 100 || m_nEditY > 100 || m_nEditX < 10 || m_nEditY < 10)
    {
        AfxMessageBox("10 ~ 100");
    }
    else
    {
        // 다이얼로그 초기화
        InitObj();
 
        m_ctrl_PCT_Userarea.MoveWindow(m_nLineSize + 15, m_nLineSize * 6, GetPCWidth() * 
        15, GetPCHeight() * 15);
        TRACE("%d - %d\n", GetPCWidth(), GetPCHeight());
 
 
        // OnSize를 호출해 스크롤바 갱신
        ShowScrollBar(SB_VERT, TRUE);
        ShowScrollBar(SB_HORZ, TRUE);
 
        CRect rc;
        GetDlgItem(IDC_PCC_USERAREA)->GetClientRect(rc);
        ::SendMessage(this->m_hWnd, WM_SIZE,
         (WPARAM)SIZE_RESTORED, MAKELPARAM(rc.Width() + 45, rc.Height() + 120));
    }
}
cs


다이얼로그에서 스크롤바 속성을 설정하지 않고 코드상에서 설정합니다.


OnSize() 함수 역시, 코드상에서 호출하도록 설정합니다. 이렇게 구성하지 않으면, OnSize() 함수가 너무 자주 호출되어 버벅대지요. 그리고, 픽쳐 컨트롤 자체가 작아지면 MFC 다이얼로그 스크롤바가 필요하지 않게 되어, ShowScrollBar() 함수엔 자동으로 FALSE가 들어가게 되어 소스 구성이 복잡해집니다.


그러니, 위의 소스처럼 필요할 때만 호출해 사용합니다.


다른 곳에서 스크롤바는 정확한 API를 참조하여 제대로 프로그래밍하라는 조언을 들었습니다. 그렇지만, MS에서 지원을 끊은 지 10년도 넘은 MFC로는 뭘 만들고 뭘 참조하던 크게 의미가 있을까요?


MFC 코드를 아무리 꾸민다 해도, 생산성 저하엔 탁월한 언어인지라 크게 관련이 없다는 생각입니다.


ps. 참조 사이트는 기억이 안 나네요.


mfc 다이얼로그 스크롤바(PictureControl) 만들기 (프로그래밍 팁)

댓글