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

Ana page > Programlama > Windows API Programlama > WinAPI programları > bg_lvgrid

Windows API programları

BG Listview Grid

Windows API ile bir ListView kontrolü kullanarak içeriği değiştirilebilir, fare ve ok tuşları ile hücreleri arasında geçiş yapılabilen bir grid arayüz oluşturabiliriz.

1. Öncelikle burada gösterildiği gibi bir Windows API projesi oluşturalım.

2. main.c dosyasını aşağıdaki şekilde düzenleyerek aşağıdaki hale getirelim.

main.c


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

// commctrl.h içindeki bazı verilerin kullanımı için gerekli
#define _WIN32_WINNT 0x0601
#define _WIN32_IE 0x0501

#define IDC_LISTVIEW 1001
#define IDC_EDITLV 1002
#define IDC_CHECKBOX 1003

#define RENKFONT RGB(50, 50, 50)
#define RENKEDITBG RGB(234, 250, 74)

#include <tchar.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <stdio.h>

HWND hwndListView;
HWND hwndEditLV;
HWND hwndCheckBox;

int actItem, actSubItem;

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

LRESULT CALLBACK ListViewProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK EditLVProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
void FillListView(HWND hwnd);
int bg_GetColumCount (HWND hwnd);

// 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("BG Listview Grid"),       // Title Text
           WS_OVERLAPPEDWINDOW, // default window
           CW_USEDEFAULT,       // Windows decides the position
           CW_USEDEFAULT,       // where the window ends up on the screen
           530,                 // The programs width
           265,                 // 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
           );

    // Make the window visible on the screen
    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:
        {
             // ListView kontrolu oluşturma
             hwndListView = CreateWindowEx(WS_EX_CLIENTEDGE , WC_LISTVIEW, "",
                                  WS_CHILD | LVS_REPORT | WS_VISIBLE,
                                  10, 10, 494, 185, hwnd,
                                  (HMENU) IDC_LISTVIEW, NULL, NULL);
             ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_GRIDLINES);
             // ListView kontroluna Subclassing işlemi uygulama
             SetWindowSubclass(hwndListView, ListViewProc, 0, 0);
             // ListView içeriğini doldurma
             FillListView(hwndListView);

             // ListView içindeki veri girişi için kullanılacak olan Edit kontrolu oluşturma
             hwndEditLV = CreateWindowEx(0, "EDIT", "",
                                             WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_MULTILINE, 0, 0, 50, 20,
                                             hwndListView, (HMENU) IDC_EDITLV, NULL, NULL);
             ShowWindow(hwndEditLV, SW_HIDE);
             // Edit kontroluna Subclassing işlemi uygulama
             SetWindowSubclass(hwndEditLV, EditLVProc, 0, 0);
             SendMessage(hwndEditLV, WM_SETFONT, SendMessage(hwndListView, WM_GETFONT, 0, 0), TRUE);

             // Edit kontrolundan SubItem'lara geçiş seçeneği
             hwndCheckBox = CreateWindowEx(0, "BUTTON", " Edit aktif iken ok tuşlarıyla geçiş yapma",
                                         WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 10, 200, 250, 20,
                                         hwnd, (HMENU) IDC_CHECKBOX, NULL, NULL);
             SendMessage(hwndCheckBox, WM_SETFONT, SendMessage(hwndListView, WM_GETFONT, 0, 0), TRUE);

        }
        break;

        case WM_NOTIFY:
        {
           switch (((LPNMHDR)lParam)->code) {
              case NM_CLICK:
              {
                   if (((LPNMHDR)lParam)->hwndFrom == hwndListView) {
                       LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam;
                       LVHITTESTINFO hti = { 0 };
                       RECT rcItem;
                       char cdizi[50];

                       hti.pt = lpnmia->ptAction;
                       // ListView kontrolu LVS_EX_FULLROWSELECT özelliği ile tanımlanmadığında,
                       // NM_CLICK bildirim mesajında lpnmia->iItem ilk sütun dışındaki tıklamalarda daima -1 değeri aldığından
                       // seçilen Item değerini belirlemek için ListView_SubItemHitTest() fonksiyonu kullanılır.
                       ListView_SubItemHitTest(lpnmia->hdr.hwndFrom,  &hti);

                       ListView_GetItemText(lpnmia->hdr.hwndFrom, hti.iItem, hti.iSubItem, cdizi, sizeof(cdizi));
                       // Seçilen Item ve SubItem indeks değerlerini global değişikenlere aktarma
                       actItem = hti.iItem;
                       actSubItem = hti.iSubItem;
                       // ListView aktif SubItem değerini Edit kontroluna aktarma
                       if (strlen(cdizi)) SetWindowText(hwndEditLV, cdizi);
                       else SetWindowText(hwndEditLV, "");

                       if (ListView_GetSubItemRect(lpnmia->hdr.hwndFrom, hti.iItem, hti.iSubItem, LVIR_BOUNDS, &rcItem)) {
                           if (hti.iSubItem==0) {
                               rcItem.right = ListView_GetColumnWidth(lpnmia->hdr.hwndFrom, 0);
                               rcItem.left-=2;
                           }
                           MoveWindow(hwndEditLV, rcItem.left+1, rcItem.top, (rcItem.right-rcItem.left)-1, (rcItem.bottom-rcItem.top)-1, TRUE);

                           GetClientRect(hwndEditLV, &rcItem);
                           rcItem.left += 5;
                           // Edit kontrolu aktif olduğunda metin başlangıcını
                           // ListView SubItem metin başlangıçları ile aynı margin değere ayarlamak için
                           SendMessage(hwndEditLV, EM_SETRECT, 0, (LPARAM)&rcItem);
                           // Edit kontrolu metin içeriğini seçme
                           Edit_SetSel(hwndEditLV, 0, -1);
                           // Edit kontrolunu gösterme
                           ShowWindow(hwndEditLV, SW_SHOW);
                           // Edit kontrolunu aktif hale getirme
                           SetFocus(hwndEditLV);
                       }
                   }
              }
              break;

              // ListView kontrolunun en solundaki Item sütununa tıklandığında
              // görünüp kaybolan mavi arka plan rengini engellemek için
              case LVN_ITEMCHANGING:
                   return TRUE;
                   break;
           }
           break;
        }

        // CheckBox kontrolunu transparent yapmak için
        case WM_CTLCOLORSTATIC:
        {
             if((HWND)lParam == hwndCheckBox) {
                HBRUSH hbr = (HBRUSH)DefWindowProc(hwnd, message, wParam, lParam);
                DeleteObject(hbr);
                SetBkMode((HDC)wParam, TRANSPARENT);
                return (LRESULT) GetStockObject(NULL_BRUSH);
            }
        }
        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;
}

