[MFC] CListCtrl SetItemData, GetItemData – 아이템에 데이터 저장하기

이번 글에서는 리스트 컨트롤의 SetItemData와 GetITemData에 대해 알아보겠습니다.
위 함수는, 리스트 컨트롤의 아이템에 데이터를 설정하거나 가져오는 함수 입니다.
예를들어 설명하겠습니다. 아래와 같이 학생 정보를 표시하는 클래스가 정의 되어 있다고 봅시다.

// 학생 클래스
class CStudent
{
public:
	CStudent();
	~CStudent();
    
protected:
	TCHAR m_tzName[MAX_PATH]; // 이름
	int m_iAge; // 나이
	TCHAR m_tzPhone[MAX_PATH]; // 핸드폰 번호

public:
	void SetInfo(CString strName, int iAge, CString strPhone);

	CString GetName(void);
	CString GetAge(void);
	CString GetPhone(void);
};

학생에 대한 정보를 아래와 같이 리스트 컨트롤에 표현할 수 있습니다.

// 리스트 컨트롤 초기화
m_ListCtrl.InsertColumn( 0, _T( "번호" ), LVCFMT_CENTER, 100 );
m_ListCtrl.InsertColumn( 1, _T( "이름" ), LVCFMT_CENTER, 100 );
m_ListCtrl.InsertColumn( 2, _T( "나이" ), LVCFMT_CENTER, 100 );
m_ListCtrl.InsertColumn( 3, _T( "핸드폰" ), LVCFMT_CENTER, 100 );

// 학생 정보
CStudent Students[3];
Students[0].SetInfo( _T( "김민수" ), 10, _T( "010-1111-1111" ) );
Students[1].SetInfo( _T( "박동현" ), 20, _T( "010-2222-2222" ) );
Students[2].SetInfo( _T( "최덕기" ), 30, _T( "010-3333-3333" ) );

// 리스트에 표시
for ( int i = 0 ; i < 3 ; i++ )
{
	CString strIndex;
	strIndex.Format(_T("%d"), i);
	m_ListCtrl.InsertItem( i, strIndex );
	m_ListCtrl.SetItemText( i, 1, Students[i].GetName() );
	m_ListCtrl.SetItemText( i, 2, Students[i].GetAge() );
	m_ListCtrl.SetItemText( i, 3, Students[i].GetPhone() );		
}

이 경우가 제일 일반적인 리스트 컨트롤의 표시 방법입니다.
만약에 개인정보 보안이 강화되어 이름과 핸드폰번호를 표시하지 않아야 한다면 어떻게 해야 할까요?
아래 코드와 같이 “****”로 암호화 하여 표시하는 방법이 있습니다.

// 리스트에 표시
for ( int i = 0 ; i < 3 ; i++ )
{
	CString strIndex;
	strIndex.Format(_T("%d"), i);
	m_ListCtrl.InsertItem( i, strIndex );

	CString strName;
	strName.Format( _T( "%s**" ), Students[i].GetName().Mid( 0, 1 ) );
	m_ListCtrl.SetItemText( i, 1, strName );
	m_ListCtrl.SetItemText( i, 2, Students[i].GetAge() );

	CString strPhone;
	strPhone.Format( _T( "%s-****-****" ), Students[i].GetPhone().Mid( 0, 3 ) );
	m_ListCtrl.SetItemText( i, 3, strPhone );		
}

위 코드는 일반적으로 이름을 가리는 방법이지만 설정한 아이템을 구별할 수 없는 상황이 발생하게 됩니다.
만약에 성과 나이가 같으면 더욱 구별하기 힘들어 집니다.
한마디로 개발자는 리스트 컨트롤에서 GetItemText 함수를 사용하여 암호화 전의 이름과 핸드폰 번호를 알 수가 없다는 의미입니다.

이 경우에 SetItemData와 GetItemData 함수를 사용하면 됩니다.
SetItemData의 함수 원형은 아래와 같습니다.

BOOL SetItemData(int nItem, DWORD_PTR dwData);

첫번째는 인덱스이고, 두번째 인자에 동적으로 생성된 데이터의 주소를 입력하면 됩니다.
GetItemData의 함수 원형은 아래와 같습니다.

DWORD_PTR GetItemData(int nItem) const;

원하는 행의 값을 넣으면 SetItemData 시 입력했던 데이터가 반환됩니다.
다시 코드를 수정해서 보자면 아래와 같습니다.

// 리스트 컨트롤 초기화
m_ListCtrl.InsertColumn( 0, _T( "번호" ), LVCFMT_CENTER, 100 );
m_ListCtrl.InsertColumn( 1, _T( "이름" ), LVCFMT_CENTER, 100 );
m_ListCtrl.InsertColumn( 2, _T( "나이" ), LVCFMT_CENTER, 100 );
m_ListCtrl.InsertColumn( 3, _T( "핸드폰" ), LVCFMT_CENTER, 100 );

// 학생 정보
CStudent *pStudents;
pStudents = new CStudent[3]; // 동적으로 생성해야한다.
pStudents[0].SetInfo( _T( "김민수" ), 10, _T( "010-1111-1111" ) );
pStudents[1].SetInfo( _T( "박동현" ), 20, _T( "010-2222-2222" ) );
pStudents[2].SetInfo( _T( "최덕기" ), 30, _T( "010-3333-3333" ) );

// 리스트에 표시
for ( int i = 0 ; i < 3 ; i++ )
{
	CString strIndex;
	strIndex.Format(_T("%d"), i);
	m_ListCtrl.InsertItem( i, strIndex );

	CString strName;
	strName.Format( _T( "%s**" ), pStudents[i].GetName().Mid( 0, 1 ) );
	m_ListCtrl.SetItemText( i, 1, strName );
	m_ListCtrl.SetItemText( i, 2, pStudents[i].GetAge() );

	CString strPhone;
	strPhone.Format( _T( "%s-****-****" ), pStudents[i].GetPhone().Mid( 0, 3 ) );
	m_ListCtrl.SetItemText( i, 3, strPhone );

	m_ListCtrl.SetItemData(i, (DWORD_PTR)&pStudents[i]); // DATA 설정
}

아이템을 클릭 했을 때 이벤트에 아래와 같이 GetItemData 코드를 넣어 원본 데이터 값을 팝업 하는 코드입니다.

void CMFCApplication1Dlg::OnNMClickListTest( NMHDR *pNMHDR, LRESULT *pResult )
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	int nItem = pNMItemActivate->iItem;
	int nCount = m_ListCtrl.GetItemCount();

	if ( 0 <= nItem && nItem < nCount )
	{
		CStudent *pStudent = (CStudent *)m_ListCtrl.GetItemData( nItem ); // 설정된 데이터를 가져온다.

		CString strMsg;
		strMsg.Format( _T( "%s, %s, %s" ), pStudent->GetName(), pStudent->GetAge(), pStudent->GetPhone() );
		AfxMessageBox( strMsg );
		*pResult = 0;
	}

	*pResult = 0;
}

가장 중요한 것은 아이템이 삭제 될 때 메모리를 해제 해야한다는 것입니다.

// 전체 삭제 시
int nCount = m_ListCtrl.GetItemCount();
for ( int i = 0 ; i < nCount ; i++ )
{
	CStudent *pStudent = (CStudent *)m_ListCtrl.GetItemData( i );
	delete pStudent;
}
m_ListCtrl.DeleteAllItems();


// 일부 삭제 시
CStudent *pStudent = (CStudent *)m_ListCtrl.GetItemData( 0 );
delete pStudent;
m_ListCtrl.DeleteItem(0);

실제 동작을 보면 아래와 같습니다.