Bir Windows API programında kullanılan herhangi bir kontrolün dış çerçeve rengini değiştirmek için, kontrol için subclassing işlemi uygulayarak bir fonksiyon tanımladıktan sonra, bu fonksiyon içinde WM_NCPAINT ve WM_NCCALCSIZE mesajlarına işlem yapabiliriz.
Subclassing işlemi için commctrl.h başlık dosyasına ihtiyaç duyulmaktadır.
WM_NCPAINT mesajı bir kontrol penceresi dış çerçevesi çizileceği zaman pencereye gönderilir.
WM_NCCALCSIZE mesajı bir kontrol penceresinin istemci (client) alanının boyutu ve pozisyonu yeniden hesaplanması gerektiğinde gönderilir. Program bu mesaja işlem yaparak, bir pencerenin boyutu veya konumu değiştiğinde, pencerenin istemci (client) alanının içeriğini kontrol edebilir.
Ancak, WM_NCCALCSIZE mesajı Subclass fonksiyonu içinde çağrılmadığından, SetWindowPos() fonksiyonu SWP_FRAMECHANGED seçeneği ile kullanılmalıdır.
Programda birer adet Static, Edit, ListView ve ListBox kontrolu ile işlem yapılmaktadır. Programın ana dosya içeriği aşağıdaki şekilde olacaktır.
main.c
#define _WIN32_WINNT 0x0601 // DefSubclassProc için gerekli
#define _WIN32_IE 0x0501 // DefSubclassProc için gerekli
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
#include <tchar.h>
#include <windows.h>
#include <commctrl.h> // DefSubclassProc için gerekli
#include <stdio.h>
HWND hwndStatic, hwndEdit, hwndListView, hwndListBox, hwndRichEdit;
// Declare Windows procedure
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
// Subclassing fonksiyon bildirimi
LRESULT CALLBACK ControlBorderProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
void CreateListView(HWND hwndParent);
void CreateListBox(HWND hwndParent);
// 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("Custom control border"), // Title Text
WS_OVERLAPPEDWINDOW, // default window
CW_USEDEFAULT, // Windows decides the position
CW_USEDEFAULT, // where the window ends up on the screen
525, // The programs width
380, // 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:
{
hwndStatic = CreateWindowEx(0, "STATIC", "Static kontrol",
WS_CHILD | WS_VISIBLE, 20, 20, 300, 20,
hwnd, 0, NULL, NULL);
// Subclassing işlemi için fonksiyon tanımlama
SetWindowSubclass(hwndStatic, ControlBorderProc, 0, 0);
// Subclass fonksiyonu içinde WM_NCCALCSIZE mesajı çağrılmadığından, SetWindowPos() fonksiyonu SWP_FRAMECHANGED seçeneği ile çağrılmalıdır.
SetWindowPos(hwndStatic, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); // Static kontrol için gerekli
hwndEdit = CreateWindowEx(0, "EDIT", "Edit kontrol",
WS_CHILD | WS_VISIBLE, 20, 50, 300, 20,
hwnd, 0, NULL, NULL);
// Subclassing işlemi için fonksiyon tanımlama
SetWindowSubclass(hwndEdit, ControlBorderProc, 0, 0);
SetWindowPos(hwndEdit, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); // Edit kontrolu için gerekli
CreateListView(hwnd);
// Subclassing işlemi için fonksiyon tanımlama
SetWindowSubclass(hwndListView, ControlBorderProc, 0, 0);
CreateListBox(hwnd);
// Subclassing işlemi için fonksiyon tanımlama
SetWindowSubclass(hwndListBox, ControlBorderProc, 0, 0);
SetWindowPos(hwndListBox, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); // ListBox kontrolu için gerekli
}
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;
}
// Subclassing fonksiyonu
LRESULT CALLBACK ControlBorderProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (message) {
case WM_NCPAINT:
{
RECT rect;
HDC hdc;
hdc = GetWindowDC(hwnd);
GetWindowRect(hwnd, &rect);
OffsetRect(&rect, -rect.left, -rect.top);
FrameRect(hdc, &rect, CreateSolidBrush(RGB(255, 0, 0)));
ReleaseDC(hwnd, hdc);
return 0;
}
// Static, Edit ve ListBox kontrolleri için gerekli
case WM_NCCALCSIZE:
if(lParam) {
NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)lParam;
InflateRect(&sz->rgrc[0], -1, -1);
return 0;
}
break;
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
void CreateListView(HWND hwndParent)
{
LVCOLUMN lvc;
LVITEM lvI;
int idCol=3, idItem=8;
char cdizi[15];
int id1, id2;
hwndListView = CreateWindowEx(WS_EX_CLIENTEDGE , WC_LISTVIEW, "",
WS_CHILD | LVS_REPORT | WS_VISIBLE,
20, 80, 300, 245, hwndParent,
0, NULL, NULL);
// 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.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(hwndListView, 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(hwndListView, &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(hwndListView, id1, id2, cdizi);
}
}
}
void CreateListBox(HWND hwndParent)
{
char cdizi[15];
int id;
hwndListBox = CreateWindowEx(0, "LISTBOX", NULL,
WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 340, 20, 150, 310,
hwndParent, 0, NULL, NULL);
for (id=0; id<10; id++) {
sprintf(cdizi, "%s%d", "Item", id+1);
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM) cdizi);
}
SendMessage(hwndListBox, LB_SETCURSEL, 0, 0);
}
Program derleyip çalıştırdığımızda aşağıdakine benzer bir ekran görüntüsü karşımıza gelecektir: