BG MVC Model View Controller eğitim serisi yayında...

Ana page > Programlama > Windows API Programlama > Treeview

Treeview

► Kontroller

Windows API ile bir Treeview kontrolü oluşturmak için CreateWindowEx() fonksiyonunun ikinci parametresinde WC_TREEVIEW değerini kullanmamız gerekir. Bu ifadenin tanımı ve Treeview kontrolü için kullanılacak değerler commctrl.h başlık dosyasında yer aldığından bu dosya programımıza dahil edilmelidir.

Treeview kontrolü oluşturma

1. Öncelikle Burada gösterildiği gibi bir Windows API projesi oluşturalım. Projeyle birlikte otomatik olarak oluşturulan main.c dosyasının başlangıç kısmını aşağıdaki şekilde düzenleyelim:


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

HWND hwndTreeview;

BOOL InitTreeViewImageLists(HWND hwndTV);
HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel);

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)

Aşağıdaki satır ile commctrl.h başlık dosyası ile Treeview kontrolü oluşturma ile ilgili fonksiyonların ve değerlerin kullanımı sağlanır.


#include <commctrl.h>

Aşağıdaki satır ile resource.h başlık dosyası programa dahil edilir.


#include "resource.h"

Aşağıdaki satırlar ile Treeview kontrolü resim listesi oluşturmak için kullanılacak InitTreeViewImageLists() fonksiyonu ile Treeview kontrolüne item eklemek için kullanılacak olan AddItemToTree() fonksiyonunun tanımlaması programa eklenir.


BOOL InitTreeViewImageLists(HWND hwndTV);
HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel);

Aşağıdaki satır ile bir adet Treeview kontrolü oluşturulur.


HWND hwndTreeview;

2. resource.h dosyası oluşturma

Aşağıda gösterildiği şekilde bir resource.h dosyası oluşturarak programa dahil edelim.


#include <windows.h>

#define IDC_TREEVIEW 101

#define IDI_ICONFOLDER 201
#define IDI_ICONOPENFOLDER 202
#define IDI_ICONFILE 203

3. resource.rc dosyası oluşturma

Aşağıda gösterildiği şekilde bir resource.rc dosyası oluşturarak programa dahil edelim.


#include "resource.h"

IDI_ICONFOLDER ICON "folder.ico"
IDI_ICONOPENFOLDER ICON "openfolder.ico"
IDI_ICONFILE ICON "file.ico"

1 24 "manifest.xml"

4. manifest.xml dosyası oluşturma

Aşağıda gösterildiği şekilde bir manifest.xml dosyası oluşturarak program dosyalarının bulunduğu dizine kaydedelim.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="CompanyName.ProductName.YourApplication" type="win32" />
      <description>Your application description here.</description>
      <dependency>
         <dependentAssembly>
            <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
         </dependentAssembly>
      </dependency>
   </assembly>

Burdaki manifest.xml dosyasını oluşturulmasının nedeni, Imagelist içinde yer alan .ico uzantılı dosyaların arka planının saydam yerine siyah olarak görünmesini engellemektir.

5. Windows Procedure düzenleme

WindowProcedure içinde oluşturacağımız WM_CREATE seçeneğine aşağıdaki satırları ekleyelim:


LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_CREATE:
        {
             HTREEITEM hitem, hitem02;
             /* Treeview oluşturma */
             hwndTreeview = CreateWindowEx(0, WC_TREEVIEW, TEXT("Tree View"), WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES,
                            5, 5, 320, 320,
                            hwnd, (HMENU)IDC_TREEVIEW, 0, NULL);

             /* Imagelist oluşturma */
             InitTreeViewImageLists(hwndTreeview);

             /* Treeview item değerlerini ekleme */
             AddItemToTree(hwndTreeview, "Item01", 1);

             AddItemToTree(hwndTreeview, "Item02", 1);

             hitem = AddItemToTree(hwndTreeview, "Item03", 1);

             hitem02 = AddItemToTree(hwndTreeview, "Item03-01", 2);
             AddItemToTree(hwndTreeview, "Item03-01-01", 3);
             AddItemToTree(hwndTreeview, "Item03-01-02", 3);
             TreeView_Expand(hwndTreeview, hitem02, TVE_EXPAND);

             hitem02 = AddItemToTree(hwndTreeview, "Item03-02", 2);
             AddItemToTree(hwndTreeview, "Item03-02-01", 3);
             AddItemToTree(hwndTreeview, "Item03-02-02", 3);
             AddItemToTree(hwndTreeview, "Item03-02-03", 3);
             TreeView_Expand(hwndTreeview, hitem02, TVE_EXPAND);

             AddItemToTree(hwndTreeview, "Item03-03", 2);

             hitem02 = AddItemToTree(hwndTreeview, "Item03-04", 2);
             AddItemToTree(hwndTreeview, "Item03-02-01", 3);
             AddItemToTree(hwndTreeview, "Item03-02-02", 3);
             TreeView_Expand(hwndTreeview, hitem02, TVE_EXPAND);

             AddItemToTree(hwndTreeview, "Item03-05", 2);

             AddItemToTree(hwndTreeview, "Item03-06", 2);

             AddItemToTree(hwndTreeview, "Item04", 1);

             TreeView_Expand(hwndTreeview, hitem, TVE_EXPAND);
        }
        break;

Önce Treeview kontrolü oluşturulur, sonra InitTreeViewImageLists() fonksiyonu ile Imagelist oluşturularak Treeview kontrolüne atanır. Treeview item değerlerini ekleme işlemi yapılır.

WindowProcedure içinde oluşturacağımız WM_NOTIFY seçeneğine aşağıdaki satırları ekleyelim:


case WM_NOTIFY:
        {
            /* Alt item'lar içeren item'lar kapandığında ve açıldığında farklı resim atama işlemi için */
            if (((LPNMHDR)lParam)->hwndFrom == hwndTreeview){
                switch (((LPNMHDR)lParam)->code){
                    case TVN_ITEMEXPANDED:
                    {
                        LPNMTREEVIEW nmtv = (LPNMTREEVIEW)lParam;

                        if (nmtv->hdr.code == TVN_ITEMEXPANDED){
                            TVITEM item = nmtv->itemNew;
                            item.stateMask = TVIF_IMAGE;

                            if (nmtv->action == TVE_COLLAPSE){
                                item.iImage = 0;
                                item.iSelectedImage = 0;
                            }
                            else if (nmtv->action == TVE_EXPAND){
                                item.iImage = 1;
                                item.iSelectedImage = 1;
                            }
                            TreeView_SetItem(((LPNMHDR)lParam)->hwndFrom, &item);
                        }
                        break;
                    }
                }
            }
            break;
        }

Yukarıdaki satırların amacı, alt item'lar içeren item'lar kapandığında ve açıldığında farklı resim atama işlemi yapmaktır.

6. InitTreeViewImageLists() fonksiyonunu oluşturma

Bu fonksiyon Treeview kontrolü resim listesi oluşturmak için kullanılacaktır.


BOOL InitTreeViewImageLists(HWND hwndTV)
{
  HIMAGELIST himl;  /* Imagelist handle */
  HICON hicon;      /* Icon handle */
  /* Imagelist oluşturma (3 resim içeren) */
  if ((himl = ImageList_Create(16, 16, ILC_COLOR32, 3, 0)) == NULL) return FALSE;

  /* .ico dosyası yükleme ve resim listesine ekleme */
  hicon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONFOLDER), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
  ImageList_AddIcon(himl, hicon);
  DeleteObject(hicon);

  hicon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONOPENFOLDER), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
  ImageList_AddIcon(himl, hicon);
  DeleteObject(hicon);

  hicon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONFILE), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
  ImageList_AddIcon(himl, hicon);
  DeleteObject(hicon);

  /* Bütün .ico dosyaları (3 adet) eklenmezse hata değeri döndürür. */
  if (ImageList_GetImageCount(himl) < 3) return FALSE;

  /* Imagelist'i Treeview kontrolüne atama */
  TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL);

  return TRUE;
}

7. AddItemToTree() fonksiyonunu oluşturma

Bu fonksiyon Treeview kontrolüne item eklemek için kullanılacaktır.


HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel)
{
  TVITEM tvi;
  TVINSERTSTRUCT tvins;
  static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
  static HTREEITEM hPrevRootItem = NULL;
  static HTREEITEM hPrevLev2Item = NULL;
  HTREEITEM hti;

  tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;

  /* Metin değerlerini atama */
  tvi.pszText = lpszItem;
  tvi.cchTextMax = sizeof(tvi.pszText)/sizeof(tvi.pszText[0]);

  /* Item'ın parent olmadığını yani alt item'lar içermediğini kabul ederek dosya icon resmini atama */
  tvi.iImage = 2;
  tvi.iSelectedImage = 2;

  /* Item verilerini atama */
  tvi.lParam = (LPARAM)nLevel;
  tvins.item = tvi;
  tvins.hInsertAfter = hPrev;

  /* Tanımlanan seviyeye göre parent item'ı ayarlama */
  if (nLevel == 1) tvins.hParent = TVI_ROOT;
  else if (nLevel == 2) tvins.hParent = hPrevRootItem;
  else tvins.hParent = hPrevLev2Item;

  /* Item'ı Treeview kontrolüne kaydetme */
  hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);

  if (hPrev == NULL) return NULL;

  /* Handle değerini item'a kaydetme */
  if (nLevel == 1) hPrevRootItem = hPrev;
  else if (nLevel == 2) hPrevLev2Item = hPrev;

  /* Yeni item diğer bir item altında oluşturulmuşsa, parent item'a
     alt item2lar içerdiğini gösteren kapalı klasör resmini atama işlemi */
  if (nLevel > 1) {
      hti = TreeView_GetParent(hwndTV, hPrev);
      tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
      tvi.hItem = hti;
      tvi.iImage = 0;
      tvi.iSelectedImage = 0;
      TreeView_SetItem(hwndTV, &tvi);
  }

  return hPrev;
}

8. Kodların son hali (main.c dosyası)

Yukarıdaki kodları eklediğimizde, main.c dosyasının en son hali aşağıdaki şekilde olacaktır.

main.c


#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

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

HWND hwndTreeview;

BOOL InitTreeViewImageLists(HWND hwndTV);
HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel);

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                      /* Extended possibilites for variation */
           szClassName,            /* Classname */
           _T("Treeview örneği"),       /* Title Text */
           WS_OVERLAPPEDWINDOW,    /* default window */
           CW_USEDEFAULT,          /* Windows decides the position */
           CW_USEDEFAULT,          /* where the window ends up on the screen */
           544,                    /* The programs width */
           375,                    /* and height in pixels */
           HWND_DESKTOP,           /* The window is a child-window to desktop */
           NULL,                   /* No menu */
           hThisInstance,          /* Program Instance handler */
           NULL                    /* No Window Creation data */
           );

    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_CREATE:
        {
             HTREEITEM hitem, hitem02;
             /* Treeview oluşturma */
             hwndTreeview = CreateWindowEx(0, WC_TREEVIEW, TEXT("Tree View"), WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES,
                            5, 5, 320, 320,
                            hwnd, (HMENU)IDC_TREEVIEW, 0, NULL);

             /* Imagelist oluşturma */
             InitTreeViewImageLists(hwndTreeview);

             /* Treeview item değerlerini ekleme */
             AddItemToTree(hwndTreeview, "Item01", 1);

             AddItemToTree(hwndTreeview, "Item02", 1);

             hitem = AddItemToTree(hwndTreeview, "Item03", 1);

             hitem02 = AddItemToTree(hwndTreeview, "Item03-01", 2);
             AddItemToTree(hwndTreeview, "Item03-01-01", 3);
             AddItemToTree(hwndTreeview, "Item03-01-02", 3);
             TreeView_Expand(hwndTreeview, hitem02, TVE_EXPAND);

             hitem02 = AddItemToTree(hwndTreeview, "Item03-02", 2);
             AddItemToTree(hwndTreeview, "Item03-02-01", 3);
             AddItemToTree(hwndTreeview, "Item03-02-02", 3);
             AddItemToTree(hwndTreeview, "Item03-02-03", 3);
             TreeView_Expand(hwndTreeview, hitem02, TVE_EXPAND);

             AddItemToTree(hwndTreeview, "Item03-03", 2);

             hitem02 = AddItemToTree(hwndTreeview, "Item03-04", 2);
             AddItemToTree(hwndTreeview, "Item03-02-01", 3);
             AddItemToTree(hwndTreeview, "Item03-02-02", 3);
             TreeView_Expand(hwndTreeview, hitem02, TVE_EXPAND);

             AddItemToTree(hwndTreeview, "Item03-05", 2);

             AddItemToTree(hwndTreeview, "Item03-06", 2);

             AddItemToTree(hwndTreeview, "Item04", 1);

             TreeView_Expand(hwndTreeview, hitem, TVE_EXPAND);
        }
        break;

        case WM_NOTIFY:
        {
            /* Alt item'lar içeren item'lar kapandığında ve açıldığında farklı resim atama işlemi için */
            if (((LPNMHDR)lParam)->hwndFrom == hwndTreeview){
                switch (((LPNMHDR)lParam)->code){
                    case TVN_ITEMEXPANDED:
                    {
                        LPNMTREEVIEW nmtv = (LPNMTREEVIEW)lParam;

                        if (nmtv->hdr.code == TVN_ITEMEXPANDED){
                            TVITEM item = nmtv->itemNew;
                            item.stateMask = TVIF_IMAGE;

                            if (nmtv->action == TVE_COLLAPSE){
                                item.iImage = 0;
                                item.iSelectedImage = 0;
                            }
                            else if (nmtv->action == TVE_EXPAND){
                                item.iImage = 1;
                                item.iSelectedImage = 1;
                            }
                            TreeView_SetItem(((LPNMHDR)lParam)->hwndFrom, &item);
                        }
                        break;
                    }
                }
            }
            break;
        }

        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

