КАК освоить Combo Box?

Прокручиваемые списки родились, как логическое продолжение обычных списков. Когда список становится безнадёжно длинным, его лучше спрятать в одну строку и вызывать только, когда он действительно нужен. В таких списках удобно хранить числа, длинные перечисления, даты, названия, списки языков и цветовых режимов. Широко используются прокручивемые списки в окнах настройки, где надо выбрать шрифт, его размер и толщину, в окнах выбора стандартных цветов, файловых менеджерах - для выбора текущего диска и много где ещё. Наверное, не мне говорить вам о пользе прокручиваемых списков.

У Combo Box есть одна особенность. Когда вы сделаете его активным, щёлкнув по нему один раз мышью, а потом нажмёте какую-нибудь клавишу на клавиатуре, то активным станет тот пункт, который начинается на введённую вами букву. Так как в списке может быть много значений, это позволяет быстро получать доступ к нужному пункту.

Также, вы наверное не знаете о том, что Combo Box бывает трёх видом. Первый - простой. Он не имеет кнопки со стрелкой. Первая его строка - обычное поле ввода, которое позволяет редактировать содержимое выбранного пункта. Стиль такого списка обозначается, как CBS_SIMPLE - простой Combo Box.

Второй тип - тоже позволяет редактировать содержимое выделенной строки, но справа от списка имеется кнопочка со стрелкой, нажав которую, вы увидите весь выпадающий список. Такой стиль называется CBS_DROPDOWN.

И наконец, самый первый по частоте использования список - без возможности редактирования содержимого. Он не имеет ничего общего с полями ввода, а являет собой нечто среднее между обычным списком, типа List Box и нередактируемой строкой. Его представляет стиль CBS_DROPDOWNLIST.

Также, вы можете разрешить сортировку списка, добавив стиль CBS_SORT, и позаботиться о том, чтобы всё строки были в нижнем регистре, дописав CBS_LOWERCASE (соответственно для верхнего регистра CBS_UPPERCASE). Если вы хотите, чтобы в списке отображалась полоса прокрутки (значений может быть столько, что они все не уместятся), то для этого существует стиль CBS_DISABLENOSCROLL. Для автоматической свёртки текста по горизонтали применяют стиль CBS_AUTOHSCROLL (действительно, строки могут быть длиннее, чем указанная в CreateWindow длина списка).

Лучший способ познакомиться с этими стилями поближе - это попробовать их все. Стили CBS_SIMPLE, CBS_DROPDOWN и CBS_DROPDOWNLIST конечно нужно применять по отдельности.

Несколько слов о примере.

В следующем примере мы закрепим работу с обычными списками, динамическими элементами управления, перерисовкой динамических окон и узнаем много нового о том, как применяются прокручиваемые списки.

Я предлагаю вам программу, где создаётся окно - календарь. В прокручиваемых списках будут числа месяца и годы: 2003, 2004, 2005. Названия месяцев мы поместим в обычный список. Нажимая кнопку: "Выбор", пользователь получит сообщение в удобоваримом виде. Например: "1-е мая 2005-го года". Для этого мы напишем свою функцию, которая будет преобразовывать выделенные значения в нормальный вид. В этой программе нам придётся много работать со строками - конвертировать их в числа и наоборот, складывать строки, копировать их. Поэтому, заодно вы узнаете как можно работать со строками в API, Принцип совсем другой - не такой, как в DOS!


Итак, по порядку!

Представьте, что нам надо написать функцию, которая преобразует три числа: день, месяц и год (13,6,2005) в приятную фразу: "13-е июня 2005-го года". Как вы уже догадались, для этого нам потребуется конструкция switch/case и знание строк. Вот как будет выглядеть тело такой функции:

//Функция возвращает выбранную дату
LPSTR ReturnDate(int day, int month, int year)
{
//Переменные-буферы для хранения строковых значений
char myDay[80], myYear[4];

//Преобразуем число day в строку myDay
_itoa(day+1, myDay, 10);

lstrcat(myDay, "-e "); //добавляем в конец строки -е

//Преобразуем число месяца в название месяца
//и добавляем в нашу строку
switch(month+1){
case 1: lstrcat(myDay, "Января "); break;
case 2: lstrcat(myDay, "Февраля "); break;
case 3: lstrcat(myDay, "Марта "); break;
case 4: lstrcat(myDay, "Апреля"); break;
case 5: lstrcat(myDay,"Мая "); break;
case 6: lstrcat(myDay,"Июня "); break;
case 7: lstrcat(myDay,"Июля "); break;
case 8: lstrcat(myDay,"Августа "); break;
case 9: lstrcat(myDay,"Сентября "); break;
case 10: lstrcat(myDay,"Октября "); break;
case 11: lstrcat(myDay,"Ноября "); break;
case 12: lstrcat(myDay,"Декабря "); break;
default: lstrcat(myDay,"Июня "); break;
}

//Конкатенируем с годом
lstrcat(myDay, _itoa(year, myYear, 10));
lstrcat(myDay, " г.");

//Возвращаем готовую дату
return myDay;
}

Поскольку функция возвращает строку, она имеет тип LPSTR, который эквивалентен типу char *. Функция формирует строку содержащую текст даты в зависимости от введённых чисел-аргументов. Для того, чтобы более детально понять её, мы должны знать...

КАК работать со строками в Win 32 API?

Функций для строк в API безумно много. И связано это в том числе и с тем, что существует несколько стандартов: ANSI, Unicide, ASCII... Функции конвертирования из одного формата в другой стоят на первом месте в справочниках в разделе "строки". Между тем, они гораздо менее насущны, по сравнению с теми, что мы разберём сейчас.

1. Длина строки.
Длину строки можно узнать с помощью _lstrlen(). Эта функция аналогичная strlen в DOS и используется там, где нужно указать число символов в строке. Например, в функции TextOut:

TextOut(hdc, x, y, Buf, _lstrlen(Buf));

2. Строку в число:
Если мы вводим в текстовое поле целое число, можно воспользоваться GetDlgItemInt - получение целого числа из поля ввода. Но если число дробное, проще получить сначала текст поля ввода функцией GetDlgItemText, а потом полученную строку превратить в вещественное число. Это позволяет сделать atof(char *), которая конвертирует строку в скобках в число типа double. (Для типа int она называется соответственно atoi).

double Val;
Val=atof(Buf);

c=sqrt(Val);


3. Число в строку.
Когда мы хотим выводить числовые значения в статический элемент (метку), используя SetDlgItemText, на помощь приходит _itoa(int, char *, int). Первый аргумент конвертируемое число, второе - строка текста, третий по умолчанию =10 - система счёта. Пример:

int a=10;
char Buf[2];
_itoa(a, Buf, 10);

....

SetDlgItemText(hWnd, IDC_STATIC, (LPSTR)Buf);

4. Поместить в строку значение.

Иногда вместо того, чтобы приравнивать две строки (что очень не любит Visual C++), можно скопировать значение одной строки в другую.

lstrcpy(Buf, ReturnDate(date,month,atoi(Buf1)));

В переменную Buf мы записываем строку, которую возвращает наша функция ReturnDate.

5. Сравнение строк.

Если вы решили сделать контроль доступа к вашей программе, то лучшей функции вам не найти.
int lstrcmp(LPSTR str1, LPSTR str2);

Возвраемое значение >0, если str1>str2, <0, если str1<str2 и равно 0, если строки равны. Напомню, что строки сравниваются посимвольно, и строка "аав" < "бав".

6. Конкатенция или сцепление строк.

Как я уже говорил, во многих языках программирования сцепление двух строк осуществляется знаком "+". Это очень удобно. В C++ нет такой операции, поэтому приходится вызывать специальную функцию. Она называется: lstrcat(); Синтаксис у неё такой:

LPSTR lstrcat(LPSTR str1, LPSTR str2);

Возвращает она сцеплённую строку, где в конец str1 добавлена str2.
Пример:

lstrcat(Buf,"Ещё одна строка");

Итак, снова задаёмся вопросом:

КАК работает Combo Box?

В этом примере у нас опять не будет ресурсов. Часто не требуется создавать главное окно приложения, из которого вызываются через меню диалоговые окна. Если наша программа выполняет какие-то небольшие конкретные действия, она может быть сразу диалоговым окном. Это используется чаще, поэтому в компиляторах Delphi и C++ Builder по умолчанию вам предлагается работать именно с диалоговым окном, из которого при желании всегда можно сделать главное окно с меню и панелью инструментов.

