블로그 이미지
인생의무한루프
인생에 무한루프.....

calendar

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

Notice

Tag

Recent Post

Archive

Recent Comment

Recent Trackback

2008. 12. 17. 15:31 Appication/VC++_Control

이벤트 사용하기 ( MFC ComboBox 예제 )

이번장 에서는 콤보박스의 이벤트에 대하여 다루어 보겠습니다.



콤보에서 제공하는 이벤트는 다음과 같습니다.
CBN_EDITCHANGE
CBN_CLOSEUP
CBN_DBLCLK
CBN_DROPDOWN
CBN_EDITUPDATE
CBN_ERRSPACE
CBN_KILLFOCUS
CBN_SELCHANGE

CBN_SELENDCANCEL
CBN_SELENDOK
CBN_SETFOCUS

이렇게 사용되며, 색상이 파란색인건 보통 자주사용되는 이벤트이고, 나머지는 잘 사용되지 않죠.
어디까지나 개인적으로 사용해본 경험을 바탕으로 추린 내용이니..흠..

1. CBN_EDITCHANGE, CBN_EDITUPDATE
CBN_EDITCHANGE은 드롭다운과 심플 스타일일 경우에만 발생하는 이벤트로, 에디트 박스 영역의 글자에 변화가 생길 경우에 발생하는 이벤트입니다. 보통 내부에 들어있는 콤보 아이템을 선택하는 목적으로 사용할 때는 잘 쓰이지 않습니다만, 인터넷 익스프롤러나 탐색기, 최근 들어 자주 볼수 있는 오토 컴프리트 기능들을 다룰 때 사용됩니다.

사용자가 문자열을 입력할 때, 내부 아이템중에서 선택적으로 일치하는 항목을 보여주거나 할 때
주로 사용되죠.

CBN_EDITUPDATE는 문자열의 변화뿐 아니라, 그 영역이 먼가에 가려져있다바 보인다던가..
화면을 갱신해야 할경우 발생하는 이벤트입니다. 한번 들어본 말이죠?

에디트 컨트롤의 EN_CHANGE와 EN_UPDATE와 100% 일치하는 이벤트입니다.
콤보박스에 에디트 컨트롤이 달여있는 것이니 당연한거겠지요.

2. CBN_DROPDOWN, CBN_CLOSEUP
CBN_DROPDOWN는 콤보박스의 에디트 영역 우측에 버튼을 누르면 리스트박스가 주욱 열립니다.
이때 발생하는 이벤트고, 열린 리스트박스에서 아이템을 마우스로 선택하거나 엔터를 쳐서 리스트가
닫힐 때 발생하는 이벤트가 CBN_CLOSEUP 이죠.

3. CBN_DBLCLK
요 스타일은 콤보박스의 리스트박스상에서 아이템을 더블클릭 할 경우에만 발생합니다. 드롭 다운 스타일이나 드롭 리스트 스타일일 경우, 아무리 순발력이 좋아도 절대 일으킬 수 없는 메시지죠..
심플 스타일일 경우에만 발생하는 메시지입니다. ^^;

4. CBN_ERRSPACE
이것도 에디트를 다룰 때 한번 봤던 이벤트인데, 아이템을 추가하거나 삽입할 경우, 내부적으로
주소 공간을 할당해야 하겠죠? 이 때 할당 공간이 부족하면 발생하는 경고성 이벤트입니다.

5. CBN_KILLFOCUS, CBN_SETFOCUS
기본적인 윈도우 컨트롤이 가지고 있는 포커스 이벤트입니다.
예를 들어 선택해야 하는 아이템들이 잔뜩 모여있는 필수 아이템 그룹이 있다고 가정했을 때,
보통 라디오 버튼 그룹이나 콤보박스 혹은 리스트를 사용하게 됩니다. 라디오가 직관적이긴 한데
많은 아이템일 경우 공간이 부족하게 되죠, 리스트도 마찬가지고..

이럴 경우 콤보가 첨에 생기면 빨간색 글자로 <필수 선택 항목>이라고 써있다가 포커스가 옮겨
가게 되면 경고문구가 지워지고 아이템을 선택할 수 있는 원래의 환경으로 돌아오는 겁니다.

6. CBN_SELCHANGE
일반적인 콤보 기능을 담당할 경우 가장 많이 사용되는 이벤트 중에 하나입니다. 마우스나 키보드로
아이템을 선택하거나 변경할 경우 발생하는 이벤트죠.

7. CBN_SELENDOK, CBN_SELENDCANCEL
콤보박스에서 아이템을 선택할 경우, 나열된 리스트에서 마우스로 콕 찍거나 엔터키를 치면
발생하는 이벤트입니다. CBN_SELCHANGE와 같다고 볼수 있는데 이는 변경되는 내내 발생하지만
CBN_SELNDOK는 선택되어지는 그 순간에 한번만 발생한다는 차이점이 있습니다. 물론 이 후에
CBN_SELCHANGE도 당연히 발생하죠.

