본문 바로가기

Windows/Windows API

리스트뷰 (listview)

#include <windows.h>
#include <commctrl.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass="MyListCtrl";
HWND hList;
HIMAGELIST hImgSm, hImgLa;

struct tag_people {
    char name[20];
    char tel[20];
    char addr[50];
    BOOL male;
};

tag_people people[10]={{"김상형","123-4567","서울시 강남구 논현동",TRUE},
{"이송우","543-9876","서울시 구의동",TRUE},
{"박다희","111-3333","경기도 광명시",FALSE},
{"오궁섭","236-1818","서울시 강남구 반포동",TRUE},
{"조기순","358-2277","서울시 압구정동",FALSE},
{"오뱅훈","548-1109","서울시 신사동",TRUE},
{"End","","",TRUE},
};

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
         ,LPSTR lpszCmdParam,int nCmdShow)
{
    HWND hWnd;
    MSG Message;
    WNDCLASS WndClass;
    g_hInst=hInstance;
    
    if(!hPrevInstance) {
        WndClass.cbClsExtra=0;
        WndClass.cbWndExtra=0;
        WndClass.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1);
        WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
        WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
        WndClass.hInstance=hInstance;
        WndClass.lpfnWndProc=(WNDPROC)WndProc;
        WndClass.lpszClassName=lpszClass;
        WndClass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);
        WndClass.style=CS_HREDRAW | CS_VREDRAW;
        RegisterClass(&WndClass);
    }
    hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
        NULL,(HMENU)NULL,hInstance,NULL);
    ShowWindow(hWnd,nCmdShow);
    
    while(GetMessage(&Message,0,0,0)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}

// 리스트 뷰의 보기 상태를 실행중에 변경한다.
void SetListViewStyle(HWND hList, DWORD dwView)
{
    DWORD dwStyle;

    dwStyle=GetWindowLong(hList, GWL_STYLE);
    if ((dwStyle & LVS_TYPEMASK) != dwView) {
        SetWindowLong(hList, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | dwView);
    }
}

