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

스크롤바를 드래그해서 움직일 경우 잔상이 남습니다.

[요 부분에 대한 아이디어가 있으신 분은.. 메시지좀 남겨주시와요 ^^]

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


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

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);
}

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

105.zip
0.03MB

 



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

콤보에서 제공하는 이벤트는 다음과 같습니다.

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에서 찾아보시면 쉽게 이해하실 수 있을겁니다

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

77.zip
0.03MB

콤보박스를 사용하면서 글자를 출력할 경우, 기본 폰트를 사용하면 너무 위 아래가 따닥 따닥
붙어 있어 보기가 조금 답답해 보이는 경향이 있다.

콤보에서 제공해주는 가상함수  DrawItem 조금만 손보면 아주쉽게 이를 조절할  있다.
속성은 Owner Draw Fixed, Has String 속성을 주고 코드를 다음과 같이 수정하면 된다.

void CComboBoxHeight::DrawItem(LPDRAWITEMSTRUCT pDIStruct)
{
    
CDC dc;

    
// 코드를 간결하게 하기 위하여 전달된 HDC CDC 어태치한다.
    
if( !dc.Attach( pDIStruct -> hDC ) )
        
return;

    
// 현재 전달된 아이템이 선택되어진 넘인이 확인한다.
    
if( pDIStruct -> itemState & ODS_SELECTED )
    {
        
// 속성에 맞게 글자색배경색상을 지정한다.
        
dc.SetTextColor((0x00FFFFFF & ~(GetSysColor(COLOR_WINDOWTEXT))));
        
dc.SetBkColor(GetSysColor(COLOR_HIGHLIGHT));
        
dc.FillSolidRect(&pDIStruct->rcItem, GetSysColor(COLOR_HIGHLIGHT));
    }
    
else
    {
        
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
        
dc.SetBkColor(RGB(0,0,0));
        
dc.FillSolidRect(&pDIStruct->rcItem, GetSysColor(COLOR_WINDOW));
    }

    
// 아이템의 인덱스가 -1 아니면
    
if(pDIStruct->itemID != -1)
    {
        
// 선택된 아이템의 문자열을 읽어온다.
        
CString m_SelText;
        
GetLBText(pDIStruct->itemID, m_SelText);

        
// 만약 선택된 아이템이 디저블 속성이면 글자 색상을 회색으로
        
if(pDIStruct->itemState & ODS_DISABLED)
            
dc.SetTextColor(::GetSysColor(COLOR_GRAYTEXT));

        
// 배경은 투명 속성으로
        
dc.SetBkMode(TRANSPARENT);

        
// 아이템 영역의 수직 센터에 글자를 그린다.
        
dc.DrawText(m_SelText, &pDIStruct->rcItem, DT_VCENTER | DT_SINGLELINE);

        
// 필요하면 여기다비트맵을 그리거나아이콘을 그리거나 맘대로 하면 된다.
        
// 보통 아이콘을 자주 그리는데 DrawIconEx 사용하면 된다.
    }

    
dc.Detach();

    
return;
}

100.zip
0.09MB

콤보박스를 어떻게 다루어야 하는지에 대하여 알아보겠습니다.

보통 사용한다함은?
1. 데이터를 추가한다.
2. 데이터를 삽입한다.
3. 데이터 하나를 지운다.
4. 데이터를 모두 지운다.
5. 부가정보를 첨부한다.
6. 현재 선택되어진 문자열을 읽어온다.
이정도 선에서 사용하게 됩니다.

우선 콤보박스를 빈 다이알로그에 하나 올리고 아이디를 IDC_COMBO1 이라고 하고 다음과
같은 스타일로 샘플을 만들겠습니다.


다음은 CComboBox m_combo 로 컨트롤을 연결합니다.


다음으로 위에서 열거한 기능을 하나씩 버튼을 넣어가면 진행하여 보겠습니다.

1. 우선 그림처럼 버튼을 넣고, 그 버튼을 누르면 데이터를 추가합니다.