CBN_SELENDCANCEL은 키보드의 방향키로 아이템을 고르고 있다가 혹은 마우스로 찾아다니다가
이건 아닌가바.. ESCAPE 키를 딱 누르는 순간에 발생하는 이벤트입니다. 선택을 취소했다고 나오는
이벤트인데 콤보박스에서 동작특성이 상당히 불안한 이벤트이기도 하죠..
믿고 쓸만한 이벤트가 못됩니다.

-------------------------------------------------------------------------------------
위 이벤트를 모두 사용하는 샘플을 제작하기가 여간 까다로운게 아니네요..
그래서 여러스타일의 콤보를 놓고 사용자가 하나씩 클릭해 보면서
이벤트가 어떤 순서로 어떻게 발생하는 지를 관찰할 수 있는 아주 간단한 -_- 샘플을 올립니다.


샘플을 제작하면서 각각의 컨트롤의 이벤트 핸들러를 클래스위저드를 이용해서 추가할 수 도
있겠지만 코드가 길어지니, 셀체인지 이벤트만 클래스 위저드에서 콤보박스마다 넣고, 나머지는
ON_CONTROL_RANGE를 이용하겠습니다.

ON_CONTROL_RANGE는 어떤 이벤트를 잡아줄건지를 명시하여 원하는 컨트롤의 이벤트를
묶음으로 건질수 있다는 차이가 있습니다.

ON_CONTROL_RANGE(event, begin, end, handler)
event : 처리하고자 하는 대상 이벤트
begin : 범주의 시작이 되는 컨트롤의 아이디
end : 범주가 끝나는 컨트롤의 아이디
handler : LRESULT 함수명(UIND nID) 타입의 멤버함수

저것들도 다 한방에 묶어서 처리할 수 있으면 좋겠지만, 거기까지는 자동으로 지원하는 매크로는
없구요, PreTranslateMessage 에서 처리하면 가능하겠죠. 하지만 코드가 지저분 해진다는거..





지금까지 콤보박스에서 다룬 멤버 함수들과 이벤트들을 제외하고도 몇몇 가지가 더 있지만,
이름과 기능이 잘 매칭되는 단순한 기능들이니 MSDN에서 찾아보시면 쉽게 이해하실 수 있을겁니다

다음 장에서는 콤보를 이용한 간단한 샘플 하나 정도 다룬 후에 콤보과정을 마치겠습니다.



Posted by 까막백(홈페이지 이동)
posted by 인생의무한루프
2008. 12. 17. 15:30 Appication/VC++_Control

투명하게 만들어보자. ( MFC ComboBox 예제 )

콤보박스를 투명하게 만들일이야 없겠지만, 한번 도전해봤습니다.
하지만 불완전한 구현이 되고 말았습니다.
리스트박스가 보일경우 리스트의 스크롤바 무브 이벤트를 처리하지 못하겠더군요.. -_-;

스크롤바를 드래그해서 움직일 경우 잔상이 남습니다.
[요 부분에 대한 아이디어가 있으신 분은.. 메시지좀 남겨주시와요 ^^]



기본적인 아이디어:
리스트 박스가 그려질 영역만큼, 보여지기 전에 미리 화면을 캡처 해놓은 다음에..
드롭된 리스트의 배경에 캡처된 화면을 그려주면 투명한 것처럼 보일 것이다.


// 윈도우 화면에서 주어진 사격형 만큼을 캡처해서 비트맵으로 넘겨준다.
// 저 콤보박스의 리스트 영역의 배경을 직접 그려주어 투명한것 처럼 보여주는
// 꽁수를 쓰기 위해서 필요한 함수.

