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: