안녕하세요? 수구리입니다.
이번 포스팅은 MFC에서 자주 쓰이는 list control에 대한 내용입니다.
list control은 테이블 모양을 하고 있으며, header에는 클릭 시 이벤트를 추가할 수 있습니다.
위의 사진이 MFC에서 list control을 사용한 부분입니다.
지금 상태는 Usage rate라는 list control header 부분에 마우스를 올려놓은 상태입니다.
이벤트를 아직 설정하지 않았으므로 클릭 시 아무런 동작을 하지 않고 있는데,
하나씩 추가해 주도록 하겠습니다.
우선 하려는 것은 각 header(PID, Name, Usage rate)를 클릭 시 오름차순 정렬을 하고
한번 더 클릭 시 반대로 내림차순으로 정렬을 하려고 하는 것입니다.
우선 우리가 제어하려고 하는 list control의 멤버 변수가 있는 곳의. h 파일로 이동합니다.
[ ResourceMonitorView.h ]
// ResourceMonitorView.h : CResourceMonitorView 클래스의 인터페이스
//
#pragma once
// inclue something
class CResourceMonitorView : public CScrollView
{
// 특성입니다.
public:
// 생략
CListCtrl m_tableList;
// 생략
// 1. 사용할 변수 및 정렬 정보를 담은 구조체 선언
// 정렬 방식에 대한 BOOL 변수
BOOL m_bAscending;
// 구조체에 정보를 담아서 파라메터로 넘기기 위해서 사용
struct SORTPARAM
{
int iSortColumn;
bool bSortDirect;
CListCtrl *pList;
int flag = -1; // 클릭한 header에 따라서 정렬할 값이 다르기 때문에 구분해주기위한 변수
};
// 작업입니다.
public:
// 생략
// 2. 클릭시 이벤트 추가 및 정렬에 사용할 함수 선언
afx_msg void OnHdnItemclickList1(NMHDR *pNMHDR, LRESULT *pResult);
static int CALLBACK CompareItem(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
}
이렇게. h 파일에 선언을 해주고 다음으로는 구현 파일 (. cpp)로 이동하여
구체적으로 어떤 동작을 할 건지 구현해주도록 하겠습니다.
[ ResourceMonitorView.cpp ]
1. MessageMap에 ON_NOTIFY 추가
BEGIN_MESSAGE_MAP(CResourceMonitorView, CScrollView)
// 표준 인쇄 명령입니다.
ON_COMMAND(ID_FILE_PRINT, &CScrollView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CScrollView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CScrollView::OnFilePrintPreview)
ON_WM_SIZE()
// 3. 헤더에서 만들어준 이벤트 함수를 등록
ON_NOTIFY(HDN_ITEMCLICK, 0, &CResourceMonitorView::OnHdnItemclickList1)
END_MESSAGE_MAP()
&CResourceMonitorView 부분은 자신의 클래스 이름에 맞게 바꿔주어야 합니다.
2. 아래 부분에 다음 내용 추가
// 리스트컨트롤 컬럼 클릭시 데이터 정렬
void CResourceMonitorView::OnHdnItemclickList1(NMHDR * pNMHDR, LRESULT * pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// 클릭한 컬럼의 인덱스
int nColumn = pNMLV->iItem;
// 현 리스트 컨트롤에 있는 데이터 총 자료 개수만큼 저장
for (int i = 0; i < (m_tableList.GetItemCount()); i++) {
m_tableList.SetItemData(i, i);
}
// 정렬 방식 저장
m_bAscending = !m_bAscending;
// 정렬 관련 구조체 변수 생성 및 데이터 초기화
SORTPARAM sortparams;
sortparams.pList = &m_tableList;
sortparams.iSortColumn = nColumn;
sortparams.bSortDirect = m_bAscending;
// | 0 | 1 | 2 |
// | PID| NAME | USAGE(rate) |
// PID 정렬
if (nColumn == 0)
sortparams.flag = 0;
// NAME은 알파벳 정렬
if (nColumn == 1)
sortparams.flag = 1;
// Usage(rate) 정렬
if (nColumn == 2 || nColumn == 3)
sortparams.flag = 2;
// 정렬 함수 호출
m_tableList.SortItems(&CompareItem, (LPARAM)&sortparams);
*pResult = 0;
}
int CResourceMonitorView::CompareItem(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CListCtrl *pList = ((SORTPARAM*)lParamSort)->pList;
int iSortColumn = ((SORTPARAM*)lParamSort)->iSortColumn;
bool bSortDirect = ((SORTPARAM*)lParamSort)->bSortDirect;
int flag = ((SORTPARAM*)lParamSort)->flag;
LVFINDINFO info1, info2;
info1.flags = LVFI_PARAM;
info1.lParam = lParam1;
info2.flags = LVFI_PARAM;
info2.lParam = lParam2;
int irow1 = pList->FindItem(&info1, -1);
int irow2 = pList->FindItem(&info2, -1);
CString strItem1 = pList->GetItemText(irow1, iSortColumn);
CString strItem2 = pList->GetItemText(irow2, iSortColumn);
// PID 정렬
if (flag == 0)
{
int iItem1 = _tstoi(strItem1);
int iItem2 = _tstoi(strItem2);
if (bSortDirect) {
return iItem1 == iItem2 ? 0 : iItem1 > iItem2;
}
else {
return iItem1 == iItem2 ? 0 : iItem1 < iItem2;
}
}
// NAME 정렬
else if (flag == 1)
{
return bSortDirect ? strcmp(LPSTR(LPCTSTR(strItem1)), LPSTR(LPCTSTR(strItem2))) : -strcmp(LPSTR(LPCTSTR(strItem1)), LPSTR(LPCTSTR(strItem2)));
}
// RATE 정렬
else if (flag == 2)
{
double dItem1 = _wtof(strItem1);
double dItem2 = _wtof(strItem2);
if (bSortDirect) {
return dItem1 == dItem2 ? 0 : dItem1 > dItem2;
}
else {
return dItem1 == dItem2 ? 0 : dItem1 < dItem2;
}
}
else {
return AfxMessageBox(L"정렬할 수 없습니다!");
}
}
기본적으로 list control의 값을 가져와서 정렬하기 때문에, 모든 타입이 CString입니다.
따라서, 문자로 정렬하기 때문에, Name 같은 경우는 문자열로 즉, 알파벳순으로 정렬해주면 잘 동작하지만,
PID나 Usage 같은 경우, int 또는 double형태로 값을 바꿔서 비교를 해주고 정렬을 해야 정상적으로 정렬이 가능합니다.
필자의 경우는 PID와 Usage는 클릭 시 클릭한 칼럼의 index값에 따라서 구분해주어 정렬을 수행하도록 하였습니다.
마지막으로는 실행 결과 화면을 차례로 보여드리겠습니다.
[ 실행 결과 ]
1. 우선 Usage rate를 첫 번째 클릭한 뒤의 모습입니다.
0.00 값으로 쭉 정렬된 모습입니다.
아래의 결과는 CPU 사용률이 많은 순으로 정렬된 모습입니다.
정렬하자마자 CPU값이 실시간으로 바뀌다 보니.. 2번째에 값이 바뀌어서 찍혔나 봅니다..
원래는 정상적으로 작동합니다!
다음으로는 클릭한 header를 PID 쪽을 첫 번째 클릭한 결과입니다.
0번 PID부터 쭉 정렬된 모습이 참 깔끔합니다.
아래는 한번 더 클릭한 경우겠죠?
내림차순으로 정렬된 모습입니다.
기본적으로 String으로 정렬하기 때문에 PID와 Usage rate는 한번 더 형 변환을 통해서 값을 비교해주어야 한다는 점을 알아가셨으면 좋겠습니다.
이상으로 MFC에서 List Control을 다루면서 header를 클릭했을 때 정렬하는 예제를 살펴보았습니다.
감사합니다.
'🌈 프로그래밍 > MFC' 카테고리의 다른 글
[ MFC, C++ ] BYTE to CString, CString to int 형변환 (0) | 2022.01.26 |
---|---|
[ MFC ] Ontimer와 SetTimer, KillTimer 사용법 (0) | 2021.10.26 |
[ MFC ] AfxBeginThread를 사용한 Timer 예제 (1) | 2021.10.20 |
MFC 프로그래밍 시작하기 (4) | 2021.08.06 |