Доброго дня.
Сьогодні ми розглянемо створення і використання табличного віджета в МС Віндовс.
Функції
І найперше, що потрібно розглянути — це основні функції і структури, які необхідно знати при використанні таблиці у вікні.
Для створення примірника табличного елементу необхідно скористатися функцією CreateWindow. Її виклик може виглядати наступним чином.
/* створюємо примірник табличного елементу */
listview = CreateWindow (WC_LISTVIEW,
L"listview",
WS_TABSTOP | WS_VISIBLE | WS_CHILD |
LVS_REPORT | LVS_EDITLABELS, /* прапорці властивостей */
0, /* відступ від лівого краю вікна */
0, /* відступ від верхнього краю вікна */
100, /* відступ від правого краю */
100, /* відступ від нижнього краю */
window, /* батьківське вікно */
0, /* меню */
hThisInstance,
NULL) ;
Наступним кроком стане створення колонок для елементу за допомогою функції ListView_InsertColumn, яка вставляє колонку в табличний елемент, і структури LVCOLUMN, яка описує дану колонку. Оголошення функції ListView_InsertColumn виглядає наступним чином.
int ListView_InsertColumn(HWND hwnd, int iCol, const LPLVCOLUMN pcol) ;
hwnd — це примірник, створеного раніше функцією CreateWindow, табличного елементу.
iCol — це порядковий номер колонки.
pcol — це вказівник на заповнену структуру LPLVCOLUMN.
Оголошення структури LPLVCOLUMN виглядає наступним чином:
typedef struct _LVCOLUMN {
UINT mask;
int fmt;
int cx;
LPTSTR pszText;
int cchTextMax;
int iSubItem;
#if (_WIN32_IE >= 0x0300)
int iImage;
int iOrder;
#endif
#if (_WIN32_WINNT >= 0x0600)
int cxMin;
int cxDefault;
int cxIdeal;
#endif
} LVCOLUMN, *LPLVCOLUMN;
Нам знадобляться наступні її елементи:
cx — ширина колонки в пікселях;
iSubItem — індекс дочірнього елементу (встановлюємо в 0);
mask — ця змінна містить інформацію про те, яка інформація є правильно заповненою і доступною в структурі;
fmt — вирівнювання заголовку колонки і її елементів (LVCFMT_LEFT, LVCFMT_RIGHT, LVCFMT_CENTER і інші);
pszText — заголовок колонки.
Для того, щоб додавати елементи (значення) в таблицю, необхідно скористатися функціями-макросами ListView_InsertItem і ListView_SetItem, а вказувати дані необхідно в структурі LVITEM. Оголошення функцій виглядає наступним чином.
int ListView_InsertItem (HWND hwnd, const LPLVITEM pitem) ;
BOOL ListView_SetItem (HWND hwnd, const LPLVITEM pitem) ;
hwnd — це вказівник на табличний елемент;
pitem — це вказівник на заповнену структуру LVITEM.
Для заповнення таблиці даними, необхідно спочатку використати функцію ListView_InsertItem, яка створить новий рядок і встановить першу комірку для нього, всі інші комірки даного рядка необхідно вставляти функцією ListView_SetItem. Без використання функції ListView_InsertItem ваша програма буде завершуватись з помилкою.
Оголошення структури LVITEM виглядає наступним чином:
typedef struct {
UINT mask;
int iItem;
int iSubItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
int iIndent;
#endif
#if (_WIN32_WINNT >= 0x0501)
int iGroupId;
UINT cColumns;
PUINT puColumns;
#endif
#if (_WIN32_WINNT >= 0x0600)
int *piColFmt;
int iGroup;
#endif
} LVITEM, *LPLVITEM ;
Основні її елементи, які ми будемо використовувати:
mask — це є бітна маска, котра вказує, які дані є доступними (заповненими) для використання (LVIF_TEXT);
stateMask — встановлюємо в 0;
state — також являється бітовою маскою, її встановимо в 0.
pszText — текст комірки.
ISubItem — номер колонки в таблиці.
іItem — номер рядка в таблиці.
Програма
Розглянемо приклад програми, яка використовує табличний віджет.
#include <windows.h>
#include <commctrl.h>
#include <Commdlg.h>
#include <Wingdi.h>
#include <commctrl.h>
#include <string>
using namespace std ;
/* об'єкти вікна і табличного елементу */
HWND window ;
HWND listview ;
/* головна функція програми (аналог main) */
int WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil) ;
/* функція обробки повідомлень */
LRESULT CALLBACK WindowProcedure (HWND,
UINT,
WPARAM,
LPARAM) ;
/* функція заповнення даними таблиці */
void fill_table_with_data () ;
/* ім'я класу вікна */
wchar_t window_class_name [] = L"WindowInWindowsClass" ;
/* реалізація головної функції програми */
int WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
/* ініціація бібліотеки */
InitCommonControls () ;
/* Змінна, в якій зберігаються повідомлення до програми */
MSG messages ;
/* структура даних для класу вікна */
WNDCLASSEX wincl ;
/* заповнюємо структуру класу вікна */
wincl.hInstance = hThisInstance ;
wincl.lpszClassName = window_class_name ;
wincl.lpfnWndProc = WindowProcedure ;
wincl.style = CS_DBLCLKS ;
wincl.cbSize = sizeof (WNDCLASSEX) ;
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
wincl.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wincl.lpszMenuName = NULL ;
wincl.cbClsExtra = 0 ;
wincl.cbWndExtra = 0 ;
/* Створюємо об'єкт, який буде заповняти кольором полотно вікна */
wincl.hbrBackground = (HBRUSH) CreateSolidBrush (RGB (232, 246, 255)) ;
/* реєструємо клас і перевіряємо повернене */
/* значення на предмет помилки */
if (!RegisterClassEx (&wincl))
{
MessageBox (GetDesktopWindow (),
L"Не можу зареєструвати клас вікна",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* створюємо примірник вікна */
window = CreateWindowEx (0, /* Додаткові можливості */
window_class_name, /* ім'я класу вікна */
L"Вікно таблиці", /* заголовок */
WS_OVERLAPPEDWINDOW, /* звичайне вікно */
1, /* координата лівого верхнього кута по осі x */
1, /* координата лівого верхнього кута по осі у */
1000, /* ширина програми в пікселях */
600, /* висота програми в пікселях */
GetDesktopWindow (), /* вікно буде дочірнім до робочого столу */
NULL, /* немає меню */
hThisInstance, /* примірник обробника програми */
NULL) ; /* дані створення вікна (сигнал WM_CREATE) */
/* перевіряємо повернене значення на предмет помилки */
if (window==NULL)
{
MessageBox (GetDesktopWindow (),
L"Помилка при створенні примірника вікна.",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* змінна, яка буде зберігати розміри вікна */
RECT rcClient ;
/* дізнаємося розміри вікна */
GetClientRect (window, &rcClient) ;
/* створюємо примірник табличного елементу */
listview = CreateWindow (WC_LISTVIEW,
L"listview",
WS_TABSTOP | WS_VISIBLE | WS_CHILD |
LVS_REPORT | LVS_EDITLABELS, /* прапорці властивостей */
2, /* відступ від лівого краю вікна */
2, /* відступ від верхнього краю вікна */
rcClient.right - rcClient.left - 2, /* відступ від правого краю */
rcClient.bottom - rcClient.top - 2, /* відступ від нижнього краю */
window, /* батьківське вікно */
0, /* меню */
hThisInstance,
NULL) ;
/* перевіряємо повернене значення на предмет помилки */
if (listview==NULL)
{
MessageBox (GetDesktopWindow (),
L"Помилка при створенні примірника табличного елементу.",
L"Невиправна помилка!",
MB_OK | MB_ICONERROR) ;
return 0 ;
}
/* змінні для створення колонок табличного елементу*/
wstring col_name ;
LVCOLUMN lvc ; /* об'єкт ініціалізації колонки */
/* частково заповнюємо структуру */
lvc.cx = 50 ; /* ширина колонки в пікселях */
lvc.iSubItem = 0 ; /* дочірні елементи */
/* Дана бітова маска вказує, що формат, ширина, текст і дочірні елементи
** дозволені для даної структури */
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM ;
lvc.fmt = LVCFMT_LEFT ;
col_name = L"Номер" ;
/* встановлюємо назву першої колонки */
lvc.pszText = (LPWSTR) col_name.c_str() ;
/* вставляємо першу колонку в табличний елемент */
if (ListView_InsertColumn (listview, /* табличний елемент */
0, /* порядковий номер колонки */
&lvc) /* структура ініціалізації колонки */
==(-1))
{
MessageBox(window,
L"Не можу створити колонку для об'єкта представлення табличних даних",
L"Невиправна помилка!",
MB_ICONERROR | MB_OK) ;
return 0 ;
}
col_name = L"Дані" ;
/* встановлюємо назву другої колонки */
lvc.pszText = (LPWSTR) col_name.c_str() ;
/* вставляємо другу колонку в табличний елемент */
if (ListView_InsertColumn (listview, /* табличний елемент */
1, /* порядковий номер колонки */
&lvc) /* структура ініціалізації колонки */
==(-1))
{
MessageBox(window,
L"Не можу створити колонку для об'єкта представлення табличних даних",
L"Невиправна помилка!",
MB_ICONERROR | MB_OK) ;
return 0 ;
}
/* Показуємо вікно на весь екран */
ShowWindow (window, SW_SHOW) ;
/* заповняємо таблицю даними */
fill_table_with_data () ;
/* Приймаємо повідомлення, поки не зустрінемо 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Перетворюємо віртуальні клавіші до символьних */
TranslateMessage (&messages) ;
/* Посилаємо повідомлення до WindowProcedure */
DispatchMessage (&messages) ;
}
/* Повертаємо значення, яке нам дала функція PostQuitMessage() */
return messages.wParam ;
}
/* Дана функція викликається системною функцією DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND phwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
/* обробляємо повідомлення */
switch (message)
{
case WM_DESTROY:
/* посилаємо повідомлення WM_QUIT */
PostQuitMessage (0) ;
break;
case WM_SIZE:
/* дана подія вказує на зміни розміру вікна */
/* визначаємо розміри вікна */
RECT rcClient ;
GetClientRect (window, &rcClient) ;
/* у відповідності до нових розмірів переміщаємо
** табличний елемент і змінюємо його розміри відносно вікна */
MoveWindow (listview,
2, /* двохпіксельні відступи від країв рамки вікна */
2,
rcClient.right - rcClient.left - 2,
rcClient.bottom - rcClient.top - 2,
true) ;
case WM_COMMAND:
/* обробляємо події вікна (натискання кнопок і т.д.) */
default:
/* запускаємо обробник за умовчанням (якщо
** не враховано повідомлення) */
return DefWindowProc (phwnd, message, wParam, lParam) ;
}
return 0 ;
}
void fill_table_with_data ()
{
/* структура, яка містить інформацію про рядок/комірку */
LVITEM lvI ;
/* заповняємо структуру */
lvI.mask = LVIF_TEXT ;
lvI.stateMask= 0 ;
lvI.state = 0 ;
/* заповняємо структуру для першого рядка першої комірки */
lvI.pszText = L"1" ;
lvI.iSubItem = 0 ;
lvI.iItem = 0 ;
/* створюємо перший рядок і першу комірку */
ListView_InsertItem(listview, &lvI) ;
/* заповняємо структуру для другої комірки першого рядка */
lvI.pszText = L"Data1" ;
lvI.iSubItem = 1 ;
ListView_SetItem(listview, &lvI) ;
/* заповняємо структуру для другого рядка першої комірки */
lvI.pszText = L"2" ;
lvI.iSubItem = 0 ;
lvI.iItem = 1 ;
/* створюємо другий рядок і першу комірку */
ListView_InsertItem(listview, &lvI) ;
/* заповняємо структуру для другої комірки другого рядка */
lvI.pszText = L"Data2" ;
lvI.iSubItem = 1 ;
ListView_SetItem(listview, &lvI) ;
}
Для “збирання” даної програми використаємо наступний файл Makefile (для порту компілятора GCC і Makefile):
main: main.cxx
g++ -c main.cxx -o main.obj -D UNICODE -D _UNICODE
g++ -mwindows main.obj -o main.exe -luuid -lComctl32 -lodbc32 -lComdlg32 -lgdi32
clean:
rm *.exe
rm *.obj
rm *.res
Після компілювання програми командою:
Ми можемо отримати результат: