이전에 이어 첵크박스가 가지고 있던 속성중에 주요한 속성들을 살펴보겠다.
속성 페이지의 Styles 에서 기존에 못보던 Tri State라는 속성을 보았을 것이다.
샘플을 수행한 화면이다.
아래 상위의 두개 첵크 박스는 모두 Tri State 속성을 부여하였고, 나머지는 아니다.
일반적으로는 첵크상태(BST_CHECKED)와 막연한 상태???(BST_INDETERMINATE) 및
언첵크 상태(BST_UNCHECKED)로 구분되며...
이번 장에서는 BST_INDETERMINATE에 대하여 자세히 살펴본다.
우선 장을 진행하기에 앞서 살펴보아야할 것들에 대하여 나열해 보겠다.
ON_COMMAND_RANGE
IsDlgButtonChecked
EnableWindow
CheckDlgButton
중요한 내용들이며 각각 다음과 같은 기능을 제공한다. 특히나 ON_COMMAND_RANGE 는 UI를 다룰 경우 상당히 유용한 매크로이므로 알아두면 써먹을 곳이 많다.
1. ON_COMMAND_RANGE
제시 : 만약에 버튼이 100개 있다면? OnButton1 ~ OnButton100 까지 100개를 다 만들것인가?
MFC에서 제공해주는 아주 유용한 매크로로 여러개의 컨트롤 아이디를 하나의 함수로 매핑해주는
기능을 제공한다. 이 기능을 이용하면 연속적인 아이디를 가진 컨트롤을 몽땅 한 함수에서 처리할
수 있으므로 불필요한 함수의 증가를 막고, 코드 관리에 아주 유용하다.
단, 기능에서 보아 짐작할 수 있겠지만.. 저 매크로는 자동화로 제공되지 않으므로 개발자가 직접
손수 타이핑해주어야 한다.
2. IsDlgButtonChecked
첵크 박스의 첵크 상태를 읽어내는 함수로 CWnd의 멤버 함수이다.
3. EnableWindow
CWnd를 상속한 컨트롤의 활성화/비활성화를 처리해주는 멤버 함수이다.
4. CheckDlgButton
첵크 박스의 여러가지 첵크 상태를 임의로 변경해 줄 수 있는 CWnd의 멤버함수이다.
---------------------------------------------------------------------------------
다이알로그 헤더에 추가될 코드
// 프로토콜 상위 첵크박스 상태.
BOOL m_protocol;
// 프로토콜 하위 첵크박스들의 상태.
UINT m_protocol_sub;
// 서포트 상위 첵크박스 상태
BOOL m_support;
// 서포트 하위 첵크박스들의 상태
UINT m_support_sub;
// 두개 그룹의 첵크 박스의 이벤트 핸들러를 묶어 놓은 Range 함수 2개.
// 리턴값은 LRESULT 이고, 인자는 UINT 타입의 인자 1개이다. 잘 기억해두자.
afx_msg LRESULT OnCheckProtocolRange(UINT nID);
afx_msg LRESULT OnCheckSupportRange(UINT nID);
---------------------------------------------------------------------------------
다이알로그 소스에 추가될 코드
// 변수 초기화
CSssDlg::CSssDlg(CWnd* pParent /*=NULL*/)
: CDialog(CSssDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CSssDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_protocol = FALSE;
m_support = FALSE;
m_protocol_sub = 0;
m_support_sub = 0;
}
// 메시지 등록
BEGIN_MESSAGE_MAP(CSssDlg, CDialog)
//{{AFX_MSG_MAP(CSssDlg)
//}}AFX_MSG_MAP
// 메시지 맵에는 아래와 같은 용법으로 등록한다.
// 첫번째는 시작되는 컨트롤 아이디,
// 두번째는 마지막 컨트롤 아이디.
// 세번째는 멤버함수 이름
ON_COMMAND_RANGE(IDC_CHECK1, IDC_CHECK3, OnCheckProtocolRange)
ON_COMMAND_RANGE(IDC_CHECK4, IDC_CHECK7, OnCheckSupportRange)
END_MESSAGE_MAP()
// 프로토콜 그룹의 첵크박스 핸들러
// 위 메시지 맵에 처럼 등록해 놓으면 IDC_CHECK1, IDC_CHDEK2, IDC_CHECK3 첵크시
// 모든 이벤트가 아래 함수로 온다. 물론 어떤 컨트롤을 건드렸는지는 nID 에 넘어온다.
LRESULT CSssDlg::OnCheckProtocolRange(UINT nID)
{
// 어떤 놈을 건드렸나?
switch(nID)
{
// 나는 상위 프로토콜 그룹 첵크 박스
case IDC_CHECK1:
{
// IDC_CHECK1 이 첵크되어있나?
// 이 코드는 Tri State를 가진 첵크 박스는 첵크를 하면 3가지 상태를 모두 토글하므로
// IDC_CHECK1을 사용자가 직접 클릭했늘 경우는 CHECK/UNCHECK상태만을
// 가지도록 처리해주는 루틴이다.
BOOL bChecked = IsDlgButtonChecked(IDC_CHECK1);
if(m_protocol == FALSE && bChecked)
CheckDlgButton(IDC_CHECK1, BST_CHECKED),
m_protocol = TRUE;
else if(m_protocol == TRUE && bChecked)
CheckDlgButton(IDC_CHECK1, BST_UNCHECKED),
m_protocol = FALSE;
// IDC_CHECK1 이 첵크되어 있다면 하부 첵크박스를 모두 활성화 시킨다.
// 아니라면 모두 비활성화 시키지...
GetDlgItem(IDC_CHECK2)->EnableWindow(m_protocol);
GetDlgItem(IDC_CHECK3)->EnableWindow(m_protocol);
return 0;
}
// IDC_CHECK1을 제외한 모든 첵크박스
default:
{
// 현재 첵크박스가 몇번째 것인지 구한다.
int n = nID - IDC_CHECK2;
// 현재 첵크박스의 상태를 구한다.
BOOL bChecked = IsDlgButtonChecked(nID);
// 첵크 상태에 따라 m_protocol_sub 을 설정한다.
if(bChecked)
m_protocol_sub |= 0x01 << n;
else
m_protocol_sub &= ~(0x01 << n);
// 상태에 따라 IDC_CHECK1의 첵크 상태를 변경시킨다.
if(m_protocol_sub)
CheckDlgButton(IDC_CHECK1, BST_INDETERMINATE);
else
CheckDlgButton(IDC_CHECK1, BST_CHECKED);
}
}
return 0;
}
// 서포트 그룹의 첵크박스 핸들러
// 코드의 내부 기능 및 동작은 OnCheckProtocolRange 와 동일하다.
// 단지 구분하기 위하여 따로 놓았을 뿐...
LRESULT CSssDlg::OnCheckSupportRange(UINT nID)
{
switch(nID)
{
case IDC_CHECK4:
{
BOOL bChecked = IsDlgButtonChecked(IDC_CHECK4);
if(m_support == FALSE && bChecked)
CheckDlgButton(IDC_CHECK4, BST_CHECKED),
m_support = TRUE;
else if(m_support == TRUE && bChecked)
CheckDlgButton(IDC_CHECK4, BST_UNCHECKED),
m_support = FALSE;
GetDlgItem(IDC_CHECK5)->EnableWindow(m_support);
GetDlgItem(IDC_CHECK6)->EnableWindow(m_support);
GetDlgItem(IDC_CHECK7)->EnableWindow(m_support);
return 0;
}
default:
{
int n = nID - IDC_CHECK5;
BOOL bChecked = IsDlgButtonChecked(nID);
if(bChecked)
m_support_sub |= 0x01 << n;
else
m_support_sub &= ~(0x01 << n);
if(m_support_sub)
CheckDlgButton(IDC_CHECK4, BST_INDETERMINATE);
else
CheckDlgButton(IDC_CHECK4, BST_CHECKED);
}
}
return 0;
}
------------------------------------------------------------------------------------
모두 7개의 첵크 박스를 하나씩 다 제어하려면 함수도 많아지고, 이만 저만 귀찬은 것이 아닐겁니다.
복잡한 UI는 수집개의 버튼과 수십개의 첵크박스, 에디트, 라디오, 에디트.. 를 동반하는데
일일이 하나씩 모두 할 필요는 없겠죠? ^^;
구지 함수를 2개가 아닌 1개로 해도 되지만, 이를 나눈 이유는 코드 수를 줄이는 것도 중요하지만
서로 다른 그룹임을 구분해 주는 것이 나중에 코드를 수정하거나 보완할때 수월하기 때문입니다.