에디트에 대한 기초강좌를 마무리한 시점에서 질문이 하나 들어왔다..
지금까지의 내용을 기초로 에디트의 전체 배경을 바꾸었는데.. 깜빡이거나 먼가 이상하다...??

지난 강좌를 찾아보니, 에디트에 대한 배경 처리가 제대로 마무리 되지 못한 상태에서
강좌를 마쳐 혼란을 가져온것일 수 있을꺼 같아..
질문을 토대로 배경처리 기능을 마무리 하려고 한다.

위 그림은 두가지로 그 기능이 분류된다.

1. 왼쪽 에디트
-> 글자 바탕과 배경색상을 모두 녹색으로 처리한 것
2. 오른쪽 에디트
-> 글자 바탕은 TRANSPARENT 시키고, 바탕에는 HATCH 브러쉬를 뿌린것이다.

위 두가지 구현에는 다음과 같은 차이가 있다.
1번은 글자와 배경을 모두 그려주므로, 글씨를 쓸때 잔상이 남거나 깜빡임이 전혀없다.
2번은 글자는 쓰되 배경을 그려주지 않으므로 글씨를 쓸때 깜빡임은 없으나 잔상이 남는다.
이러한 이유로 두가지의 구현 부분에 조금 차이를 가지게 된다.

기본적인 코드는 브러쉬를 2개 만든다는 점만 빼고는 헤더에 추가할 내용은 없다.
BOOL CSssDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // 솔리드, 해치 브러쉬를 각각만든다.
    m_brush1.CreateSolidBrush(RGB(0, 255, 0));
    m_brush2.CreateHatchBrush(HS_DIAGCROSS, RGB(0, 255, 0));

    return TRUE;
}

// 클래스 위저드에서 WM_CTLCOLOR 이벤트 핸들러를 추가한다.
HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    switch (nCtlColor)
    {
    case CTLCOLOR_EDIT:
        pDC->SetTextColor(RGB(255, 0, 0));
        if (pWnd->GetDlgCtrlID() == IDC_EDIT1)
        {
            // 글자 바탕을 녹색으로 하고, 녹색 솔리드 브러쉬를 리턴한다.
            // 이게 전부다.
            pDC->SetBkColor(RGB(0, 255, 0));
            return m_brush1;
        }
        else if (pWnd->GetDlgCtrlID() == IDC_EDIT2)
        {
            // 글자의 배경을 그려주게 되면 바탕의 해치브러쉬 영역에 글자뒷 부분은
            // 녹색 사각형이 그려지게 된다.
            // 이를 막아주기 위하여, 배경그리는 기능을 제거한다. TRANSPARENT
            pDC->SetBkMode(TRANSPARENT);

            // 에디트 컨트롤 영역을 구한 후에
            // 브러쉬로 색칠해준다.
            CRect rc;
            GetDlgItem(IDC_EDIT2)->GetWindowRect(rc);
            ScreenToClient(rc);
            pDC->FillRect(rc, &m_brush2);

            return m_brush2;
        }
    }
    return hbr;
}

클래스위저드에서 IDC_EDIT2의 EN_CHANGE 이벤트 핸들러를 추가한다.
이 핸들러를 추가하는 이유는 두번째 에디트는 글자의 배경이 뒤에 그려지는 해치 브러쉬의
패턴을 덥어쓰는 걸 막아주기 위하여 TRANSPARENT 속성을 주었으므로, 글씨의 뒷부분을
그려주지 않게된다. 그러므로 잔상이 남게되어 글자에 대한 변경이 발생하면
배경을 다시 그려주도록 하기 위해서이다.
void CSssDlg::OnChangeEdit2()
{
    CRect rc;
    GetDlgItem(IDC_EDIT2)->GetWindowRect(rc);
    ScreenToClient(rc);
    InvalidateRect(rc);
}

49.zip
0.06MB

스태틱 컨트롤을 이미지 형태로 사용할 경우 보통은 리소스에 있는 이미지를 연결시킨다.


왼쪽은 리소스 편집기에서 정적으로 이미지를 연결시킨 픽처 컨트롤이고,
오른쪽은 속성을 비트맵으로 설정해 놓고, 아무런 작업도 진행하지 않은 경우이다.

그렇다면 파일에서 비트맵을 로딩하여 필요할 때마다 바꾸려면 어떤 과정을 거쳐야할까?

아래는 디스크에 있는 파일을 읽어 들여서 오른족에 보이는 컨트롤에 동적 로딩한 화면이다.

복잡한 과정은 전혀 필요없다.

