정말 이거 만드는데 얼마를 걸린지 모르겠다

 

정말 멍빡친다.

 

국내에서 Virtual List control을 사용하는 예제가 아주 옛날 자료들만 있고 ( 사실 이거만 봐도 괜찮음 )

 

자세한 설명들이 없어서 개고생을 했다.

 

나처럼 C++, MFC 초보분들에게 도움을 주려한다.

 

 

 

 

 

가장 먼저 설명을 하자면 Virtual List Control은 List Control과는 다른 점이 있다.

 

바로 ListCtrl이 데이터를 직접적으로 가지고 있는 것이 아니다. 그래서 빠르다.

 

눈에 보이는 값들만 ListCtrl에 표현하여 주므로 속도도 좋다.

 

하지만 속성들이 더럽게 어렵다. 이제 시작

 

 

 

 

Virtual List Control은 어디서 검색하나 마찬가지로

 

 

1. List Control을 생성한다.

 

2. 속성 탭의 Owner Data를 True로 설정하여 준다 ( 시스템에게 데이터 권한을 넘긴다는 의미 ? )

 

3. View를 Report로 설정하여 준다. ( 목록형식으로 보기 위함 )

 

 

이게 완전 기본적인 생성 방법이다.

 

그 후에 ListCtrl을 사용하는 Dlg.cpp에서

 

처음 List control을 Init할 때

 

Column을 먼저 지정하여 준다.

 

	m_ListCtrl.InsertColumn(0, TEXT("1열같은 0열"), LVCFMT_CENTER, 112);

첫번째 파라미터에는 열의 번호가 들어간다.

 

두번째 파라미터에는 텍스트가 들어간다.

 

세번째 파라미터에는 정렬 방식이 들어간다.

 

네번째 파라미터에는 너비값이 들어간다.

 

( 첫 눈에 봐도 알겠지만, 이런식으로 파라미터 값 하나하나가 무엇인지 설명해주는게 없어

나중에 가면 공부하기가 힘들어졌다. )

 

 

 

가장 중요한 것을 빼먹었다.

 

yourListCtrl.SetItemCountEx(리스트의 크기);

 

리스트의 크기를 지정해주어야 리스트가 나타난다.

 

가변하는 크기라면 그 데이터 구조의 Size를 넣는것이 좋다.

 

 

 

 

아무튼 이런식으로 열들을 지정을 쭉해준다.

 

그 이후

 

Row를 입력하기 전에

m_ListCtrl.SetExtendedStyle(LVS_EX_DOUBLEBUFFER | m_ListCtrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL | LVS_EX_GRIDLINES);

 

ListCtrl의 SetExtendedStyle을 설정해야 한다.

 

 

1.  DOUBLEBUFFER = 깜빡임을 줄여준다.

 

 

2.  GetExtendedStyle() = 현재 컨트롤의 확장 스타일

 

 

3.  FULLROWSELECT = Row의 아이템을 클릭했을 때 해당 Row를 모두 Focus한다.

 

 

4.  NOSCROLL = Row의 수가 변경되어도 스크롤은 움직이지 않는다.

 

 

5.  NOINVALIDATEALL = 현재 리스트에 표시되지않는 목록이면 표시하지않는다.

 

 

6.  GRIDLINES = 리스트의 Row와 Col에 실선을 긋는다.

 

 

이렇게 지정해주어야 합니다.

 

DOUBLEBUFFER는 ListCtrl이 빠르게 변화하는 값들이면 설정해줍니다. 왜냐면 깜빡이지 않거든요

 

 

FULLROWSELECT = Virtual List Control은 0행만 클릭이 됩니다. 그러니까 이걸 설정해주는게 좋죠

 

 

NOSCROLL = 목록의 값이 추가되는 형식이면 쓰는게 좋습니다. 자꾸 스크롤이 움직이는걸 방지해주죠

 

 

아무튼 이정도입니다.

 

그 이후에 Virtual List Control에서 Row를 입력해주려면

 

ListCtrl의 클래스 마법사에서 LVN_GETDISPINFO를 추가해주어야 합니다.

 

void AnchorDlg::OnGetdispinfoListctrl(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);

	LV_ITEM* pItem = &(pDispInfo)->item;

	static CString str;

	if(pItem == NULL) return;

	int nRow = pItem->iItem;
	int nCol = pItem->iSubItem;

	if(nRow<0 || nRow >= mapShip->size()) return;
	//if(nRow<0 || nRow >= 100) return;
	

	DataList* iter = mapList->find(nRow)->second;

	switch(nCol)
	{
	case 0:
		str.Format(_T("%.0f"), iter->aa);
		break;
	case 1:
		str.Format(_T("%.7f"), iter->bb);
		break;
	case 2:
		str.Format(_T("%.7f"), iter->cc);
		break;
	case 3:
		str.Format(_T("%.1f"), iter->dd);
		break;
	case 4:
		str.Format(_T("%.1f"), iter->ee);
		break;
	default :
		break;
	}
	pItem->pszText = const_cast<LPTSTR>((LPCTSTR)str);

	Invalidate(FALSE);


	*pResult = 0;
}

저는 mapList<double, DataList> 라는 map 데이터 구조를 활용하였습니다.

 

1. pItem->iItem 은 ListCtrl의 Row 값을 가져옵니다. ( 10 Row라면 0~10까지 가져온다. )

	int nRow = pItem->iItem;

 

2. pItem->iSubItem은 ListCtrl의 Col 값을 가져옵니다. ( 위에서 InsertColumn이 1개라면 1까지 5개라면 5까지 )

	int nCol = pItem->iSubItem;

 

3. map의 사이즈를 row가 넘는다면 멈추고 return합니다.

	if(nRow<0 || nRow >= mapShip->size()) return;

 

 

3. 저는 mapList의 Key값에 List의 Index를 넣어서 로우 값과 일치시켜줬습니다.

 

그래서 mapList의 Key값에서 nRow와 같은 Key를 찾아 해당 Key의 Value를 iter에 저장해주었습니다.

 

그 다음 switch case를 통해 nCol의 값을 찾고 case에 도달하여 Col마다 알맞은 값을 입력해주었습니다.

	DataList* iter = mapList->find(nRow)->second;

	switch(nCol)
	{
	case 0:
		str.Format(_T("%.0f"), iter->mmsi);
		break;
	case 1:
		str.Format(_T("%.7f"), iter->lat);
		break;
	case 2:
		str.Format(_T("%.7f"), iter->lng);
		break;
	case 3:
		str.Format(_T("%.1f"), iter->sog);
		break;
	case 4:
		str.Format(_T("%.1f"), iter->cog);
		break;
	default :
		break;
	}

 

 

마지막으로

	pItem->pszText = const_cast<LPTSTR>((LPCTSTR)str);

Text를 지정해준다면 끝입니다.

 

다른 내용은 다음 포스팅에