Итак, разместим динамически в окне элементы управления - ComboBox дней, ListBox месяцев, ComboBox лет. Статический текст я решил сделать не стандартного чёрного цвета, а цветной. Как, вы увидите ниже.

Итак, текст программы:

// Combo.cpp

#include<windows.h>
#include "combo.h"

//Создаём прототип функции окна
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//Функция формата даты
LPSTR ReturnDate(int day, int month, int year);

//объявляем имя программы
char szProgName[]="Имя программы";

HINSTANCE hInst;
HBRUSH hbrush; //создаём объект-кисть

//Идентификаторы окон-элементов управления
HWND hComboBox, hListBox, hComboYear, hExit, hTake;

char Buf1[80],Buf[80];

HDC hdc1,memdc; //создаём контекст устройства
PAINTSTRUCT ps; //структура рисования


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
HWND hWnd; //идентификатор главного окна
MSG lpMsg;
hInst=hInstance;

WNDCLASS w; //создаём экземпляр структуры WNDCLASS
//И начинаем её заполнять
w.lpszClassName=szProgName;
w.hInstance=hInstance;
w.lpfnWndProc=WndProc;
w.hCursor=LoadCursor(NULL, IDC_ARROW);
w.hIcon=LoadIcon(NULL, IDI_APPLICATION); //иконка
w.lpszMenuName=0;
w.hbrBackground=(HBRUSH)CreateSolidBrush(RGB(0,0,0)); //цвет фона окна
w.style=CS_HREDRAW|CS_VREDRAW; //стиль - перерисовываемое по х и по у
w.cbClsExtra=0;
w.cbWndExtra=0;

//Если не удалось зарегистрировать класс окна - выходим
if(!RegisterClass(&w))
return 0;

//Создадим окно в памяти, заполнив аргументы CreateWindow
hWnd=CreateWindow(szProgName,
"Демонстрация списков",
WS_OVERLAPPEDWINDOW,
100, //Координаты окна
100,
300,
530,
(HWND)NULL,
(HMENU)NULL,
hInstance,
(LPSTR)NULL);

//Выводим окно из памяти на экран
ShowWindow(hWnd, nCmdShow);
//Обновим содержимое окна
UpdateWindow(hWnd);

while(GetMessage(&lpMsg, NULL, 0, 0)) {
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}
return((int)lpMsg.wParam);
}

