Windows/Windows API

트리뷰 (tree view)

aucd29 2013. 10. 1. 18:59
#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
HWND hWndMain;
LPSTR lpszClass="TreeCtrl3";

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=NULL;
        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);
    hWndMain=hWnd;
    
    while(GetMessage(&Message,0,0,0)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}

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

HWND hTree;
HIMAGELIST Image;

// 우편 번호 구조체
struct tag_Post {
    int iParent;        // 부모 노드의 배열 인덱스
    char Title[30];        // 동 이름
    char Num[10];        // 우편 번호
    int iImage;            // 아이콘 이미지 인덱스
};

// 우편 번호 구조체 배열
tag_Post arPost[]={
    {-1,"우편번호부","000-000",0},
    {0,"서울특별시","000-000",1},
    {0,"경기도","000-000",1},
    {0,"강원도","000-000",1},
    {0,"충청남도","000-000",1},
    {0,"충청북도","000-000",1},
    {1,"강남구","000-000",2},
    {1,"강동구","000-000",2},
    {1,"강북구","000-000",2},
    {1,"강서구","000-000",2},
    {1,"관악구","000-000",2},
    {1,"광진구","000-000",2},
    {1,"구로구","000-000",2},
    {1,"금천구","000-000",2},
    {1,"노원구","000-000",2},
    {1,"도봉구","000-000",2},
    {1,"동대문구","000-000",2},
    {1,"동작구","000-000",2},
    {2,"고양시","000-000",2},
    {2,"일산시","000-000",2},
    {2,"과천시","000-000",2},
    {2,"광명시","000-000",2},
    {2,"구리시","000-000",2},
    {2,"군포시","000-000",2},
    {2,"남양주시","000-000",2},
    {2,"동두천시","000-000",2},
    {2,"부천시","000-000",2},
    {3,"강릉시","000-000",2},
    {3,"삼척시","000-000",2},
    {6,"개포동","135-240",3},
    {6,"논현동","135-010",3},
    {6,"대치동","135-280",3},
    {6,"도곡동","135-270",3},
    {6,"삼성동","135-090",3},
    {6,"세곡동","135-190",3},
    {6,"수서동","135-220",3},
    {6,"신사동","135-120",3},
    {6,"압구정동","135-110",3},
    {6,"역삼동","135-080",3},
    {6,"율현동","135-210",3},
    {7,"고덕동","134-080",3},
    {8,"미아동","142-100",3},
    {9,"가양동","157-200",3},
    {10,"남현동","151-080",3},
    {10,"봉천동","151-050",3},
    {10,"신림동","151-010",3},
    {11,"광장동","143-210",3},
    {12,"가리봉동","152-020",3},
    {13,"가산동","153-023",3},
    {14,"공릉동","139-240",3},
    {15,"도봉동","132-010",3},
    {16,"답십리동","130-030",3},
    {17,"노량진동","156-050",3},
    {18,"강매동","412-290",3},
    {18,"고양동","412-500",3},
    {18,"관산동","412-470",3},
    {18,"내곡동","412-260",3},
    {18,"내유동","412-520",3},
    {18,"대자동","412-480",3},
    {18,"대장동","412-250",3},
    {18,"덕은동","412-170",3},
    {18,"도내동","412-060",3},
    {18,"동산동","412-090",3},
    {19,"가좌동","411-440",3},
    {19,"구산동","411-430",3},
    {20,"갈현동","427-100",3},
    {20,"과천동","427-060",3},
    {20,"관문동","427-020",3},
    {20,"막계동","427-080",3},
    {20,"문원동","427-090",3},
    {20,"별양동","427-040",3},
    {21,"가학동","423-070",3},
    {22,"갈매동","471-080",3},
    {23,"광정동","435-045",3},
    {24,"가운동","472-060",3},
    {25,"걸산동","483-070",3},
    {26,"계수동","422-070",3},
};