void CSssDlg::OnButton1()
{
    
m_combo.AddString("하나");
    
m_combo.AddString("");
    
m_combo.AddString("");
}

int CComboBox::AddString(LPCTSTR lpszString);
-------------------------------------------------
Parameters
    lpszString  - NULL로 끝나는 문자열
Return Value
   추가된 아이템의 크기가 0보다 크다면 Zero-Based 인덱스가 리턴된다.
   만약 CB_ERRSPACE가 리턴된다면 데이터를 저장하기 위해 할당할 메모리가 모자라다.
Remark
   만약 생성시에 CBS_SORT 스타일을 넣었다면 정렬된 위치로 삽입되게 된다.


2. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 데이터를 2번째에 삽입한다.

void CSssDlg::OnButton2()
{
   
m_combo.InsertString(1, "삽입됨");
}

인덱스가 Zero-Based Index 이기 때문에 인덱스 1에다 넣으면 2번째 위치가 된다.

int CComboBox::InsertString(int nIndex, LPCTSTR lpszString);
--------------------------------------------------------------
Parameter
   nIndex  - Zero-Based Index 이고, 만약 -1이면 마지막에 추가된다.
   lpszString - NULL로 끝나는 문자열
Return Value
   현재 삽입된 위치의 인덱스가 리턴된다.
Remark
   AddString과는 달리 CBS_SORT 스타일이라 하더라도, 삽입위치는 변경되지 않는다.


3. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 선택되어진 아이템을 삭제한다.

void CSssDlg::OnButton3()
{
    
int nIndex = m_combo.GetCurSel();
    
if(nIndex == -1)
        
return;
    
m_combo.DeleteString(nIndex);
}

아무것도 선택되지 않았거나, 아이템이 하나도 없을 경우에 대한 에러처리를 해야한다.

int CComboBox::DeleteString(UINT nIndex);
----------------------------------------
Parameter
   nIndex - 0보다 크거나 같은 삭제할 인덱스
Return Value
   0이거나 0보다 큰값으로 현재 남아있는 아이템의 카운트를 리턴한다.
Remark
   당연한 이야기겠지만, 여러개의 아이템중에 중간에 것을 삭제하면 아래의 하위 아이템들의
   인덱스가 당연히 하나씩 줄어든다. 아이템을 인덱스로 관리할 경우 에러가 나거나 오동작
   할 수 있으므로 주의해야한다.


4. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 데이터를 모두 지운다.

void CSssDlg::OnButton4()
{
   
m_combo.ResetContent();
}

루프를 돌며 하나씩 지워나갈 수도 있겠지만, 지원하는 함수가 있으니...

void CComboBox::ResetContent();
-----------------------------------
Remark
   루프를 돌면서 하나식 지우게 되면 에디트의 선택영역이 글자는 남아있게된다. 이 함수를
   이용하게 되면 에디트와 리스트영역을 모두 초기화 시킨다.

5. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 개별 아이템에 부가정보를 첨부한다.
첨부된 부가 정보를 보여주기 위하여 콤보박스 하단에 스태틱 컨트롤을 하나추가한다.
또한 아이템의 선택이 변경되었을 때마다 스태틱의 정보를 갱신해 주기 위하여 CBN_SELCHANGE
이벤트를 추가하고 핸들러에 부가 코드를 넣는다.

// 아래의 이벤트는 콤보박스에서 아이템을 선택을 변경하면 발생하는 이벤트이다.
void CSssDlg::OnSelchangeCombo1()
{
    
// 현재 선택되어진 아이템
    
int nIndex = m_combo.GetCurSel();
    
if(nIndex == -1)
        
return;    CString s;
    
// 현재 아이템의 부가 정보를 읽어온다.
    
// 여기서는 단순하게 숫자로 넣었지만포인터를 넣을  있으므로 모든 데이터를 
    
// 첨부할  있다.
    
// 디비에서 사용자 정보 전체를 읽어와서 이름만 콤보박스에 넣는다면...
    
// 나머지 정보를 구조체등에 넣은다음..  포인터를 SetItemData() 넣어두면
    
// 언제든지 쉽게 접근할  있게된다.
    
s.Format("0x%X", (UINT)m_combo.GetItemData(nIndex));
    
SetDlgItemText(IDC_STATIC_OPTION, s);
}

선택되어진 아이템의 부가정보를 읽어와 숫자로 읽어온다음 문자열로 변환하여 해당 스태틱 컨트롤에 뿌려준다.
void CSssDlg::OnButton5()
{
    
int count = m_combo.GetCount(), i;
    
for(i=0; i<count; i++)
        
m_combo.SetItemData(i, (DWORD)rand());
}


콤보박스의 갯수를 세어, 부가정보로 랜덤한 숫자를 임시로 넣어본다.

int CComboBox::SetItemData(int nIndex, DWORD_PTR dwItemData);
------------------------------------------------------------------
Parameter
  nIndex - 제로베이스 인덱스
  dwITemData - 아이템에 넣을 부가정보의 포인터, 데이터를 넣을 경우는 DWORD로 타입 캐스팅 하여 넣으면 된다.
Return Value
   현재 아이템의 제로베이스 인덱스
Remark
   만약 new 등을 이용해 할당한 것이라면 수동으로 delete 해주어야 한다.

6. 다음 그림처럼 버튼을 넣고, 그 버튼을 누르면 선택된 아이템의 캡션을 읽어온다.

void CSssDlg::OnButton6()
{
    
int nIndex = m_combo.GetCurSel();
    
if(nIndex == -1)
        
return;    CString s;
    
m_combo.GetLBText(nIndex, s);
    
AfxMessageBox(s);
}

선택되어진 아이템의 인덱스를 구한 후, 아이템의 문자열을 읽어와 메시지박스로 뿌려준다.

void CComboBox::GetLBText(int nIndex, CString& rString) const;
----------------------------------------------------------------
Parameter
  
nIndex - 제로베이스 인덱스
  rString - 읽어올 문자열을 저장할 객체.

그 외에도 콤보박스에서 제공되는 함수나 기능들은 좀더 다양하게 있지만 그렇게 자주 쓰이거나 하지는 않는다.
다음 장에서 콤보박스의 이벤트를 다루며 몇몇 다른 함수들의 기능도 살펴보자.

74.zip
0.03MB

이전에 이어 콤보박스가 여러개 있을 때, 각각의 콤보 색상을 어떻게 다르게 처리할 수 있는지에
대하여 진행해 보겠습니다.

우선 다이알로그에 콤보1, 콤보2 이렇게 2개를 올려놓고 진행합니다.
먼저 에디트 색상부터 변경해 보겠습니다.

[드롭다운 스타일의 콤보박스 2개를 준비합니다.]

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    
if(nCtlColor == CTLCOLOR_EDIT )
    {
        
if(pWnd->GetDlgCtrlID() == 1001)
        {
            
// 부모 윈도우가 NULL 이면 리턴한다.
            
if(pWnd->GetParent() == NULL)
                
return hbr;

            
// 부모 윈도우의 컨트롤 아이디를 구한다.
            
// 아이디가 아래와 같으면 에디트의 색상을 변경시키다.
            
switch(pWnd->GetParent()->GetDlgCtrlID())
            {
            
case IDC_COMBO1:
                
pDC->SetTextColor(RGB(0,255, 0));
                
pDC->SetBkColor(RGB(255, 0, 0));
                
break;
            
case IDC_COMBO2:
                
pDC->SetTextColor(RGB(0,0, 255));
                
pDC->SetBkColor(RGB(255, 255, 0));
                
break;
            }
        }
    }

    
return hbr;
}

결과화면...

위의 코드에서 보았듯이 콤보박스의 에디트는 부모윈도우가 콤보박스입니다.
그렇다면 리스트의 색상을 변경해 보도록 하죠...

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    
if(nCtlColor == CTLCOLOR_LISTBOX )
    {
        
if(pWnd->GetDlgCtrlID() == 1000)
        {
            
// 현재 포커스가 있는 윈도우를 구한다.
            
pWnd = pWnd->GetFocus();
            
if(pWnd && pWnd->GetParent())
            {
                
// 심플이나 드롭다운일 경우는 에디트에 포커스가 있으므로
                
// 페어런트의 컨트롤 아이디를 구한다페어런트가 콤보박스이다.
                
switch(pWnd->GetParent()->GetDlgCtrlID())
                {
                
case IDC_COMBO1:
                    
pDC->SetTextColor(RGB(0,255, 0));
                    
pDC->SetBkColor(RGB(255, 0, 0));
                    
return brh;
                
case IDC_COMBO2:
                    
pDC->SetTextColor(RGB(255,255, 0));
                    
pDC->SetBkColor(RGB(255, 0, 0));
                    
return brh;
                }
            }
        }
    }

    
return hbr;
}

실행화면


예상했던것과는 코드가 좀 다르죠?
에디트의 페어런트는 콤보박스이지만, 리스트의 페어런트는 콤보박스가 아니고...
템퍼러리 윈도우가 됩니다.
저 리스트박스가 뜨는 원리는 흡사 메뉴가 뜨는것과 비슷한 형식으로 구동되는것 같은데
내부 로직이야 알 수가 없으니.. -_-;;;

리스트가 나올려면 에디트를 꾹 눌러야 나오니까..
현재 포커스가 당연히 있을 테고, 그 포커스가 있는 에디트를 기준으로..
콤보박스의 아이디를 구한겁니다.

이렇게 드롭다운 스타일일 경우는 에디트를 기준으로 콤보박스의 아이디를 구했는데..
드롭리스트 스타일은 에디트가 아니라 스태틱 컨트롤이라고 이전 장에서 알려드렸습니다.
그래서 드롭 리스트 스타일일 경우는 저 방법으로 처리가 불가능해집니다.

그럼 드롭다운 콤보와 드롭리스트 콤보를 2개 놓고 색상을 처리해 보도록 하겠습니다.

[콤보2를 드롭리스트 스타일로 변경합니다.]

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    
if(nCtlColor == CTLCOLOR_LISTBOX )
    {
        
if(pWnd->GetDlgCtrlID() == 1000)
        {
            
// 현재 포커스가 있는 윈도우를 구한다.
            
pWnd = pWnd->GetFocus();
            
if(pWnd && pWnd->GetParent())
            {
                
// 심플이나 드롭다운일 경우는 에디트에 포커스가 있으므로
                
// 페어런트의 컨트롤 아이디를 구한다페어런트가 콤보박스이다.
                
switch(pWnd->GetParent()->GetDlgCtrlID())
                {
                
case IDC_COMBO1:
                    
pDC->SetTextColor(RGB(0,255, 0));
                    
pDC->SetBkColor(RGB(255, 0, 0));
                    
return brh;
                
case IDC_COMBO2:
                    
pDC->SetTextColor(RGB(255,255, 0));
                    
pDC->SetBkColor(RGB(255, 0, 0));
                    
return brh;
                }
            }
        }
    }

    
return hbr;
}

이로써 콤보박스가 여러개 또는 여러가지 스타일로 섞여 있더라도 개별적으로 찾아서
원하는 색상과 배경을 넣어 줄 수 있게 되었습니다.

콤보의 색상을 처리하면서 느꼇던 점은.. ( 사실 콤보 색상변경은 저도 이번에 첨 해본겁니다. )
윈도우는 어떻게든 부모, 자식, 형제를 찾다보면 모두 거미줄 처럼 이어져 있기 때문에..
언젠가는 원하는 것을 찾을 수 있다는 점이었습니다.

자, 뒤바뀐 순서를 다시 복귀 시켜서 다음에는 콤보에 데이터를 추가하고, 삭제하고 하는 등의
기본적인 용법에 대하여 다루어 보겠습니다.

95.zip
0.15MB

이전에는 콤보박스의 프로퍼티에 대하여 살펴보았다.

이번에는 기본적인 사용법에 대하여 알아보고자 하였지만 필요성에 의하여
글자 색상  배경 색상을 변경하는 것을 먼저 다루고자한다.

콤보박스의 글자와 배경색을 바꾸는  강좌를 진행하면서, 다른 사이트에 올라온 자료들은
어떻게 색상을 변경하였는지를 살펴보았는데, 아쉽게도 만족할 만한 자료를 찾지 못하였다.

 살펴보아야 할것이 콤보박스는 두개의 컨트롤 조합으로 구성되어 있다는 것이다.

심플 / 드롭다운 스타일 경우
콤보박스 == 에디트 컨트롤 + 리스트 박스

드롭 리스트 스타일 경우
콤보박스 == 스태틱 컨트롤(에디트를 가장한) + 리스트 박스

얼핏보면 이상하지만 기능을 생각해보면 이상할 것도 없다.
[
 그렇다면 콤보를 이해하면 리스트 박스도 색상을 바꾸는데 문제 없겠군 ^^;]


우선 콤보박스가 하나라고 생각하고 색상을 먼저 바꾸어보자..
---------------------------------------------------------------------------
헤더에 CBrush m_brush  하나 선언한다.

소스는 다음과 같다.
BOOL CSssDlg::OnInitDialog()
{
    
CDialog::OnInitDialog();
    
brh.CreateSolidBrush(RGB(255, 0, 0));
    
return TRUE;
}

HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    
// 콤보박스는 2개의 컨트롤로 구성되어 있으므로 각각에 대하여 색상을 변경해 주어야 한다.
    
if(nCtlColor == CTLCOLOR_EDIT )
    {
        
pDC->SetTextColor(RGB(0,255, 0));
        
pDC->SetBkColor(RGB(255, 0, 0));
    }
    
else if(nCtlColor == CTLCOLOR_LISTBOX )
    {
        
pDC->SetTextColor(RGB(0,255, 0));
        
pDC->SetBkColor(RGB(255, 0, 0));
        
return brh;
    }

    
return hbr;
}

수행된 화면은 아래와 같다.

우선 주의할 점이 에디트의 글자 색상을 변경할 경우 컨트롤 아이디를 특별하게 구분하지
않았기 때문에.. 좌측의 에디트 컨트롤도 영향을 받았다.

그렇다면 코드를 아래와 같이 바꾸면 콤보박스의 에디트만 색상을 바꿀  있는가?
HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    
if(nCtlColor == CTLCOLOR_EDIT )
    {
        
if(pWnd->GetDlgCtrlID() == IDC_COMBO1)
        {
            
pDC->SetTextColor(RGB(0,255, 0));
            
pDC->SetBkColor(RGB(255, 0, 0));
        }
    }
    
else if(nCtlColor == CTLCOLOR_LISTBOX )
    {
        
pDC->SetTextColor(RGB(0,255, 0));
        
pDC->SetBkColor(RGB(255, 0, 0));
        
return brh;
    }

    
return hbr;
}

기대했던 결과와는 다르게 다음과 같은 그림이 나온다.

그렇다면 콤보박스의 에디트 부분은 별도의 컨트롤 아이디를 가진다는 말인가????

심플 / 드롭다운 스타일 경우
콤보박스(1033) == 에디트 컨트롤 (1001) + 리스트 박스 (1000)

드롭 리스트 스타일 경우
콤보박스(1033)  == 스태틱 컨트롤 (1033) + 리스트 박스 (1000)

위에서 아이디 1000 1001  어디서 나온놈일까?
해당 콤보박스가 에디트와 리스트로 조합되어있다는 이야기를 이전에 하였을 것이다.
이는 MFC 내부 시스템 리소스를 사용하여 구성되었고 각각의 아이디가 위와 같은 것이다.

그러므로 사용자가 작성한 다이알로그의 컨트롤 중에 1000 이나, 1001  있고
 두개의 컨트롤이 각각 에디트와 리스트라면 구분이 용이하지 않게 된다.

우선 다음과 같이 코드를 수정해 보자.
HBRUSH CSssDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    
if(nCtlColor == CTLCOLOR_EDIT )
    {
        
if(pWnd->GetDlgCtrlID() == 1001)
        {
            
pDC->SetTextColor(RGB(0,255, 0));
            
pDC->SetBkColor(RGB(255, 0, 0));
        }
    }
    
else if(nCtlColor == CTLCOLOR_LISTBOX )
    {
        
if(pWnd->GetDlgCtrlID() == 1000)
        {
            
pDC->SetTextColor(RGB(0,255, 0));
            
pDC->SetBkColor(RGB(255, 0, 0));
            
return brh;
        }
    }

    
return hbr;
}

원하는 결과 화면이 나왔다.

콤보박스가 하나일 경우는 이러한 방법으로 원하는 결과를 가져오는데 전혀 문제가 없다.
단, 콤보박스가 여러개일 경우 각각의 색상을 모두 다르게 처리하려면 몇가지 난관이 남게된다..

이것은 다음장에서 다루어 보자..

68.zip
0.15MB

 

CComboBox 컨트롤에 대하여 알아보자.

리소스 편집창에서 툴바에 있는 빨간색 아이콘.. 이 콤보 박스를 나타내는 아이콘이다.
마우스로 드래그 하거나, 클릭하여 놓으면 된다.

콤보박스는 2가지로 구성이 되어있는데..
첫번째는 먼저 현재 선택된 아이템을 보여주는 에디트부분과
데이터 항목을 쭈욱 열거해주는 리스트 부분으로 나누어진다.
이러한 속성을 먼저 꾸며 놓은 것이 위의 3가지 대표적인 타입니다.

Simple - 에디트와 리스트가 한번에 모두 펼쳐진 상태로 보여진다. 에디트는 수정 가능.
Dropdown - 리스트는 감추어진 상태이고, 에디트 우측의 버튼을 누르면 아래로 리스트가 쭈욱 늘어나는 스타일이다. 에디트는 수정 가능
Drop List - 기본 속성은 Dropdown과 같으나 에디트 영역을 사용자가 수정할 수 없다.

처음에 콤보를 접하여 열심히 데이터를 넣은 후 다이알로그에 올려놓고..
콤보를 딱 선택하였는데.. 아래 그림처럼 나왔었다.

도대체 내가 넣어논 데이터는 어디로 사라진걸까? -_-;;;
비주얼 스튜디오 2003부터는 달라졌지만, 6.0에서는 지금도 저렇다.. 패치도 않하나 --^;

저런 현상을 막아주기 위해서, 리소스 편집창에서 편집할 경우, 콤보 박스의 우측 버튼을 마우스로 눌러주면 위 그림처럼 주변의 트랙커가 활성화 된다. 마우스로 찍을 때마다 토글 되므로 그림처럼 활성화된 트랙커를 이용하여 크기를 변경할 수 있다.

콤보박스는 주로, 여러가지 선택 아이템중에 하나를 고를 경우에 주로 사용된다. 물론
내부 구조를 좀 손봐서 동시에 여러개를 선택할 수 있도록 첵크박스를 넣거나 하기도 하지만
기본적인 속성은 그렇다. (다나와사이트의 목록 선택창)

다음으로 콤보박스의 기본 속성에 대하여 살펴보자.
속성창에는 4개이 페이지가 존재하는데, General 및 Extended Styles는 거의 모든 컨트롤의
공통속성이기도 하고, 이전 강좌(에디트 기초01)에서 설명하였으므로 생략하도록 한다.

콤보박스 속성창중 Data 페이지.

데이터 페이지의 하얀색 에디트 영역은 콤보박스에 미리 필요한 데이터를 넣어 둘 수 있도록
제공되는 입력창이다. 간단하고 미리 픽스된 크기의 데이터들은 넣어두면 좋다.