//Функция окна
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM lParam)
{

int i; //переменная цикла
int date, month, year; //переменные пользовательской даты
int maxX=0, maxY=0; //максимальные координаты по х и по у

char day[2]; //Число

HBITMAP hbit; //объект изображение окна

//Массив месяцев года
LPCSTR masMonth[12]={
"Январь","Февраль","Март","Апрель","Май","Июнь",
"Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"

};


//Цикл обработки сообщений
switch(messg)
{
case WM_CREATE:

hdc1=GetDC(hWnd); //получаем контекст изображения

maxX=GetSystemMetrics(SM_CXSCREEN); //узнаём максимальные координаты по х и по у
maxY=GetSystemMetrics(SM_CYSCREEN);


//Создаём окно-список чисел
hComboBox=CreateWindow("Combobox", NULL, WS_CHILD|WS_VISIBLE|WS_VSCROLL|CBS_DROPDOWNLIST|CBS_HASSTRINGS,
90,80,100,200, hWnd, (HMENU)ID_COMBODAY, hInst, NULL);

//Создаём окно-список месяцев.
hListBox=CreateWindow("Listbox", NULL,
WS_VISIBLE|WS_CHILD| WS_VSCROLL | WS_TABSTOP |WS_BORDER ,
90,140,100,200, hWnd, (HMENU)ID_LISTMONTH, hInst, NULL);

//Создаём список лет:
hComboYear=CreateWindow("Combobox",NULL, WS_CHILD|WS_VISIBLE|WS_VSCROLL|CBS_DROPDOWNLIST,
90,360,100,200, hWnd, (HMENU)ID_COMBOYEAR, hInst, NULL);

//Кнопка "Выбрать"
hTake=CreateWindow("button","Выбрать", WS_CHILD|WS_VISIBLE,
90,415,100,27, hWnd, (HMENU)ID_TAKE, hInst, NULL);

//Кнопка "Выход"
hExit=CreateWindow("button","Выход", WS_CHILD|WS_VISIBLE,
90,450,100,27, hWnd, (HMENU)ID_EXIT, hInst, NULL);


//Заполняем список дней значениями чисел от 1 до 31
for(i=1; i<32; i++)
{
//Преобразуем номер дня в строку
_itoa(i, day, 10);

//Запись строки в список
SendMessage(hComboBox, CB_ADDSTRING, 1, (LPARAM)day);
}

//Делаем текущей первую строку списка дней
SendMessage(hComboBox, CB_SETCURSEL, 0, 0L);

//Заносим значения месяцев в список
for(i=0; i<12; i++)
{
SendMessage(hListBox, LB_ADDSTRING, 1, (LPARAM)masMonth[i]);
}

//Делаем текущий строку "Июнь"
SendMessage(hListBox, LB_SETCURSEL, (WPARAM)5, 0L);

//Заносим значения лет:
SendMessage(hComboYear, CB_ADDSTRING, 1, (LPARAM)"2003");
SendMessage(hComboYear, CB_ADDSTRING, 1, (LPARAM)"2004");
SendMessage(hComboYear, CB_ADDSTRING, 1, (LPARAM)"2005");

//Делаем текущим первую строчку
SendMessage(hComboYear, CB_SETCURSEL, 0, 0L);

memdc=CreateCompatibleDC(hdc1); //создём в памяти контекст, совместимый с нашим
hbit=CreateCompatibleBitmap(hdc1, maxX, maxY); //создаём картинку нашего окна
SelectObject(memdc, hbit); //делаем её активной в области контекста памяти
hbrush=CreateSolidBrush(RGB(255,255,255)); //создаём кисть
SelectObject(memdc, hbrush); //делаем её активной
PatBlt(memdc, 0,0,maxX, maxY, PATCOPY); //копируем картинку из памяти в наше окно, растянув её на всё окно
ReleaseDC(hWnd, hdc1); //освобождаем контекст

break;


case WM_SIZE: //при изменении размеров окна, взывается
InvalidateRect(hWnd, NULL, 1); //сообщение WM_PAINT
break;

case WM_PAINT: //которое

hdc1=BeginPaint(hWnd, &ps);
//Заголовки над элементами управления
SetTextColor(hdc1, RGB(255,255,67));
SetBkMode(hdc1, TRANSPARENT);
TextOut(hdc1, 85,60,"Выберите число: ", 16);
TextOut(hdc1, 85,120,"Выберите месяц: ", 16);
TextOut(hdc1, 85,340,"Выберите год: ", 16);

//Перерисовка окна
memdc=CreateCompatibleDC(hdc1);
BitBlt(hdc1, 0, 0, maxX, maxY, memdc, 0, 0, SRCCOPY); //копирует в окно сохранённую картинку
EndPaint(hWnd, &ps);
break;

case WM_COMMAND:
switch(LOWORD(wParam)) {

//Нажатие кнопки выход
case ID_EXIT:
DestroyWindow(hWnd);
break;

//Кнопка "Выбрать"
case ID_TAKE:

//Получаем номер выделенной строки числа
date=SendMessage(hComboBox, CB_GETCURSEL,0, 0L);

//Получаем номер выделенной строки месяца
month=SendMessage(hListBox, LB_GETCURSEL, 0, 0L);

//Получаем номер выделенной строки года
year=SendMessage(hComboYear, CB_GETCURSEL, 0, 0L);
SendMessage(hComboYear, CB_GETLBTEXT, (WPARAM)year, (LPARAM)Buf1);

//Выводим на экран дату в удобоваримом формате
lstrcpy(Buf,ReturnDate(date, month,atoi(Buf1)));
MessageBox(NULL, Buf, "", MB_OK);
break;
}

break;

break;

case WM_DESTROY:
PostQuitMessage(0);
break;

default:
return(DefWindowProc(hWnd, messg, wParam, lParam));
}
return 0;
}