LRESULT CALLBACK ListViewProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  static RECT rc;
  static char cdizi[100];

  switch (message) {
     case WM_KEYDOWN:
     {
          switch (wParam) {
            case VK_HOME:
                 actSubItem = 0;
                 break;
            case VK_END:
                 actSubItem = bg_GetColumCount(hwnd) - 1;
                 break;
            case VK_PRIOR:
                 actItem = 0;
                 break;
            case VK_NEXT:
                 actItem = ListView_GetItemCount(hwnd) - 1;
                 break;
            case VK_UP:
                 actItem = (actItem==0) ? 0 : actItem - 1;
                 break;
            case VK_DOWN:
                 actItem = (ListView_GetItemCount(hwnd) - 1) == actItem ? actItem : actItem + 1;
                 break;
            case VK_LEFT:
                 actSubItem = (actSubItem==0) ? 0 : actSubItem - 1;
                 break;
            case VK_RIGHT:
                 actSubItem = (bg_GetColumCount(hwnd) - 1) == actSubItem ? bg_GetColumCount(hwnd) - 1 : actSubItem + 1;
                 break;
            default:
                 return 0;
          }

          // Edit kontrolunu gösterme
          ShowWindow(hwndEditLV, FALSE);
          // Aktif SubItem pencere boyutlarını alma
          ListView_GetSubItemRect(hwnd, actItem, actSubItem, LVIR_LABEL, &rc);
          // Edit kontrolunu aktif SubItem penceresi içine yerleştirme
          SetWindowPos(hwndEditLV, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_DEFERERASE);

          // Aktif SubItem metin değerini alma
          ListView_GetItemText(hwnd, actItem, actSubItem, cdizi, sizeof cdizi);

          // Edit kontrolu margin değerlerini ayarlama
          GetClientRect(hwndEditLV, &rc);
          if (actSubItem==0) rc.left+=2;
          else rc.left += 6;
          SendMessage(hwndEditLV, EM_SETRECT, 0, (LPARAM)&rc);

          // Edit kontrolu metin içeriğini ayarlama.
          Edit_SetText(hwndEditLV, cdizi);
          // Edit kontrolu metin içeriğini seçme
          Edit_SetSel(hwndEditLV, 0, -1);
          // Edit kontrolunu gösterme
          ShowWindow(hwndEditLV, TRUE);

          return 0;
     }
     break;

     case WM_KEYUP:
     {
          if(wParam==VK_RETURN)	{ // SubItem'da işlem yapma
             Edit_SetSel(hwndEditLV, 0, -1);
             ShowWindow(hwndEditLV, TRUE);
             SetFocus(hwndEditLV);
          }
     }
     break;

     // Edit kontrolu arka plan ve font rengini belirleme
     case WM_CTLCOLOREDIT:
     {
          if (lParam==(LPARAM)hwndEditLV) {
              HDC hdcStatic = (HDC) wParam;
              SetTextColor(hdcStatic, RENKFONT);
              SetBkColor(hdcStatic, RENKEDITBG);
              return (INT_PTR)CreateSolidBrush(RENKEDITBG);
          }
          break;
     }
  }

  return DefSubclassProc(hwnd, message, wParam, lParam);
}