주의1. 데이터 한줄을 넣고, ENTER를 치면 창이 닫겨 버리거나 포커스를 잃을 수 있다.
다음줄로 넘어가려면 Ctrl + ENTER로 넘겨주어야 한다.

주의2. 한글을 입력하고 있던 경우는 마우스커서나 키보드 에로우키를 이용하여 현재 줄의
마지막으로 이동해 주어야 Ctrl + ENTER 가 먹는다.

콤보박스의 속성중 Styles 페이지.

1. Type
기본 설명은 위에서 달았으므로, 참고할 MSDN 영문 자료를 넣었다.
CBS_DROPDOWN   Similar to CBS_SIMPLE, except that the list box is not displayed unless the user selects an icon next to the edit control.
CBS_DROPDOWNLIST   Similar to CBS_DROPDOWN, except that the edit control is replaced by a static-text item that displays the current selection in the list box.
CBS_SIMPLE   The list box is displayed at all times. The current selection in the list box is displayed in the edit control.

2. Owner draw
이 속성은 콤보박스를 다루는 기초과정에서는 사실상 사용하지 않는다.
상당한 노가다 작업이 필요하고 내부 로직을 알고 있어야하므로.. 응용편정도에서 다룰 것이다.

No - 현재의 기본 속성을 유지한다.
Fixed - 사용자가 콤보박스의 리스트 박스 영역을 임의로 정의하여 그릴 수있다. 모든
           아이템의 높이가 동일하게 적용된다.
Variable - 사용자가 콤보박스의 리스트 박스 영역을 임의로 정의하여 그릴 수있다. 모든
           아이템의 높이를 개별적으로 다르게 설정할 수 있다.

3. Has strings
Owner draw 속성을 No가 아닌 것으로 선택하여 데이터를 사용자가 정의하여 그릴때 적용되는
속성으로 문자열 데이터를 다룬다고 알려준다.

주의. Owner draw 로 데이터를 처리할 경우 위 속성을 정의하여 주지 않으면 GetText 멤버를
이용하여 문자열을 읽어올 수 없게된다.

4. Sort
내부에 존재하는 문자열 데이터를 자동으로 소팅해준다.

5. Vertical Scroll
데이터가 화면 출력범위를 넘을 경우 스크롤바를 생성해준다.

6. No Integal height
콤보박스가 생성될 때 아래로 늘어지는 리스트 박스의 크기를 보통 사용자가 정해놓는다.
이 경우, 아이템의 하나의 폭의 배수가 이 리스트 전체 크기와 맞지 않으면 시스템에서 크기를
조절하여 아이템에 맞도록 자동으로 조절한다.
만약 이 옵션을 켜 놓으면 크기가 맞지 않는 경우에.. 나머지 만큼의 부분을 보여주게 된다.

7. OEM Convert
ANSI Character Set 을 OEM Character Set 으로 변경한다. 기본적으로 Windows NT/2000/XP는
ANSI 캐릭터 셑의 Escape Character를 지원하지 않는다. 이러한 Windows 캐릭터 셑을 DOS
캐릭터 셑으로 변환하는 것을 ANSI to OEM conversion 이라고 부른다.

[캐릭터 셑에 대하여 자세히 알지 못하는 관계로 써본적이 없는 속성입니다. -_-...]
[위 내용을 이해하는데 도움이 되는 페이지.
http://www.bribes.org/perl/wANSIConsole.html ]

8. Auto HScroll
선택된 문자열이 에디트 영역에 출력될 경우 너비가 넘어가면 자동으로 스크롤 될 수 있도록
지원해준다. 이를 설정하지 않으면 전체 문자열을 볼 수 없다.

9. Disable no scroll
데이터들이 들어있는 리스트 박스에 데이터가 충분치 않아 스크롤바가 보여질 필요가 없더라도
이 옵션이 켜져 있으면 Disable 된 스크롤바가 보여진다.

10. Upper/Lower Case
입력된 문자열 중에 영문을 대문자/소문자로 자동 전환해 준다.

+ Recent posts