리스트뷰 컨트롤의 추가, 삭제, 수정, 검색 등

아주 기초적인 사용법에 대해 적어보고자 합니다.




이런식으로 전체적인 레이아웃을 잡고 시작한다고 봐야겠죠?

그리고 중요한 컨트롤의 컨트롤의 ID 의 값을 각각 아래와 같이 정하고 시작하겠습니다.




각각의 컨트롤에 대해서는 WM_COMMAND 메시지를 다루면 되겠죠.



추가



추가의 경우에는 지난번에 했던 방식 그대로

ListView_InsertItem 과 ListView_SetItemText 매크로를 이용하면 됩니다.


   li.mask=LVIF_TEXT;
   li.state=0;
   li.stateMask=0;
   
   idx=ListView_GetItemCount(hList);

   GetDlgItemText(hWnd,IDC_NAME,szName,255);
   GetDlgItemText(hWnd,IDC_SEX,szSex,255);
   GetDlgItemText(hWnd,IDC_ADDR,szAddr,255);


   li.iItem=idx;
   li.iSubItem=0;
   li.pszText=szName;


   ListView_InsertItem(hList,&li);
   ListView_SetItemText(hList,idx,1,szSex);
   ListView_SetItemText(hList,idx,2,szAddr);



그나마 지난번과 달라진건 컨트롤의 값을 읽어오는 GetDlgItemText 와

리스트 뷰의 아이템들의 수를 읽는 ListView_GetItemCount 가 추가된 것 밖에 없겠군요 ^^


int ListView_GetItemCount(HWND hWnd)


이게 원형입니다. sizeof 와 비슷하다고 보면 되겠군요.



삭제


삭제를 하기 위해서 사용되는 매크로는 아래와 같습니다.


int ListView_GetNextItem(HWND hWnd, int iStart, UINT flags);

BOOL ListView_DeleteItem(HWND hWnd, int iItem)


ListView_GetNextItem 의 첫번째 인수는 당연히 리스트의 핸들이고,

두번째는 어디서부터 찾을 것인가를 정하는 것입니다. -1 의 경우는

전체에서 찾는 것이고 주어지는 값에 따라 그 인덱스부터 차례로 찾아가는 것이죠.


리턴값이 int 형인데 이건 찾은 인덱스 값을 리턴합니다 ^^ 몇 번째 존재하는지를 리턴하죠.

세번째 인수는 Flags 값을 지정하는 것인데 LVNI_ALL | LVNI_SELECTED 를 보통 지정합니다.

왜 그런가 싶으면 LVNI_ALL 이나 LVNI_SELECTED 를 하나씩 삭제하여 컴파일해보면 알겠죠?


idx=ListView_GetNextItem(hList, -1, LVNI_ALL | LVNI_SELECTED);


이 경우는 만약 찾았을 경우에는 idx 의 값에는 해당 인덱스가 들어가겠지만,

찾기 실패하였을 경우에는 idx 에 -1 값이 들어갑니다.



   idx=ListView_GetNextItem(hList,-1,LVNI_ALL|LVNI_SELECTED);
   if(idx==-1)
        MessageBox(hWnd,"삭제할 아이템을 선택하세요","Error",MB_OK);
   else
   {
        while(idx!=-1)
        {
             ListView_DeleteItem(hList,idx);
             idx=ListView_GetNextItem(hList,idx-1,LVNI_ALL|LVNI_SELECTED);
        }
   }



여기서 중요한 것은 while 문의 ListView_GetNextItem 에 idx-1 이 들어간다는 것이죠.

선택된 아이템들을 하나씩 찾아서 지워주는 것이기 때문에 idx 보다 -1 된 것을 넣어야

차례로 지워나갑니다. idx  를 그대로 넣어준다면 무슨 일이 일어나는지는 직접 해보시면 압니다.


선택된 아이템들이 전부 삭제가 되지 않겠죠;



수정


수정은 추가와 비슷하여 상당히 쉽습니다. 다만 ListView_InsertItem 매크로 대신

ListView_SetItem 을 통하여 재설정을 해줘야 합니다. 아주 조금 추가와 다를 뿐,

기본 방식은 같습니다.



   idx=ListView_GetNextItem(hList,-1,LVNI_ALL|LVNI_SELECTED);

   if(idx==-1)
        MessageBox(hWnd,"수정할 아이템을 선택하세요","Error",MB_OK);
   else
   {
        GetDlgItemText(hWnd,IDC_NAME,szName,255);
        GetDlgItemText(hWnd,IDC_SEX,szSex,255);
        GetDlgItemText(hWnd,IDC_ADDR,szAddr,255);


        li.iItem=idx;
        li.iSubItem=0;
   
        ListView_SetItem(hList,&li);
        ListView_SetItemText(hList,idx,0,szName);
        ListView_SetItemText(hList,idx,1,szSex);
        ListView_SetItemText(hList,idx,2,szAddr);
   }