LRESULT CALLBACK EditLVProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  static char citem[50];

  switch(message) {
     case WM_CHAR: // ENTER tuşuna işlem yapma
          if (wParam==VK_RETURN) return TRUE;
          break;

     case WM_KEYUP:
          switch (wParam) {
             case VK_RETURN:
             case VK_RIGHT:
             case VK_LEFT:
             case VK_UP:
             case VK_DOWN:
             {
                   char cdizi[50];
                   Edit_GetText(hwnd, cdizi, sizeof cdizi);
                   ListView_SetItemText(GetParent(hwnd), actItem, actSubItem, cdizi);

                   if (VK_RETURN == wParam) {      // VK_RETURN
                       SetFocus(GetParent(hwnd));  // ListView aktif kontrol olarak seçilir.
                       return 0;
                   }
                   else { // Edit kontrolunda işlem yaparken ListView üzerindeki diğer SubItem'lara ok tuşlarıyla geçiş yapma
                       // CheckbBox kontrolu işaretli ise
                       if (SendMessage(hwndCheckBox, BM_GETCHECK, 0, 0)==BST_CHECKED) {
                           SNDMSG(GetParent(hwnd), WM_KEYDOWN, (WPARAM)wParam, 0L);
                           SNDMSG(GetParent(hwnd), WM_KEYUP, (WPARAM)VK_RETURN, 0L);
                       }
                   }
             }
          }

          break;

     case WM_KEYDOWN:
          // ESC tuşuna işlem yapma
          if (wParam==VK_ESCAPE) {
              ListView_GetItemText(GetParent(hwnd), actItem, actSubItem, citem, sizeof citem); // Aktif SubItem metin değerini alma
              Edit_SetText(hwnd, citem); // Edit değerini ayarlama.
              SetFocus(GetParent(hwnd)); // ListView aktif kontrol olarak seçilir.

              return 0;
          }
          break;

     case WM_DESTROY: // Subclassing işlemini kaldırma
          RemoveWindowSubclass(hwnd, EditLVProc, 0);
          return 0;
          break;
  }

  return DefSubclassProc(hwnd, message, wParam, lParam);
}

void FillListView(HWND hwnd)
{
  LVCOLUMN lvc;
  LVITEM lvI;
  int idCol=5, idItem=10;
  char cdizi[15];
  int id1, id2;

  // LVCOLUMN structure için ilk değer atama işlemlerini yapar.
  lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  lvc.cx = 98;           // Sütun genişliği değeri
  lvc.pszText = cdizi;
  lvc.cchTextMax = sizeof(cdizi);
  lvc.fmt = LVCFMT_LEFT; // Sütunları sol tarafa ayarlar

  // Sütun ekleme işlemleri
  for (id1=0; id1<idCol; id1++) {
       lvc.iSubItem = id1;
       sprintf(cdizi, "%s%d", "Column", id1+1);
       lvc.pszText = cdizi;
       // Sütun ekleme işlemi
       ListView_InsertColumn(hwnd, id1, &lvc);
  }

  // LVITEM structure için ilk değer atama işlemlerini yapar.
  lvI.mask      = LVIF_TEXT | LVIF_STATE;
  lvI.stateMask = 0;
  lvI.iSubItem  = 0;
  lvI.state     = 0;

  // Satır ekleme işlemleri
  for (id1=0; id1<idItem; id1++) {
       lvI.iItem  = id1;
       sprintf(cdizi, "%s%d", "Item", id1+1);
       lvI.pszText   = cdizi;
       // İlk sütun için öğe ekleme işlemleri
       ListView_InsertItem(hwnd, &lvI);

       // Diğer sütunlar için öğe ekleme işlemleri
       for (id2=1; id2<idCol; id2++) {
            sprintf(cdizi, "%s%d-%d", "SubItem", id1+1, id2+1);
            ListView_SetItemText(hwnd, id1, id2, cdizi);
       }
  }
}

int bg_GetColumCount (HWND hwnd)
{
  return (int) SendMessage((HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0), HDM_GETITEMCOUNT, 0, 0L);
}

Program derleyip çalıştırdığımızda aşağıdakine benzer bir ekran görüntüsü karşımıza gelecektir:

Programın kaynak kodları

Programın exe dosyası