#define IDC_NAME 1
#define IDC_TEL 2
#define IDC_ADDR 3
#define IDC_MALE 4
#define IDC_ADD 5
#define IDC_DEL 6
#define IDC_EDIT 7
#define IDC_FIND 8
HWND hMale;
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    LV_COLUMN COL;
    LV_ITEM LI;
    int i,idx;
    char szName[255], szTel[255], szAddr[255];

    switch(iMessage) {
    case WM_CREATE:
        InitCommonControls();

        // 리스트 컨트롤을 자세히 보기로 만든다.
        hList=CreateWindow(WC_LISTVIEW,NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
            LVS_REPORT | LVS_SHOWSELALWAYS,
            10,10,600,150,hWnd,NULL,g_hInst,NULL);

        // 정보를 입력받기 위한 컨트롤들을 만든다.
        CreateWindow("static","이름",WS_CHILD | WS_VISIBLE,
            10,170,50,25,hWnd,(HMENU)-1,g_hInst,NULL);
        CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
            ES_AUTOHSCROLL,60,170,90,25,hWnd,(HMENU)IDC_NAME,g_hInst,NULL);
        CreateWindow("static","전화",WS_CHILD | WS_VISIBLE,
            160,170,50,25,hWnd,(HMENU)-1,g_hInst,NULL);
        CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
            ES_AUTOHSCROLL,210,170,90,25,hWnd,(HMENU)IDC_TEL,g_hInst,NULL);
        CreateWindow("static","주소",WS_CHILD | WS_VISIBLE,
            310,170,50,25,hWnd,(HMENU)-1,g_hInst,NULL);
        CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
            ES_AUTOHSCROLL,360,170,190,25,hWnd,(HMENU)IDC_ADDR,g_hInst,NULL);
        hMale=CreateWindow("button","남자",WS_CHILD | WS_VISIBLE |
            BS_AUTOCHECKBOX,560,170,100,25,hWnd,(HMENU)IDC_MALE,g_hInst,NULL);

        // 명령 버튼
        CreateWindow("button","추가",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            110,200,90,25,hWnd,(HMENU)IDC_ADD,g_hInst,NULL);
        CreateWindow("button","삭제",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            210,200,90,25,hWnd,(HMENU)IDC_DEL,g_hInst,NULL);
        CreateWindow("button","수정",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            310,200,90,25,hWnd,(HMENU)IDC_EDIT,g_hInst,NULL);
        CreateWindow("button","검색",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            410,200,90,25,hWnd,(HMENU)IDC_FIND,g_hInst,NULL);

        // 크기별로 두 종류의 이미지 리스트를 만들고 리스트 컨트롤에 연결한다.
        hImgSm=ImageList_LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BIT16), 16, 1, RGB(255,255,255));
        hImgLa=ImageList_LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BIT32), 32, 1, RGB(255,255,255));
        ListView_SetImageList(hList, hImgSm, LVSIL_SMALL);
        ListView_SetImageList(hList, hImgLa, LVSIL_NORMAL);

        // 헤더를 추가한다.
        // LVCF_FMT : FMT 멤버의 값을 사용한다.
        // LVCF_SUBITEM : iSubItem 멤버에 있는 값을 사용한다.
        // LVCF_TEXT : pszText 멥버에 있는 값을 사용한다.
        // LVCF_WIDTH : cx 멤버에 있는 값을 사용한다.
        // LVCF_IMAGE : 이미지 멤버에 있는 값을 사용한다.
        // LVCF_ORDER : iOrder에 있는 값을 사용한다.
        COL.mask=LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;

        // LVCFMT_LEFT : 왼쪽 정렬
        // LVCFMT_RIGHT : 오른쪽 정렬
        // LVCFMT_CENTER : 가운데 정렬
        // LVCFMT_BITMAP_ON_RIGHT : 비트맵을 문자열의 오른족으로
        // LVCFMT_COL_HAS_IMAGES : 헤더에 이미지 나타난다.
        // LVCFMT_JUSTFYMASK :
        // LVCFMT_IMAGE : 이미지 리스트의 이미지를 출력
        COL.fmt=LVCFMT_LEFT;
        COL.cx=150;
        COL.pszText="이름";            // 첫번째 헤더
        COL.iSubItem=0;
        ListView_InsertColumn(hList,0,&COL);

        COL.pszText="전화번호";        // 두번째 헤더
        COL.iSubItem=1;
        ListView_InsertColumn(hList,1,&COL);

        COL.cx=250;
        COL.pszText="주소";            // 세번째 헤더
        COL.iSubItem=2;
        ListView_InsertColumn(hList,2,&COL);

        // 텍스트와 이미지를 가진 아이템들을 등록한다.
        for (i=0;i<10;i++) {
            if (lstrcmp(people[i].name,"End")==0)
                break;
            LI.mask=LVIF_TEXT | LVIF_IMAGE;
            LI.state=0;
            LI.stateMask=0;

            if (people[i].male)
                LI.iImage=0;
            else
                LI.iImage=1;
            LI.iSubItem=0;
            LI.iItem=i;
            LI.pszText=people[i].name;
            ListView_InsertItem(hList,&LI);
            ListView_SetItemText(hList,i,1,people[i].tel);
            ListView_SetItemText(hList,i,2,people[i].addr);
        }
        return 0;
    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDM_ICON:
            SetListViewStyle(hList, LVS_ICON);
            break;
        case IDM_SMALLICON:
            SetListViewStyle(hList, LVS_SMALLICON);
            break;
        case IDM_LIST:
            SetListViewStyle(hList, LVS_LIST);
            break;
        case IDM_REPORT:
            SetListViewStyle(hList, LVS_REPORT);
            break;
        // 새 항목을 추가한다.
        case IDC_ADD:
            LI.mask=LVIF_TEXT | LVIF_IMAGE;
            LI.state=0;
            LI.stateMask=0;

            if (SendMessage(hMale,BM_GETCHECK,0,0)==BST_CHECKED)
                LI.iImage=0;
            else
                LI.iImage=1;
            LI.iSubItem=0;
            idx=ListView_GetItemCount(hList);
            LI.iItem=idx;
            GetDlgItemText(hWnd,IDC_NAME,szName,255);
            LI.pszText=szName;
            ListView_InsertItem(hList,&LI);
            GetDlgItemText(hWnd,IDC_TEL,szTel,255);
            ListView_SetItemText(hList,idx,1,szTel);
            GetDlgItemText(hWnd,IDC_ADDR,szAddr,255);
            ListView_SetItemText(hList,idx,2,szAddr);
            break;
        // 선택한 항목을 삭제한다.
        case IDC_DEL:
            idx=ListView_GetNextItem(hList,-1,LVNI_ALL | LVNI_SELECTED);
            if (idx == -1) {
                MessageBox(hWnd,"삭제할 항목을 먼저 선택하십시요","알림",MB_OK);
            } else {
                ListView_DeleteItem(hList, idx);
            }