// lParam으로 대입되는 구조체
struct tag_Param {
    char Num[10];
};

// Parent 노드의 차일드를 모두 찾아 트리에 추가한다. 각 차일드에 대해서도 이
// 함수를 재귀적으로 호출함으로써 모든 트리를 다 입력한다.
// pNode:부모의 노드
// pid:부모의 인덱스 번호
void InsertChild(HTREEITEM pNode,int pid)
{
    TV_INSERTSTRUCT TI;
    HTREEITEM Node;
    int i;
    int n=sizeof(arPost)/sizeof(arPost[0]);
    tag_Param *pParam;

    for (i=0;i<n;i++) {
        if (arPost[i].iParent==pid) {
            TI.hParent=pNode;
            TI.hInsertAfter=TVI_LAST;
            TI.item.mask=TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
            TI.item.iImage=arPost[i].iImage;
            TI.item.iSelectedImage=arPost[i].iImage+4;
            TI.item.pszText=arPost[i].Title;
            pParam=(tag_Param *)malloc(sizeof(tag_Param));
            lstrcpy(pParam->Num,arPost[i].Num);
            // 파라미터에 구조체의 포인터를 저장한다.
            TI.item.lParam=(LPARAM)pParam;
            Node=TreeView_InsertItem(hTree, &TI);
            // 차일드의 차일드를 채워 넣는다.
            InsertChild(Node,i);
        }
    }
}

#define IDC_NAME 1
#define IDC_POST 2
#define IDC_ADD 3
#define IDC_DEL 4
#define IDC_EDIT 5

void AddPost();
void DelPost();
void EditPost();

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    TVITEMEX TvEx;
    char Cap[30];
    tag_Param *pParam;

    switch(iMessage) {
    case WM_CREATE:
        InitCommonControls();
        // 트리 뷰 컨트롤을 만든다.
        hTree=CreateWindow(WC_TREEVIEW, "", WS_CHILD | WS_VISIBLE | WS_BORDER |
            TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_SHOWSELALWAYS,
            10,10,200,300,hWnd,NULL,g_hInst,NULL);

        // 트리뷰에 사용할 이미지 리스트를 만들어 트리뷰에 연결한다.
        Image=ImageList_LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1), 16, 1, RGB(255,255,255));
        TreeView_SetImageList(hTree, Image, TVSIL_NORMAL);

        // 트리에 데이터를 입력한다.
        InsertChild((HTREEITEM)0,-1);

        // 정보를 입력받기 위한 컨트롤들을 만든다.
        CreateWindow("static","이름",WS_CHILD | WS_VISIBLE,
            220,70,80,25,hWnd,(HMENU)-1,g_hInst,NULL);
        CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
            ES_AUTOHSCROLL,300,70,90,25,hWnd,(HMENU)IDC_NAME,g_hInst,NULL);
        CreateWindow("static","우편번호",WS_CHILD | WS_VISIBLE,
            220,100,80,25,hWnd,(HMENU)-1,g_hInst,NULL);
        CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
            ES_AUTOHSCROLL,300,100,90,25,hWnd,(HMENU)IDC_POST,g_hInst,NULL);

        // 명령 버튼
        CreateWindow("button","추가",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            220,150,90,25,hWnd,(HMENU)IDC_ADD,g_hInst,NULL);
        CreateWindow("button","삭제",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            320,150,90,25,hWnd,(HMENU)IDC_DEL,g_hInst,NULL);
        CreateWindow("button","수정",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            420,150,90,25,hWnd,(HMENU)IDC_EDIT,g_hInst,NULL);

        return 0;
    case WM_NOTIFY:
        LPNMHDR hdr;
        LPNMTREEVIEW ntv;
        hdr=(LPNMHDR)lParam;
        ntv=(LPNMTREEVIEW)lParam;
        if (hdr->hwndFrom == hTree) {
            switch (hdr->code) {
            // 선택된 항목을 보여준다.
            case TVN_SELCHANGED:
                TvEx.mask=TVIF_PARAM | TVIF_TEXT;
                TvEx.hItem=ntv->itemNew.hItem;
                TvEx.pszText=Cap;
                TvEx.cchTextMax=30;
                TreeView_GetItem(hTree,&TvEx);
                pParam=(tag_Param *)TvEx.lParam;
                SetDlgItemText(hWnd,IDC_NAME,Cap);
                SetDlgItemText(hWnd,IDC_POST,pParam->Num);
                break;
            }
        }
        return 0;
    // 버튼의 명령들을 처리한다.
    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDC_ADD:
            AddPost();
            break;
        case IDC_DEL:
            DelPost();
            break;
        case IDC_EDIT:
            EditPost();
            break;
        }
        return 0;
    case WM_DESTROY:
        ImageList_Destroy(Image);
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