BOOL CSssDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
   
    //
파일에서 이미지를 비트맵 타입으로 읽어온다.
    //
현재는 커렌트 경로에 존재하므로 전체 경로를 넣지 않았지만.
    //
실행파일과 다른 경로에 존재한다면 절대 경로를 넣어야한다.
    //
전달되는 인자값은 MSDN에 자세히 설명이 나와있다.
    HANDLE h = ::LoadImage(AfxGetInstanceHandle(), "image_file.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

    //
화면의 픽처 컨트롤을 임시로 CStatic 컨트롤로 받아들인다.
    CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC_FILE);

    //
이미지 핸들과 스태틱 컨트롤이 제대로 값을 가지고 있다면
    if(h && pStatic) {
        //
컨트롤에 비트맵을 연결시킨다.
       pStatic->SetBitmap((HBITMAP)h);
    }
  
    return TRUE; // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
}


아이콘일 경우도 위의 과정에서 필요한 형태만 변경한다면 아주 쉬울것이다.

참고. 만약 이미 비트맵이 로딩되어 있다면.. GetBitmap()으로 비트맵 핸들을 받은 후에
그 핸들을 CloseHandle()을 이용하여 해제해주고, 새로이 SetBitmap()을 해주어야 한다.

이렇게 생성하거나 할당한 리소스를 필요할 때 해제해 주지 않으면 나중에 [리소스가 부족합니다.]
라는 엉뚱한 메시지를 볼 수 있을것이다.

58.zip
0.05MB




아래 그림을 보면 배경을 다양한 형태로 처리할 수 있음을 알수 있다.
사실상 저렇게 쓰는 경우는 별반 없지만... UI 작업을 하다보면
어떤 정신 세계가 독특한 커스터머나 디자이너를 만날지 모른다..

화면을 보면 무엇을 설명하고자 하는지 쉽게 이해할 수 있을 것이다.

스태틱 컨트롤의 배경 전체 색상을 바꾸어 주기위해서는 저 영역을 먼가로 칠해주어야한다.
MFC
에서 먼가 영역을 칠할 때 필요한 객체는 CGdiObject를 상속받은 놈 중에 CBrush 라고 하는
일종의 빗자루? ? 비끄무리한 객체가 있다.

우선 헤더에 CBrush m_brush; 라고 객체를 하나 선언한다.
BOOL CSssDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
   
    //
브러쉬 객체를 녹색으로 생성한다.
    m_brush.CreateSolidBrush(RGB(0, 255, 0));

 

    return TRUE; // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
}

그리고, 지금까지와 마찬가지로.. WM_CTLCOLOR 이벤트 핸들러를 추가한후
다음과 같이 코드를 수정한다.

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   
    switch(nCtlColor)
    {
    case CTLCOLOR_STATIC:
        {
            pDC->SetTextColor(RGB(255, 0, 0));
           
            if(pWnd->GetDlgCtrlID() == IDC_STATIC_1)
                pDC->SetBkColor(RGB(0, 255, 0));
            else if(pWnd->GetDlgCtrlID() == IDC_STATIC_3)
                //
? 그냥 브러쉬를 리턴하면 끝이냐?
                //
그렇다 그냥은 디폴트 브러쉬가 날라가게 되는데..
                //
여기에 먼가 특별한 브러쉬를 만들어 리턴하면 그게 그 컨트롤의
                //
배경에 영향을 미치게 된다.
                //
다양한 브러쉬를 만들어 리턴해보자 ^^;
                return m_brush;
            else if(pWnd->GetDlgCtrlID() == IDC_STATIC_2)
            {
                pDC->SetBkColor(RGB(0, 255, 0));
                return m_brush;
            }
        }
    }
    // TODO: Return a different brush if the default is not desired
    return hbr;
   
}

56.zip
0.04MB



---------------------------------------------------------------------------------
이 부분을 진행하다 보니, 만약 패턴 브러쉬를 넣었을 경우는 어떻게 될까? 라는
생각이 들어서 별도로 진행해 보았다.

이 그림은 위의 소스 일부를 수정하여, 변경해 본 것으로.. Hatch 브러쉬를 이용하여
배경을 칠해본것이다. 자? 두번째를 어떻게 처리할 수 있을까?

BOOL CSssDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    m_brush.CreateHatchBrush(HS_DIAGCROSS, RGB(0, 255, 0));
  
    return TRUE;  // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
}


HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   
    switch(nCtlColor)
    {
    case CTLCOLOR_STATIC:
        {
            pDC->SetTextColor(RGB(255, 0, 0));
           
            if(pWnd->GetDlgCtrlID() == IDC_STATIC_1)
                pDC->SetBkColor(RGB(0, 255, 0));
            else if(pWnd->GetDlgCtrlID() == IDC_STATIC_3)
                return m_brush;
            else if(pWnd->GetDlgCtrlID() == IDC_STATIC_2)
            {

                // ???
                //
한번 직접 구현해 보자.
                //
결과물은 아래의 첨부파일에 포함되어 있다.           
            }
        }
    }

    // TODO: Return a different brush if the default is not desired
    return hbr;
}

48.zip
0.04MB



본 예제에서도 이전처럼 새로운 클래스를 추가하거나 하는 작업들은 하지 않고, 모든 작업을 다이알로그에서 처리 가능하도록 하고자 한다.


위의 그림이 샘플 예제이며,
내부에 사용된 기능은 다음과 같다.

1. 폰트를 만들어 언더라인을 긋는다.
2. 하이퍼 링크 컨트롤 위에 마우스가 가면 손가락 모양으로 바뀐다.
3. 클릭하면 링크된 웹사이트가 뜬다.
4. 클릭이 끝나면 한번 클릭된 것으로 인식하고, 글자 색상을 바꾼다.

구현되는 기능은 일반적인 하이퍼 링크 컨트롤의 기능을 100% 지원한다.
단, 툴팁도 추가할 수 있지만.. 현재 강좌 범위를 넘어가므로 나중을 기약하며 생략한다.

준비해야할 것과 추가해야할 코딩..
1. 먼저 폼에 스태틱 컨트롤 중에 Text 컨트롤을 하나 올리고, IDC_STATIC_1 로 설정한다.
2. 컨트롤의 속성중에 Styles 탭에서 Notify를 첵크한다. (중요하다.)
3. 다이알로그의 헤더 파일에 CFont m_font; 멤버를 하나 추가한다.
4. 다이알로그의 헤처 파일에 BOOL m_m_clicked; 멤버를 추가한다.

BOOL CSssDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
   
    //
밑줄이 쫘악 그어진 폰트를 하나 만든다.
    LOGFONT log;
    GetFont()->GetLogFont(&log);
    log.lfUnderline = TRUE;
    m_font.CreateFontIndirect(&log);

    //
만들어진 폰트를 스태틱 컨트롤에 적용한다.
    GetDlgItem(IDC_STATIC_1)->SetFont(&m_font);

 

    return TRUE;  // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
}

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   
    switch(nCtlColor)
    {
    case CTLCOLOR_STATIC:
        {
            if(pWnd->GetDlgCtrlID() == IDC_STATIC_1)
            {
                //
클릭 한적이 없으면 파랑색으로...
                if(m_clicked == FALSE)
                    pDC->SetTextColor(RGB(0, 0, 255));
                //
한번 클릭하고 나면 보라색 비끄무리하게 바꾼다.
                else
                    pDC->SetTextColor(RGB(255, 100, 100));
               
                //
기왕 하는거 배경은 투명한 형태로 계속 유지하자.. --;
                pDC->SetBkMode(TRANSPARENT);
                return (HBRUSH)GetStockObject(NULL_BRUSH);;
            }
        }
    }
    // TODO: Return a different brush if the default is not desired
    return hbr; 
}


여기 까지는 기존에 강좌에 나온것과 별반 다른작업이 없다. 다음 2가지 과정을 거치고 나면
하이퍼링크 컨트롤로 변신한다. ^^;

클래스 위저드를 열어 WM_SETCURSOR 이벤트를 추가한다.

그러고 나면 다음과 같은 코드가 추가된다.

BOOL CSssDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    return CDialog::OnSetCursor(pWnd, nHitTest, message);
}


간단하게 WM_SETCURSOR 이벤트에 대하여 설명해 보면, 다이알로그 위에서 마우스가 움직이는 동안 필요한 커서를 제어할 수 있도록 기능을 제공하는 것이다.
일단 코드를 다음과 같이 변경한다.
BOOL CSssDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    CPoint pt;   //
마우스 커서 위치를 저장할 객체
    CRect rc;   // 스태틱 컨트롤의 위치를 저장할 객체.

     // 마우스 커서의 위치를 찾아온다.
    GetCursorPos(&pt);
    //
스태틱 컨트롤의 위치를 찾아온다.
    GetDlgItem(IDC_STATIC_1)->GetWindowRect(rc);

     // 만약 마우스가 스태틱 컨트롤 위에 와있으면..
    if(rc.PtInRect(pt))
    {
        // IDC_HAND
라는 스탠다드 커서를 읽어와서 커서를 변경시킨다.
        SetCursor(AfxGetApp()->LoadStandardCursor(MAKEINTRESOURCE(IDC_HAND)));

        //
꼭 리턴을 해주어야하는데, 이로써 화면에 바뀐커서가 적용된다.
        //
리턴 해주지 않으면 아무리 커서를 바꾸어도 전혀 변경이 없다.
        //
아래 return CDialog::OnSetCursor(pWnd, nHitTest, message); 에서 커서를
        //
원상 복구 시켜버리기 때문이다.
        return TRUE;
    }
 
 return CDialog::OnSetCursor(pWnd, nHitTest, message);
}


이제 마지막 하나의 기능이 남아있다.
스태틱 컨트롤을 마우스로 클릭하고 나면, 그걸 인식하여 웹페이지를 열어 주어야한다.
아까전에 속성창에서 Notify를 첵크하고 하였던 것이 기억날것이다. 만약 이 속성을 주지 않는다면
아무리 마우스를 컨트롤에 놓고 꼭, 꼭 찍어도 아래의 함수는 동작하지 않는다.

클래스 위저드를 열어서 IDC_STATIC_1의 BN_CLICKED 이벤트를 추가한다.


그러면 해당 이벤트의 핸들러가 추가된다.

그리고 코드를 다음과 같이 입력하면 된다.
void CSssDlg::OnStatic1()
{
    m_clicked = TRUE;
    GetDlgItem(IDC_STATIC_1)->Invalidate();
    ShellExecute(m_hWnd, "open", "http://crowback.tistory.com", NULL, NULL, SW_SHOW);
}

여기서 유용한 함수중에 하나가 ShellExecute라는 함수인데.. 외부 프로그램을 실행시킬때 주로
사용하는 기능이다.

설명은 길지만 전체 추가한 라인수는 대략 30라인 정도밖에는 되지않는 아주 간단한 코드이다.
이렇게 하여 하이퍼링크 하나를 넣기 위하여 새로운 클래스를 추가하는 번거로움을 막을 수 있다.

29.zip
0.04MB

 

스태틱 컨트롤중에 Text 기능을 이용하여 배경을 투명하게 만드는 것은 에디트 컨트롤에 비하여
훨씬 쉽다.

위의 샘플이 컨트롤의 배경을 투명하게 만든것인데..

우선 글자가 써진 컨트롤의 아이디를  IDC_STATIC_1 이라고 변경한 후..
코드를 다음과 같이 변경하면 끝이다.

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   
    switch(nCtlColor)
    {
    case CTLCOLOR_STATIC:
        {
            if(pWnd->GetDlgCtrlID() == IDC_STATIC_1)
            {
                pDC->SetTextColor(RGB(255, 0, 0));
                pDC->SetBkMode(TRANSPARENT);
                return (HBRUSH)GetStockObject(NULL_BRUSH);;
            }
        }
    }
    // TODO: Return a different brush if the default is not desired
    return hbr;  
}

25.zip
0.04MB

에디트 컨트롤 편에서와 마찬가지고 WM_CTLCOLOR 라는 이벤트를 이용하여 글자색과 배경색을 변경할 수 있다.

클래스 위저드에서 WM_CTLCOLOR 을 선택한 후에 Add Function 버튼을 눌러 이벤트 핸들러를 추가한다.

저걸 추가하고 나면 화면상에 다음과 같은 코드가 자동으로 추가됩니다.

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   
    // TODO: Return a different brush if the default is not desired
    return hbr;
}

1. CDC* pDC - MFC
에서 제공하는 디바이스 컨텍스트
2. CWnd* pWnd -
대상이 되는 다이알로그에 올려진 컨트롤 윈도우
3. UINT nCtlColor - 
대상이 되는 컨트롤의 구분 타입.

3
번의 대상이 되는 컨트롤의 구분 타입은 다음과 같습니다.
이는 winuser.h에 선언되어 있구요.
#define CTLCOLOR_MSGBOX         0
#define CTLCOLOR_EDIT                1
#define CTLCOLOR_LISTBOX          2
#define CTLCOLOR_BTN                 3
#define CTLCOLOR_DLG                 4
#define CTLCOLOR_SCROLLBAR     5
#define CTLCOLOR_STATIC             6

나는 Text 컨트롤을 대상으로 작업을 하므로 CTLCOLOR_STATIC 를 사용할 것입니다.
그럼 다음과 같은 코드를 추가해 봅니다.

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   
    switch(nCtlColor)
    {
    case CTLCOLOR_STATIC:
        {
            pDC->SetTextColor(RGB(255, 0, 0));
            pDC->SetBkColor(RGB(0, 255, 0));
        }
    }
    // TODO: Return a different brush if the default is not desired
    return hbr;
   
}

위와 같이 설정하면 모든 스태틱 컨트롤에 영향을 준다. 실행 결과 화면은 다음과 같다.

필요한 컨트롤만 색상을 변경하려면 다음과 같은 추가 작업이 필요하다.
좌측 상단의 첫번째 컨트롤의 속성창을 열어서 아이디를  IDC_STATIC_1 이라고 변경한다.
그리고 다음과 같이 코드를 변경한다.

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   
    switch(nCtlColor)
    {
    case CTLCOLOR_STATIC:
        {
            if(pWnd->GetDlgCtrlID() == IDC_STATIC_1)
            {
                pDC->SetTextColor(RGB(255, 0, 0));
                pDC->SetBkColor(RGB(0, 255, 0));
            }
        }
    }
    // TODO: Return a different brush if the default is not desired
    return hbr;
}


실행 결과 화면은 다음과 같다

23.zip
0.04MB

의미.
화면상에 간단하게 문자열을 보여주는 것으로 부터... 비트맵 및 아이콘을 폼에 보여주는 것등의 기초 기능을 담당하고 있다.

기본기능.
1. 필요한 곳에 간단한 문자열등을 보여준다. (에디팅은 않된다.)
2. 비트맵과 아이콘과 같은 것들을 간단한 작업으로 화면에 보여줄 수 있다.
3. 간단한 도형의 출력을 도와준다.
4. 메타 파일을 보여줄 수 있다.

설명이 좀 이상하네??? 스태틱인데 왠 이미지???
리소스 창에서는 Text 컨트롤(스태틱 컨트롤)과 Picture 컨트롤로 분리되어 있지만 모두 CStatic으로 구현된
것들이다. 이를 ActiveX 로 만들면서 대표적인 기능으로 분리해 놓았을 뿐 모두 CStatic로 구현 가능하다.

 

리소스 편집창에서 오른쪽의 빨강 똥그라미가 Text 컨트롤, 파랑 똥그라미가 Picture 컨트롤이다. 모두 CStatic에서 제공하는 기능이지만, 문자열과 그림이라는 기준으로 분리되어 다루기 쉽게 해놓았다.

왼쪽의 [난 Text 컨트롤] 이라고 표현되어 있는 모든 것이 Text 컨트롤에 각각의 속성을 부여하여 여러가지 형태로 표현된 것이고,오른쪽에 보여지는 것이 Picture 컨트롤을 이용하여 속성을 부여해본 것들이다.

앞으로 하나의 CStatic 컨트롤을  Text와 Picture 두가지 관점에서 논할것이여, 가끔 짬뽕이 될 수도 있다 --;

컨트롤이 제공하는 속성을 살펴보자. 우선 Text 관점에서 먼저 본다.
프로퍼티의 General 탭의 설명은 생략한다. 중복되는 넘이므로 [에디트 컨트롤 기초편을 참조한다.]

1. Align Test - 문자열을 수평 왼쪽, 가운데, 오른쪽으로 정렬한다.
2. Center Verically - 문자열을 수직 가운데로 정렬한다.
3. No prefix - 윈도우 컴포넌트의 문자열중에 '&' 는 다음 문자에 _ 선을 그어준다. 이 기능을 제거한다.

그림을 보면 위에꺼는 디폴트, 아래껏은 No Prefix 옵션을 준 것이다.
4. No warp - 문자열의 길이가 컨트롤 너비보다 크면 자동으로 줄바꿈하여 다음줄에 그려진다. 이 기능을 끈다.

그림을 보면 아래쪽이 No Warp 옵션을 사용한 것이다.
5. simple  - 말 그대로 모든 속성을 포기하고, 기초 속성만을 사용함을 표시한다.
6. Notify - 내부 노티파이 이벤트를 부모에게 알려준다.
7. Sunken - 테두리 속성중에 가라 앉은 듯한 효과를 준다. 첫번째 그림중 왼쪽 5번째.
8. Border - 테두리 속성중에 검은색 사각 테두리. 첫번째 그림중 왼쪽 4번째.

1. Client edge -  확장 속성인것만 빼고, 위의 7번과 같다.
2. Static edge - 확장 속성인것만 빼고, 위의 8번과 같다.
3. Modal frame - 다이알로그 처럼 툭 튀어나온것 처럼 보여준다.
4. Transparent - 배경을 그리지 않는다.
5. Accept files - 파일을 드래그 하여 떨구면 이벤트를 발생시킨다.
6. Right aligned text - 문자열을 오른쪽 정렬한다.
7. Right-to-left reading order - 딴 나라를 위하여 오른쪽부터 왼쪽으로 문자열을 오더링한다.

컨트롤이 제공하는 속성을 살펴보자. Picture 관점에서 본다.

1. Type - 컨트롤의 타입을 설정한다.
    Frame - 그냥 비어있는 사각 틀로써 형태를 제공한다.
    Rectangle - 채워진 사각 틀로써 행테를 제공한다.
    Icon - 아이콘을 그려준다.
    Bitmap - 비트맵을 그려준다.
    Enhanced Metafile - 메타 이미지 파일을 그려준다.
2. Image - 위에서 Icon, Bitmap, Enhances Metafile 일경우만 활성화 되고, 현재 리소스 중에서 아이디를 선택할 수 있도록 해준다.
3. Color - 위에서 frame과 Rectangle 속성일 경우 색상을 지정할 수 있도록 해준다.

2번째(Styles)와 3번째(Extended Styles) 속성은 Text 컨트롤과 동일하다.

참고.
Text와 Picture 컨트롤은 다른 컨트롤과 달리 처음에 생성하면 모든 컨트롤의 아이디가

IDC_STATIC

이다. 다른 컨트롤은 뒤에 1, 2 처럼 숫자가 붙는데 좀 특이하다.

이유는 보통 리소스 편집창에서 주는 속성 말고는 코딩으로 먼가를 건드릴 일이 없기 때문에 특별하게 취급된다. 그러므로 코딩에서 저걸 건드릴려면 IDC_STATIC_MYSTATIC과 같이 변경을 가해줘야한다.

IDC_STATIC는 시스템 아이디이기 때문에  GetDlgItem(IDC_STATIC)와 같이 사용할 수 없다.

지금까지 진행해 왔던 과정을 돌이켜 보고, 몇가지 정리를 하면서 이러한 기능을 대부분 수용하는 샘플을 만들어 보았다.

1. 부모 다이알로그와 자식 다이알로그간의 데이터 전달.
2. 입력된 데이터의 올바른지 판단여부.
3. 최대 입력 수 재한
4. 데이터의 저장 및 비교
5. 글자 색상 변경
6. 다이알로그 배경색 변경

처음 프로그램을 실행 시키면 뜨는 화면

우선 가입하기 버튼을 누르면 조인할 수 있는 창이 뜬다.

우선 아이디를 입력한다. 아이디는 4-8자리 까지 제한이 있고,
영문과 숫자만 입력받는다.
[한글을 넣으면 오동작을 할 수 있다. 한글 처리는 여기서 다루지 않는다.]

아이디를 입력하고 중복 확인을 누른다.

암호를 입력하는 창이 활성화 되고 가입 버튼이 함께 활성화 된다.
적당하게 암호를 입력하고 가입을 누른다.

로그인 버튼이 활성화 되었다. 로그인 버튼을 눌러 보자.

아까전에 넣었던 정보를 정확하게 입력하면 모든 과정이 완료된다.

그러면 아래와 같은 환영 메시지가 출력되면서 모든 기능이 완료된다.

26.zip
0.05MB

그림과 같이 에디트 컨트롤이 5개 있는 화면이라고 가정해 보자..
위 화면에서 시리얼 키를 입력받는 다고 가정할 때.. 숫자영어로 조합된 5개 문자씩을 각각의
에디트에 입력받으려면 어떻게 구현해야 할까?

또한 에디트 하나에 5개의 문자가 입력되면 어떻게 자동으로 다음 에디트로 넘어갈까?
요런 자동화가 되어있지 않다면 하나씩 입력하고 5개 확인하고 탭 누르고 또 입력하고 탭 누르고..


그렇게 간단한건 아니지만 사용자 편의를 제공해야 하는 입장이라면 구현해야 할것이다.
아래는 WM_KEYDOWN메시지 하나로 몽땅 처리한 구조이다.
여러가지 방법이 존재하겠지만.. 이렇게도 구현할 수 있음을 보여주는 샘플이므로 참조해보자.

코드 자체가 난해하거나 어려운것이 아니므로, 하나씩 의미를 파악해 보면서 이렇게도 사용할 수 있구나
라는 것을 즐겨보자.. ^^;

BOOL CSssDlg::PreTranslateMessage(MSG* pMsg)
{
    if(pMsg->message == WM_KEYDOWN)
    {
        CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
        CString str;
       
        if(isalnum(pMsg->wParam))
        {
            switch(pWnd->GetDlgCtrlID())
            {
            case IDC_EDIT1:
            case IDC_EDIT2:
            case IDC_EDIT3:
            case IDC_EDIT4:
            case IDC_EDIT5:
                GetDlgItemText(pWnd->GetDlgCtrlID(), str);
                if(str.GetLength() == 5)
                {
                    CEdit* pEdit = (CEdit*)pWnd;
                    int s, e;
                   
                    pEdit->GetSel(s, e);
                    if(s != e)
                        return CDialog::PreTranslateMessage(pMsg);
                   
                    if(pWnd->GetDlgCtrlID() != IDC_EDIT5)
                    {
                        GetDlgItem(pWnd->GetDlgCtrlID()+1)->SetFocus();
                        ((CEdit*)GetDlgItem(pWnd->GetDlgCtrlID()+1))->SetSel(0, -1);
                    }
                   
                    if(pWnd->GetDlgCtrlID() != IDC_EDIT5)
                        GetDlgItem(pWnd->GetDlgCtrlID()+1)->PostMessage(WM_KEYDOWN, pMsg->wParam, pMsg->lParam);
                   
                    return TRUE;
                }
                break;
            }
        }
        else if(pMsg->wParam == VK_BACK)
        {
            switch(pWnd->GetDlgCtrlID())
            {
            case IDC_EDIT1:
            case IDC_EDIT2:
            case IDC_EDIT3:
            case IDC_EDIT4:
            case IDC_EDIT5:
                GetDlgItemText(pWnd->GetDlgCtrlID(), str);
                if(str.GetLength() == 0)
                {
                    if(pWnd->GetDlgCtrlID() != IDC_EDIT1)
                        GetDlgItem(pWnd->GetDlgCtrlID()-1)->SetFocus();
                    return TRUE;
                }
                break;
            }
        }
        else
        {
            switch(pMsg->wParam)
            {
            case VK_HOME:
            case VK_END:
            case VK_UP:
            case VK_DOWN:
            case VK_LEFT:
            case VK_RIGHT:
            case VK_TAB:
                break;
            default:
                return TRUE;
            }
        }
    }
   
    return CDialog::PreTranslateMessage(pMsg);
}

아래는 구현된 샘플입니다.

17.zip
0.04MB

대부분의 컨트롤을 다룰 때 초기에는 별 필요성이 없다가, 이것 저것 기능을 처리할려다 보면 꼭 필요한
것들이 바로 이벤트를 핸들링 하는 일이다.
정확한 개념 이해를 위해서 MSDN의 원문을 첨가한다. 물론 딸려오는 한글 설명은 번역한 내용을 기술하는 것이 아니고, 그냥 정리된 개념을 서술하는 것이다.

에디트 컨트롤에 특화되어 제공되는 이벤트는 다음과 같이 8개가 있다.

1. EN_CHANGE
The EN_CHANGE notification message is sent when the user has taken an action that may have altered text in an edit control. Unlike the EN_UPDATE notification message, this notification message is sent after the system updates the screen. The parent window of the edit control receives this notification message through a WM_COMMAND message.

위 이벤트는 에디트 박스에서 데이터를 입력하거나 수정, 제거 등의 행동을 취하면 발생하는 이벤트이다.
이런걸 사용하는 이유는 방금 입력되거나 수정된 데이터가 올바른지, 혹은 어떤 정해논 범위의 캐릭터가 들어오는지, 아니면 범위를 초과하는지 등등등.. 변화에 민감하게 바로 바로 처리할 수 있도록 할 때 사용한다.

예를 들면 입력을 16진수로만 받아 들인다고 가정했을 때 입력 받을 수 있는 범위는
012345678ABCDEFX이다. 방금 입력된 값이 저 범위에 들어가지 않는다면 삑 소리를 내주거나 메시지 박스등을 뛰워 사용자로 부터 잘못된 입력이 있음을 경고해 줄 수도 있다.

2. EN_ERRSPACE
The EN_ERRSPACE notification message is sent when an edit control cannot allocate enough memory to meet a specific request. The parent window of the edit control receives this notification message through a WM_COMMAND message.

개발하면서 사실상 한번도 써본적이 없는 메시지이다. 에디트에 어떤 입력이 있을 경우 당연히 그것을 저장할 공간이 할당되어야 한다. 그런데 너무 많이 데이터를 넣어서 더이상 처리될 공간이 없을 때 발생하는 메시지이다. 요즘 윈도우에선 사실상 걸어놔도 걸릴지 의문이다 -_-;