BOOL InitTreeViewImageLists(HWND hwndTV)
{
  HIMAGELIST himl;  /* Imagelist handle */
  HICON hicon;      /* Icon handle */
  /* Imagelist oluşturma (3 resim içeren) */
  if ((himl = ImageList_Create(16, 16, ILC_COLOR32, 3, 0)) == NULL) return FALSE;

  /* .ico dosyası yükleme ve resim listesine ekleme */
  hicon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONFOLDER), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
  ImageList_AddIcon(himl, hicon);
  DeleteObject(hicon);

  hicon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONOPENFOLDER), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
  ImageList_AddIcon(himl, hicon);
  DeleteObject(hicon);

  hicon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONFILE), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
  ImageList_AddIcon(himl, hicon);
  DeleteObject(hicon);

  /* Bütün .ico dosyaları (3 adet) eklenmezse hata değeri döndürür. */
  if (ImageList_GetImageCount(himl) < 3) return FALSE;

  /* Imagelist'i Treeview kontrolüne atama */
  TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL);

  return TRUE;
}

HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel)
{
  TVITEM tvi;
  TVINSERTSTRUCT tvins;
  static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
  static HTREEITEM hPrevRootItem = NULL;
  static HTREEITEM hPrevLev2Item = NULL;
  HTREEITEM hti;

  tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;

  /* Metin değerlerini atama */
  tvi.pszText = lpszItem;
  tvi.cchTextMax = sizeof(tvi.pszText)/sizeof(tvi.pszText[0]);

  /* Item'ın parent olmadığını yani alt item'lar içermediğini kabul ederek dosya icon resmini atama */
  tvi.iImage = 2;
  tvi.iSelectedImage = 2;

  /* Item verilerini atama */
  tvi.lParam = (LPARAM)nLevel;
  tvins.item = tvi;
  tvins.hInsertAfter = hPrev;

  /* Tanımlanan seviyeye göre parent item'ı ayarlama */
  if (nLevel == 1) tvins.hParent = TVI_ROOT;
  else if (nLevel == 2) tvins.hParent = hPrevRootItem;
  else tvins.hParent = hPrevLev2Item;

  /* Item'ı Treeview kontrolüne kaydetme */
  hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);

  if (hPrev == NULL) return NULL;

  /* Handle değerini item'a kaydetme */
  if (nLevel == 1) hPrevRootItem = hPrev;
  else if (nLevel == 2) hPrevLev2Item = hPrev;

  /* Yeni item diğer bir item altında oluşturulmuşsa, parent item'a
     alt item2lar içerdiğini gösteren kapalı klasör resmini atama işlemi */
  if (nLevel > 1) {
      hti = TreeView_GetParent(hwndTV, hPrev);
      tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
      tvi.hItem = hti;
      tvi.iImage = 0;
      tvi.iSelectedImage = 0;
      TreeView_SetItem(hwndTV, &tvi);
  }

  return hPrev;
}

Program derleyip çalıştırdığımızda aşağıdakine benzer bir ekran görüntüsü karşımıza gelecektir. Alt item'lar içeren item'lar açılıp kapatıldığında item'ları gösteren resimler değişiecektir.

6. Proje dosyaları ve .exe dosya

Programın kaynak kodları

Programın exe dosyası