//Функция возвращает выбранную дату
LPSTR ReturnDate(int day, int month, int year)
{
//Переменные-буферы для хранения строковых значений
char myDay[80]="", myYear[8]="";

//Преобразуем число в строку
_itoa(day+1, myDay, 10);

//Добавляем окончание
lstrcat(myDay, "-e ");


//Преобразуем число в название месяца
switch(month+1){
case 1: lstrcat(myDay, "Января "); break;
case 2: lstrcat(myDay, "Февраля "); break;
case 3: lstrcat(myDay, "Марта "); break;
case 4: lstrcat(myDay, "Апреля"); break;
case 5: lstrcat(myDay,"Мая "); break;
case 6: lstrcat(myDay,"Июня "); break;
case 7: lstrcat(myDay,"Июля "); break;
case 8: lstrcat(myDay,"Августа "); break;
case 9: lstrcat(myDay,"Сентября "); break;
case 10: lstrcat(myDay,"Октября "); break;
case 11: lstrcat(myDay,"Ноября "); break;
case 12: lstrcat(myDay,"Декабря "); break;
default: lstrcat(myDay,"Июня "); break;
}


//Преобразуем год в строку
_itoa(year, myYear, 10);
lstrcat(myYear, " г.");

//Конкатенируем с годом
lstrcat(myDay, myYear);

return myDay;

}

//Файл заголовков Combo.h выглядит так:
// Combo.h
// Идентификаторы элементов управления

#define ID_BUTTON 1
#define ID_COMBODAY 2
#define ID_LISTMONTH 3
#define ID_COMBOYEAR 4
#define ID_TAKE 5
#define ID_EXIT 6

Итак, разбор полётов. Потому что пока мы познакомились с ComboBox только на практике. Итак, вот основные операции, которые мы сделали:

1. Создание элемента управления:
HWND hComboBox;


hComboBox=CreateWindow("Combobox", NULL, WS_CHILD|WS_VISIBLE|WS_VSCROLL|CBS_DROPDOWNLIST|CBS_HASSTRINGS,
90,80,100,200, hWnd, (HMENU)ID_COMBODAY, hInst, NULL);

Функция как функция. Стиль нашего списка CBS_DROPDOWNLIST.

2. Занесение данных:
for(i=1; i<32; i++)
{
//Преобразуем номер дня в строку
_itoa(i, day, 10);

//Запись строки в список
SendMessage(hComboBox, CB_ADDSTRING, 1, (LPARAM)day);
}

Переменная цикла считает от 1 до 31. Мы конвертируем эти значения в строки с помощью _itoa и заносим в список, посылая ему сообщение CB_ADDSTRING.

3. Сделать текущей строку:

После создания списка вид у него неприглядный. Он начинается пустой строкой. Чтобы сделать активной первую строку, вызовите функцию:
SendMessage(hComboBox, CB_SETCURSEL, 0, 0L);

Сообщение CB_SETCURSEL требует сделать текущей строку, указанную в wParam. В данном случае, 0-ую.


4. Получить из списка номер выделенной строки:

int date;

date=SendMessage(hComboBox, CB_GETCURSEL,0, 0L);

Сообщение CB_GETCURSEL возвращает номер выделенной строки. В wParam и lParam 0.


5. Получить из списка текст выделенной строки:
int year;
LPSTR Buf1[80];

SendMessage(hComboYear, CB_GETLBTEXT, (WPARAM)year, (LPARAM)Buf1);

Сообщение CB_GETLBTEXT (Get ListBox Text)
записывает в строку Buf1 текст строки с номером year. Для этого она даже не должна быть выделена.

6. Удалить строку из ComboBox. В нашем примере этого нет.

SendMessage(hComboYear, CB_DELETESTRING, (WPARAM)num, (LPARAM)0L);

Удаление строки с номером num.

7. Очистить список
SendMessage(hComboYear, CB_RESETCONTENT, 0, 0L);

8. Вставить строку по номеру.
В отличие от CB_ADDSTRING добавляет строку не в конец, а туда, куда мы укажем.

int num;
LPSTR Buf1[80];
SendMessage(hComboYear, CB_GETLBTEXT, (WPARAM)num, (LPARAM)Buf1);


Вот основные приёмы работы с прокручиваемым списком.

Назад Главная Вперёд

Сайт управляется системой uCoz