// 입력된 정보대로 항목을 추가한다.
void AddPost()
{
    HTREEITEM hNow;
    TVINSERTSTRUCT TI;
    TVITEMEX TvEx;
    char Title[30];
    tag_Param *pParam;

    hNow=TreeView_GetSelection(hTree);
    if (hNow==NULL) {
        MessageBox(hWndMain,"추가할 부모 노드를 먼저 선택해 주십시요.","알림",MB_OK);
    } else {
        // 부모 노드의 이미지 번호를 구해 항목 추가가 가능한지 조사한다.
        TvEx.mask=TVIF_IMAGE;
        TvEx.hItem=hNow;
        TreeView_GetItem(hTree,&TvEx);

        if (TvEx.iImage == 3) {
            MessageBox(hWndMain,"동 아래에는 항목을 추가할 수 없습니다.","알림",MB_OK);
            return;
        }

        // 에디트에 입력된 이름,우편번호를 읽어온다.
        pParam=(tag_Param *)malloc(sizeof(tag_Param));
        GetDlgItemText(hWndMain,IDC_NAME,Title,30);
        GetDlgItemText(hWndMain,IDC_POST,pParam->Num,10);

        // 항목을 삽입한다.
        TI.hParent=hNow;
        TI.hInsertAfter=TVI_LAST;
        TI.item.mask=TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
        TI.item.iImage=TvEx.iImage+1;
        TI.item.iSelectedImage=TvEx.iImage+1+4;
        TI.item.pszText=Title;
        TI.item.lParam=(LPARAM)pParam;
        TreeView_InsertItem(hTree, &TI);

        // 항목을 추가한 후 부모 노드를 확장하여 추가한 결과를 보여준다.
        TreeView_Expand(hTree,hNow,TVE_EXPAND);
    }
}

// 현재 선택된 항목을 삭제한다.
void DelPost()
{
    HTREEITEM hNow;

    hNow=TreeView_GetSelection(hTree);
    if (hNow!=NULL) {
        TreeView_DeleteItem(hTree,hNow);
    }
}

// 현재 선택된 항목을 수정한다.
void EditPost()
{
    HTREEITEM hNow;
    TVITEMEX TvEx;
    tag_Param *pParam;
    char Title[30];

    hNow=TreeView_GetSelection(hTree);
    if (hNow==NULL) {
        MessageBox(hWndMain,"수정할 노드를 먼저 선택해 주십시요.","알림",MB_OK);
    } else {
        TvEx.hItem=hNow;
        TvEx.mask=TVIF_PARAM;
        TreeView_GetItem(hTree,&TvEx);
        pParam=(tag_Param *)TvEx.lParam;

        GetDlgItemText(hWndMain,IDC_NAME,Title,30);
        GetDlgItemText(hWndMain,IDC_POST,pParam->Num,10);

        TvEx.mask=TVIF_TEXT;
        TvEx.pszText=Title;
        TreeView_SetItem(hTree,&TvEx);
    }
}