보시면 아시겠지만 일단 선택된 것을 찾아서 idx 에 그 인덱스 값을 넣어줍니다.

만약 선택되지 않았다면 Error 메시지박스가 뜨겠죠. 이제 LVITEM 구조체인

li 의 iItem 에 idx 값을 지정하고 iSubItem 멤버에도 첫번째 컬럼을 의미하는 0을 지정한 뒤


ListView_SetItem 으로 li 를 hList 에 설정합니다.

일단 내가 몇 번째 리스트 뷰 아이템을 설정한다고 알려주는 것이죠.


그런 뒤 ListView_SetItemText 매크로를 통하여 수정해주면 됩니다 ^^



검색



다행히 검색을 할 때도 편리하게(?) 매크로가 존재합니다.


int ListView_FindItem(HWND hWnd, int iStart, const LPLVFINDINFO plvfi);


여기서 중요한건 LVFINFO 구조체가 들어가는데요, 이건 검색의 조건으로


typedef struct tagLVFINDINFO {
    UINT flags;
    LPCTSTR psz;
    LPARAM lParam;
    POINT pt;
    UINT vkDirection;
} LVFINDINFO, *LPFINDINFO;


flags 멤버로 중요한건


LVFI_STRING    psz 멤버가 지정하는 문자열과 일치하는 텍스트를 가진 항목을 검색

LVFIN_WRAP    검색 대상이 발견되지 않을 경우 처음부터 다시 검색


이 두 개 정도로만 봐도 가장 기초적인 곳에서는 사용할 수 있습니다.

그리고 vkDirection 이라는 특이한 멤버가 존재하는데요,

이건 VK_LEFT , VK_DOWN, VK_HOME 등 가상 키를 넣어주는겁니다.


대게 VK_DOWN 을 설정하는 편이라는군요.



   LVFINDINFO fi;

   GetDlgItemText(hWnd,IDC_NAME,szName,255);


   fi.flags=LVFI_STRING;
   fi.psz=szName;
   fi.vkDirection=VK_DOWN;

   idx=ListView_FindItem(hList,-1,&fi);


   if(idx==-1)
        MessageBox(hWnd,"검색된 아이템이 없습니다","Error",MB_OK);
   else
   {
        ListView_SetItemState(hList,-1,0,LVIS_FOCUSED|LVIS_SELECTED);
        ListView_SetItemState(hList,idx,LVIS_FOCUSED|LVIS_SELECTED,0x000F);
        ListView_EnsureVisible(hList,idx,FALSE);
   }



이렇게 IDC_NAME 의 값을 읽어와 psz 에 설정한 후 ListView_FindItem 매크로에서 찾아주면 되네요.

ListView_FindItem 에서 -1 은 가장 처음에서 찾는, 전체에서 찾는다는 의미죠. 같은 문자열로 찾기를

계속 이어주려면 idx-1 값으로 루프를 돌려주면서 찾으면 되겠군요 ^^


찾았을 경우 ListView_SetItemState 로 상태를 설정해줘야하는데요.


ListView_SetItemState(hList,-1, 0,LVIS_FOCUSED|LVIS_SELECTED);

ListView_SetItemState(hList,idx,LVIS_FOCUSED|LVIS_SELECTED,0x000F);


이 두개가 왜 존재할까요? -1은 전체를 의미하고, 0은 상태를 없앤다고 보면 되죠.

일단 리스트 뷰에 어떤 항목들이 선택되어있거나 한다면 리플래시를 시켜 없앱니다.


그게 바로 ListView_SetItemState(hList,-1, 0,LVIS_FOCUSED|LVIS_SELECTED) 입니다.

그리고 나서 찾은 idx 번째의 리스트 뷰의 아이템을 선택해야하는데요, 그게 바로 두 번째인


ListView_SetItemState(hList,idx,LVIS_FOCUSED|LVIS_SELECTED,0x000F);


입니다. 마지막 인수가 16진수로 되어있지만 이것도 역시 아래와 같이 바꿀 수 있습니다.


ListView_SetItemState(hList,idx,LVIS_FOCUSED|LVIS_SELECTED, LVIS_FOCUSED|LVIS_SELECTED);


여기까지가 가장 기초적인(?) 방법이었네요.

사실상 개인적으로 시간이 많이 먹었던 부분이예요.


좀더 자세한건 소스 첨부 했으니 참고하시면 되구요 ^^;

다음엔 WM_NOTIFY 에 해당되는 리스트 뷰의 메시지 관리를 적어보겠습니다.

출처 : http://cafe.naver.com/seen/131

블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

Tag