BOOL Capture(CRect rc, CBitmap& m_Bitmap)
{
    // 화면 사이즈를 읽어온다.
    int cx = GetSystemMetrics(SM_CXSCREEN);
    int cy = GetSystemMetrics(SM_CYSCREEN);
   
    // 스크린 캡처를 위한 DC를 맹근다.
    CDC ScreenDC;
    ScreenDC.CreateDC("DISPLAY", NULL, NULL, NULL);
   
    // 비트맵을 로딩할 임시 DC를 만든다.
    CDC memDC;
    memDC.CreateCompatibleDC(&ScreenDC);
   
    // 비트맵이 살아있으면, 살짝 날려준다.
    if(m_Bitmap.m_hObject)
        m_Bitmap.DeleteObject();

    // 스크린 호환용 비트맵을 주어진 크기만큼 만든다.
    m_Bitmap.CreateCompatibleBitmap(&ScreenDC, rc.Width(), rc.Height());
   
    // 비트맵에 캡처된 화면을 그린다.
    memDC.SelectObject(&m_Bitmap);
    memDC.BitBlt(0,0,rc.Width(), rc.Height(),&ScreenDC,rc.left,rc.top, SRCCOPY);
   
    return TRUE;
}

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
 
    switch(nCtlColor)
    {
    case CTLCOLOR_STATIC:
    case CTLCOLOR_EDIT:
            pDC->SetTextColor(RGB(0, 0, 255));
            pDC->SetBkMode(TRANSPARENT);
            return (HBRUSH)GetStockObject(NULL_BRUSH);
    case CTLCOLOR_LISTBOX:
        {
            pDC->SetTextColor(RGB(255, 0, 0));
            pDC->SetBkMode(TRANSPARENT);

            // 리스트 영역의 크기를 구한다.
            CRect rc;
            pWnd->GetClientRect(rc);

            // 임시 DC를 하나 만든다.
            CDC mdc;
            mdc.CreateCompatibleDC(pDC);
            mdc.SelectObject(&m_bmp);

            // 아까 캡처해두었던 배경을 리스트의 바탕에 그려준다.
            pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &mdc, 0, 0, SRCCOPY);
            return (HBRUSH)GetStockObject(NULL_BRUSH);
        }
    }

 return hbr;
}

BOOL CSssDlg::PreTranslateMessage(MSG* pMsg)
{
    if(m_combo2.GetDroppedState())
    {
        // 리스트가 보여지고 있을 때 마우스 무브 이벤트시 리스트를 갱신해준다.
        if(pMsg->message == WM_MOUSEMOVE && ::GetDlgCtrlID(pMsg->hwnd) == 1000)
            CWnd::FromHandle(pMsg->hwnd)->Invalidate();
    }

 return CDialog::PreTranslateMessage(pMsg);
}

// 콤보박스의 리스트가 드롭될 때 발생하는 이벤트.
void CSssDlg::OnDropdownCombo2()
{
    // 현재 콤보박스의 위치를 읽어온 후..
    CRect rc;
    m_combo2.GetWindowRect(rc);

    // 대략 오프셑을 넣은 후, 드롭된 리스트의 크기를 예상하여 넣는다.
    // 실제 드롭이 되기 전까지는 크기를 알수 없어, 적당하게 높이를 200으로 주었다.
    // 만든 사람은 본인이 높이를 아니, 적당하게 값을 주면 된다.

    rc.left += 1;
    rc.top = rc.bottom+1;
    rc.bottom += 200;

    Capture(rc, m_bmp);
}

원하는 만큼 깔끔한 코드가 나오지는 못했지만, 드롭리스트에 스크롤바만 않생기면..
그럭저럭 설렁 설렁 넘어갈만 하다고.. 생각만 해봅니다. ㅜㅜ





Posted by 까막백(홈페이지 이동)
posted by 인생의무한루프
2008. 12. 16. 23:35 Appication/VC++_Control

스크롤바 기초 ( MFC Scroll 예제 )

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


속성이라곤 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;
}

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


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


Posted by 까막백(홈페이지 이동)
posted by 인생의무한루프
2008. 12. 16. 23:34 Appication/VC++_Control

얏 움직여라~ ( MFC Scroll 예제 )

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

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

현재 길쭉한 나무 막대기 모양으로 만들었으니, WM_VSCROLL 메시지를 이용하여 스크롤바가
원하는 동작을 하도록 만들어 보자.
그림 처럼 클래스 위저드를 이용하여 메시지 핸들러를 추가한다.

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

afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);

아래에서 디파인이 같은 것들이 존재하는 이유는 결국 같은 동작이지만 수직 스크롤과 수평
스크롤을 구분해 보여주기 위하여 나누어 놓은것이다.
#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_TOP, SB_BOTTOM은 레인지를 다시 설정하거나 크기를 변경하거나 할 때 발생하는
것이라는데 거의 사용하지 않는다.

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

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


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

void CSampleDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    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(nSBCode, nPos, pScrollBar);
}




Posted by 까막백(홈페이지 이동)
posted by 인생의무한루프
2008. 12. 16. 23:32 Appication/VC++_Control

struct SCROLLINFO ( MFC Scroll 예제 )

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

typedef struct tagSCROLLINFO
{
    UINT   cbSize;
    UINT    fMask;
    int       nMin;
    int       nMax;
    UINT    nPage;
    int       nPos;
    int        nTrackPos;
}   SCROLLINFO, FAR *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_PAGE, 0};
info.nPage = 50;
m_scroll.SetScrollInfo(&info);




Posted by 까막백(홈페이지 이동)
posted by 인생의무한루프
prev 1 2 3 4 5 6 7 8 ··· 11 next