/*
            // 선택된 모든 항목을 삭제한다.
            idx=ListView_GetNextItem(hList,-1,LVNI_ALL | LVNI_SELECTED);
            if (idx == -1) {
                MessageBox(hWnd,"삭제할 항목을 먼저 선택하십시요","알림",MB_OK);
            } else {
                do {
                    ListView_DeleteItem(hList, idx);
                    idx=ListView_GetNextItem(hList,idx-1,LVNI_ALL | LVNI_SELECTED);
                } while (idx != -1);
            }
*/
            break;
        // 선택한 항목을 수정한다.
        case IDC_EDIT:
            idx=ListView_GetNextItem(hList,-1,LVNI_ALL | LVNI_SELECTED);
            if (idx == -1) {
                MessageBox(hWnd,"수정할 항목을 먼저 선택하십시요","알림",MB_OK);
            } else {
                LI.mask=LVIF_IMAGE;
                LI.iItem=idx;
                LI.iSubItem=0;
                if (SendMessage(hMale,BM_GETCHECK,0,0)==BST_CHECKED)
                    LI.iImage=0;
                else
                    LI.iImage=1;
                ListView_SetItem(hList, &LI);
                GetDlgItemText(hWnd,IDC_NAME,szName,255);
                ListView_SetItemText(hList,idx,0,szName);
                GetDlgItemText(hWnd,IDC_TEL,szTel,255);
                ListView_SetItemText(hList,idx,1,szTel);
                GetDlgItemText(hWnd,IDC_ADDR,szAddr,255);
                ListView_SetItemText(hList,idx,2,szAddr);
            }
            break;
        // 이름으로 항목을 검색한다.
        case IDC_FIND:
            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,"지정한 이름의 항목은 없습니다","알림",MB_OK);
            } else {
                ListView_SetItemState(hList,-1,0,LVIS_FOCUSED | LVIS_SELECTED);
                ListView_SetItemState(hList,idx,LVIS_FOCUSED | LVIS_SELECTED,LVIS_FOCUSED | LVIS_SELECTED);
                ListView_EnsureVisible(hList,idx,FALSE);
            }
            break;
        }
        break;
    case WM_NOTIFY:
        LPNMHDR hdr;
        LPNMLISTVIEW nlv;
        hdr=(LPNMHDR)lParam;
        nlv=(LPNMLISTVIEW)lParam;
        if (hdr->hwndFrom == hList) {
            switch (hdr->code) {
            // 선택된 항목을 에디트에 보여준다.
            case LVN_ITEMCHANGED:
                if (nlv->uChanged == LVIF_STATE && nlv->uNewState == (LVIS_SELECTED | LVIS_FOCUSED)) {
                    ListView_GetItemText(hList,nlv->iItem,0,szName,255);
                    SetDlgItemText(hWnd,IDC_NAME,szName);
                    ListView_GetItemText(hList,nlv->iItem,1,szTel,255);
                    SetDlgItemText(hWnd,IDC_TEL,szTel);
                    ListView_GetItemText(hList,nlv->iItem,2,szAddr,255);
                    SetDlgItemText(hWnd,IDC_ADDR,szAddr);
                }
                break;
            }
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}