3. EN_HSCROLL, EN_VSCROLL
The EN_HSCROLL notification message is sent when the user clicks an edit control's horizontal scroll bar. The parent window of the edit control receives this notification message through a WM_COMMAND message. The parent window is notified before the screen is updated.

데이터를 입력하면서 수평/수직 스크롤바에 크기 변경이 생기거나, 사용자가 에디트 박스의 수평/수직 스크롤바를 이동시키면 발생하는 이벤트이다. 이 이벤트 또한 사용자가 에디트 내부를 직접 건드리거나 하지 않는 이상 자주 사용되지 않는다. 샘플을 기준으로 간단하게 어떻게 저 정보를 이용하는지 코드를 적어본다.

void CSssDlg::OnHscrollEdit1()
{
    SCROLLINFO info;
    GetDlgItem(IDC_EDIT1)->GetScrollInfo(

SB_HORZorSB_VERTorSB_BOTH

, &info);
}
위와 같이 현재 에디트 컨트롤의 포인터를 얻어와서 GetScrollInfo에 인자를 채운후 호출하게 되면 info라는 구조체에 현재 스크롤바의 모든 정보가 들어오게 된다.

4. EN_SETFOCUS, EN_KILLFOCUS
The EN_SETFOCUS notification message is sent when an edit control receives the keyboard focus. The parent window of the edit control receives this notification message through a WM_COMMAND message.

사용자가 마우스나 TAB버튼을 이용하여, 에디트 컨트롤에 포커스가 갔을 경우나, 혹은 포커스가 있는 상태에서 다른 컨트롤등으로 포커스가 이동할 경우에 발생하는 이벤트이다. 요런걸 어떨 때 사용하냐면..
검색창등에 있는 에디트 컨트롤을 보면..
<검색어를 입력하세요> 라고 써있어서, 마우스로 콕 찍으면 저 문자열이 사라지고 글자를 입력할 수 있는 상태가 되는걸 본적이 있을 것이다. 기타 등등의 이유가 많겠지만..

5. EN_MAXTEXT
The EN_MAXTEXT notification message is sent when the current text insertion has exceeded the specified number of characters for the edit control. The text insertion has been truncated.

에디트 컨트롤을 변수로 연결할 하고 나서, 클래스 위저드를 열 은 후에..


IDC_EDIT1 을 m_data라고 하는 CString 변수에 연결 한 후.. Memver Variable 라는 탭을 열어 보면 현재 다얄로그 폼에 존재하는 컨트롤과 어떤 변수가 연결되어 있는지 쭈욱 항목이 나온다.
거기서 Maximum Characters : 라는 곳에 5라고 넣었다.
의미는 현재 에디트 IDC_EDIT1에는 최대 5개까지의 문자를 입력 받을 수 있도록 제한을 걸었다는 것이다.

저렇게 제한을 걸어 놓았을 경우 6번째 문자가 입력되면 EN_MAXTEXT이벤트가 발생하게 된다.
void CSssDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CSssDlg)
    DDX_Text(pDX, IDC_EDIT1, m_data);

    DDV_MaxChars(pDX, m_data, 5);


    //}}AFX_DATA_MAP
}
라고 추가되는 것이 보일 것이다.
개념을 다시 되 집어 보면, 저걸 직접 손으로 코딩해도 하등 문제가 없다는 이야기이다.
DoDataExchange 에 변수와 컨트롤을 연결하는 DDX_ , DDV_ 계열의 매크로는 상당히 다양하지만 현재 그렇게 까지 자세하게 설명하진 않을 것이다. [궁금하면 콕 찍은 다음 F12를 눌러 보아라]

6. EN_UPDATE
The EN_UPDATE notification message is sent when an edit control is about to redraw itself. This notification message is sent after the control has formatted the text, but before it displays the text. This makes it possible to resize the edit control window, if necessary. The parent window of the edit control receives this notification message through a WM_COMMAND message.

이 이벤트는 에디트 컨트롤을 다시 그려야할 필요가 있는 모든 경우에 발생하는 이벤트이다. EN_CHANGE는 문자열에 대한 변과하 있을 때만 발생하지만, 컨트롤의 크기가 변경된다던가 스타일을 바꾼다던가.. 외관상 및 내부 데이터가 바뀔 때 모두 발생한다는 점이 다르다..

아래 그림은 예제 샘플의 화면이다. 위에 나온 내용중에 몇가지를 구현하여 보았다.

16.zip
0.04MB

+ Recent posts