API и FreeBasic. (дочерние окна-ComboBox)

ComboBox (комбинированный список) - используется очень часто. Чтобы увидеть его, достаточно открыть любую папку проводника. Контрол (вверху), в котором располагается адрес этой папки и есть ComboBox. Данный контрол не зря называют комбинированным, ведь он состоит из нескольких контролов (Button,Listbox,Edit). Многие сообщения посылаемые контролу имеют схожесть с сообщениями для ListBox:

у listbox - LB

у ComboBox - CB

Так например:

LB_RESETCONTENT  и  CB_RESETCONTENT

LB_DIR  и  CB_DIR

Так же как и у ListBox есть сортировка и поиск строк. Данный контрол можно создать указанием стиля WC_COMBOBOX или WC_COMBOBOXEX. Последний дает возможность устанавливать иконки в пункты , а так же использовать некоторые дополнительные сообщения, но для него нет возможности использовать сообщения для поиска и заполнения файлами и папками с помощью сообщения CB_DIR. При написании этой статьи я решил выложить 3 примера. Но об этом ниже...
Основные стили, используемые для контрола ComboBox (расширенные стили для ComboBoxEx можно посмотреть ЗДЕСЬ):

  • CBS_AUTOHSCROLL - Автоматически прокручивает текст в поле редактирования текста вправо, когда пользователь вводит с клавиатуры символ в конце строки. Если этот стиль не установлен, принимается только текст, который помещается внутри прямоугольной границы поля.
  • CBS_DISABLENOSCROLL - В окне со списком показывает вертикальную линейку прокрутки заблокированной, когда поле окна содержит не достаточно элементов для прокрутки. Без этого стиля, линейка прокрутки скрыта, если окно со списком содержит не достаточно элементов.
  • CBS_DROPDOWN - Подобен CBS_SIMPLE, за исключением того, что окно со списком не отображается, пока пользователь не выберет значок рядом с полем редактирования текста.
  • CBS_DROPDOWNLIST - Подобен CBS_DROPDOWN, за исключением того, что поле редактирования текста заменено статическим текстовым элементом, который отображает текущий выбор в окне со списком.
  • CBS_HASSTRINGS - Определяет, что представляемое владельцем комбинированное окно содержит элементы, состоящие из строк. Комбинированное окно поддерживает память и адрес для строк, так что прикладная программа может использовать сообщение CB_GETLBTEXT, чтобы восстановить текст для отдельного элемента.
  • CBS_LOWERCASE - Преобразовывает в нижний регистр любые символы верхнего регистра, введенные в поле редактирования текста комбинированного окна.
  • CBS_NOINTEGRALHEIGHT - Определяет, что размер комбинированного окна - это точный размер, определенный прикладной программой, когда она создала комбинированное окно. Обычно, Windows устанавливает размеры комбинированного окна так, чтобы оно не отображало элементы частично.
  • CBS_OEMCONVERT - Преобразует текст, введенный в поле редактирования текста комбинированного окна. Текст преобразуется из набора символов Windows в набор символов OEM, а затем обратно в набор Windows. Это гарантирует соответствующее символьное преобразование, когда прикладная программа вызывает функцию CharToOem, чтобы преобразовать строку Windows в комбинированном окне в символы OEM. Этот стиль наиболее полезен для комбинированных окон, которые содержат имена файлов и применяются только в комбинированных окнах, созданных со стилем CBS_SIMPLE или CBS_DROPDOWN.
  • CBS_OWNERDRAWFIXED - Определяет, что владелец окна со списком ответственен за прорисовку его содержания и что элементы в окне со списком все равной высоты. Окно владельца принимает сообщение WM_MEASUREITEM, когда комбинированное окно создано, а сообщение WM_DRAWITEM, когда внешний вид комбинированного окна изменился.
  • CBS_OWNERDRAWVARIABLE - Определяет, что владелец окна со списком ответственен за прорисовку его содержания и что элементы в окне со списком являются переменными по высоте. Окно владельца принимает сообщение WM_MEASUREITEM для каждого элемента комбинированного окна, когда Вы создаете комбинированное окно; окно владельца принимает сообщение WM_DRAWITEM тогда, когда изменился внешний вид комбинированного окна.
  • CBS_SIMPLE - Всегда отображать окно со списком. Текущий выбор в окне со списком отображается в поле редактирования текста.
  • CBS_SORT - Автоматически сортирует строки, введенные в окно со списком.
  • CBS_UPPERCASE - Преобразовывает любые символы нижнего регистра в символы верхнего регистра, введенные в поле редактирования текста комбинированного окна.

Часто используемые сообщения посылаемые контролу (все можно посмотреть ЗДЕСЬ):

  • CB_ADDSTRING - Добавление строки в список.
  • CB_DELETESTRING - Удаление строки из списка.
  • CB_DIR - Заполнение списка именами файлов и каталогов, расположенных в текущем каталоге, а также именами дисков.
  • CB_FINDSTRING - Поиск строки в списке, имеющей заданный префикс.
  • CB_GETCOUNT - Определение количества строк в списке.
  • CB_GETCURSEL - Определение номера выделенной строки.
  • CB_GETDROPPEDCONTROLRECT - Определение экранных координат видимой части списка. Используется в Windows версии 3.1 и более поздних версий.
  • CB_GETDROPPEDSTATE - С помощью этого сообщения можно определить, находится список в видимом или невидимом состоянии.  
  • CB_GETEDITSEL - Определение положения первого и последнего символа в выделенном фрагменте текста.
  • CB_GETEXTENDUI - С помощью этого сообщения можно определить, использует ли список расширенный интерфейс пользователя. Это сообщение используется в Windows версии 3.1 и более поздних версий.При использовании расширенного интерфейса щелчок в окне редактора текста для стиля CBS_DROPDOWMLIST приводит к отображению списка. Список также отображается, когда пользователь нажимает клавишу перемещения курсора вниз <Down>. Если список находится в невидимом состоянии, свертка окна редактирования не выполняется.
  • СB_GETITEMDATA - Получение 32-битового значения, соответствующего заданной строке.
  • СB_GETITEMHEIGHT - Определение высоты заданной строки в списке, который рисуется родительским окном и имеет переменную высоту элементов. Это сообщение используется в Windows версии 3.1 и более поздних версий.
  • CB_GETLBTEXT - Копирование текста, соответствующего заданной строке, в буфер.
  • CB_GETLBTEXTLEN - Определение длины строки, содержащейся в списке.
  • CB_INSERTSTRING - Вставка элемента в заданную позицию списка. На расположение строки не влияет стиль LBS_SORT.
  • CB_LIMITTEXT - Определение максимального количества символов, которое можно ввести в окно редактирования.
  • CB_RESETCONTENT - Удаление всех строк из списка. 
  • CB_SELECTSTRING - Поиск строки в списке, которая начинается с символов, соответствующих образцу. Найденная строка становится выбранной.
  • CB_SETCURSEL - Выбор указанной строки. Ранее выделенная строка становится невыделенной. Если данная строка находится вне окна отображения, список сворачивается таким образом, чтобы строка стала видимой.
  • CB_SETEDITSEL - Выделение заданных символов в окне редактирования.
  • CB_SETEXTENDEDUI - Установка режима использования расширенного интерфейса пользователя. Это сообщение используется в Windows версии 3.1 и более поздних версий.
  • CB_SETITEMDATA - Установка значения двойного слова, связанного с указанным элементом списка.
  • CB_SETITEMHEIGHT - Установка высоты элемента в списке, который рисует родительское окно и имеет переменную высоту элементов. Это сообщение используется в Windows версии 3.1 и более поздних версий.
  • CB_SHOWDROPDOWN - Переключение списка в отображаемое или неотображаемое состояние.

И так первый пример:

#INCLUDE "windows.bi"
#INCLUDE "win/commctrl.bi"
InitCommonControls()
Dim msg As MSG
Dim As WNDCLASSEX wc
Dim As String NameClass="MyClass"
Dim As HINSTANCE Hinst=GetModuleHandle(0)

Function wndproc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer

    Static As HWND but,combobox

    Select Case msg
        Case WM_CREATE
            Combobox=CreateWindowEx( 0,_
            WC_COMBOBOX,_
            "ComboBox",_
            WS_VISIBLE Or WS_CHILD Or CBS_DROPDOWN Or WS_VSCROLL Or WS_TABSTOP,_
            10,10,200,200,hwnd,Cast(HMENU,1),0,0)
            SendMessage(Combobox,CB_ADDSTRING,0,Cast(LPARAM,@"HH"))
            SendMessage(Combobox,CB_ADDSTRING,0,Cast(LPARAM,@"TT"))
            but=CreateWindowEx(0,"Button","FIND TT",_
            WS_VISIBLE Or WS_CHILD,45,70,130,20,hwnd,Cast(HMENU,2),0,0)
        Case WM_COMMAND
            If Loword(WPARAM)=2 Then
                Var i=SendMessage(combobox,CB_FINDSTRING,-1,Cast(LPARAM,@"TT"))
                SendMessage(combobox,CB_SETCURSEL,i,0)
            Endif
        Case WM_DESTROY
            PostQuitMessage(0)
    End Select
    Return DefWindowProc(hwnd,msg,wparam,lparam)
End Function

With wc
    .cbSize=SizeOf(WNDCLASSEX)
    .style=CS_HREDRAW Or CS_VREDRAW
    .lpfnWndProc=@wndproc
    .hInstance=Hinst
    .hIcon=LoadIcon(0,IDI_QUESTION)
    .hCursor=LoadCursor(0,IDC_ARROW)
    .hbrBackground=Cast(HBRUSH,COLOR_WINDOWFRAME)
    .lpszClassName=StrPtr(NameClass)
    .hIconSm=.hIcon
End With

If RegisterClassEx(@wc)=0 Then
    Print "Register error, press any key"
    Sleep
    End
Endif

CreateWindowEx(0,NameClass,"ComboBox",_
WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,250,150,0,0,Hinst,0)

While GetMessage(@msg,0,0,0)
    TranslateMessage(@msg)
    DispatchMessage(@msg)
Wend

В примере выше создается ComboBox в сообщении WM_CREATE и заполняется двумя строками. Так же создается кнопка, при нажатии на которую будет производится поиск строки "TT". При нажатии на кнопку в сообщении WM_COMMAND мы посылаем контролу сообщение CB_FINDSTRING и передаем в параметре WPARAM константу -1 , которая укажет системе , что поиск нужно производить с начала списка. В параметре LPARAM передаем искомую строку.

Для второго примера (ComboBox с иконками) придется создать список изображений с помощью функции ImageList_Create , ее параметры:

  • cx - ширина изображения
  • cy - высота изображения
  • flags - задает глубину цвета
    • ILC_COLOR - по умолчанию
    • ILC_COLOR4 - 4-битный DIB(16 цветов)
    • ILC_COLOR8 - 8-битный DIB
    • ILC_COLOR16 - 16-битный DIB(32/64k цвета)
    • ILC_COLOR24 - 24-битный DIB
    • ILC_COLOR32 - 32-битный DIB
    • ILC_COLORDDB - Использование зависит от изображения
    • ILC_MASK - использует маску
  • cInitial -  начальное кол-во изображений в листе
  • cGrow - кол-во на которое лист будет увеличиваться под новые изображения

После создания списка, его можно сразу, а можно после заполнения иконками, прикрепить к контролу с помощью сообщения CBEM_SETIMAGELIST , при этом в параметре Lparam нужно передать хендл списка.
Изображения в список добавляются с помощью функции ImageList_Add, ее параметры:

  • HIMAGELIST - хендл списка
  • HBITMAP - хендл битмапа
  • HBITMAP - хендл битмапа, содержащий маску, может быть 0

Если же нужно заполнять иконками, то для этого есть функция ImageList_AddIcon, ее параметры:

  • HIMAGELIST - хендл листа
  • HICON - хендл иконки

Список (IMAGELIST) создает копии изображений и сама система следит за освобождением ресурсов, занятых изображениями. Поэтому после того, как вы добавите изображение в список, если вам хендлы изображения больше не требуются, их следует освободить с помощью DeleteObject. После того как список изображений заполнен, можно использовать его индексы для пунктов ComboBoxEx. При этом для добавления пунктов нужно заполнить структуру COMBOBOXEXITEM и после послать сообщение CBEM_INSERTITEM контролу с адресом этой структуры в параметре LPARAM.
Сама структура COMBOBOXEXITEM выглядит так:

  • mask - маска, активирует поля структуры
    • CBEIF_DI_SETITEM - для сохранения информации в структуре NMCOMBOBOXEX
    • CBEIF_IMAGE - использование изображений
    • CBEIF_INDENT - использование отступов
    • CBEIF_LPARAM - использование специфичных значений
    • CBEIF_OVERLAY - использование наложений
    • CBEIF_SELECTEDIMAGE- использование изображений выбранного пункта
    • CBEIF_TEXT - использование строк для пунктов
  • iItem - индекс пункта для вставки (если -1 , то в конец)
  • pszText - указатель на вставляемую строку для пункта
  • cchTextMax - длина строки , помещенной в pszText
  • iImage - индекс изображения
  • iSelectedImage - индекс изображения выбранного пункта
  • iOverlay - номер индекса для наложения изображений
  • iIndent - отступ для пунктов
  • lParam - специфичные значения для пункта

Пример с иконками:

#INCLUDE "windows.bi"
#INCLUDE "win/commctrl.bi"
InitCommonControls()
Dim msg As MSG
Dim As WNDCLASSEX wc
Dim As String NameClass="MyClass"
Dim As HINSTANCE Hinst=GetModuleHandle(0)

Function wndproc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer

    Static As HWND but,combobox

    Select Case msg
        Case WM_CREATE
            Dim hImageList As HIMAGELIST
            Dim CBITEM As COMBOBOXEXITEM
            Dim As HICON icon1,icon2
            Dim As Integer i1,i2
            Combobox=CreateWindowEx( 0,_
            WC_COMBOBOXEX,_
            "ComboBoxEX",_
            WS_VISIBLE Or WS_CHILD Or CBS_DROPDOWN Or WS_VSCROLL,_
            10,10,200,200,hwnd,Cast(HMENU,1),0,0)
            hImageList =  ImageList_Create(16,16,ILC_COLOR32,1,1)
            SendMessage(Combobox,CBEM_SETIMAGELIST,0,Cast(LPARAM, hImageList))
            icon1 = LoadIcon(0,IDI_HAND)
            icon2 = LoadIcon(0,IDI_QUESTION)
            i1=ImageList_AddIcon(hImageList,icon1)
            i2=ImageList_AddIcon(hImageList,icon2)
            CBITEM.pszText        = Strptr("1 строка")
            CBITEM.iItem          = -1
            CBITEM.iImage         = i1
            CBITEM.iSelectedImage = i1
            CBITEM.cchTextMax     = Len("1 строка")
            CBITEM.mask           = CBEIF_TEXT Or CBEIF_IMAGE Or CBEIF_SELECTEDIMAGE
            SendMessage(Combobox,CBEM_INSERTITEM, 0,Cast(LPARAM,@CBITEM))
            CBITEM.pszText        = Strptr("2 строка")
            CBITEM.iItem          = -1
            CBITEM.iImage         = i2
            CBITEM.iSelectedImage = i2
            CBITEM.cchTextMax     = Len("2 строка")
            CBITEM.mask           = CBEIF_TEXT Or CBEIF_IMAGE Or CBEIF_SELECTEDIMAGE
            SendMessage(Combobox,CBEM_INSERTITEM, 0,Cast(LPARAM,@CBITEM))
            DeleteObject(icon1):    DeleteObject(icon2)
        Case WM_DESTROY
            PostQuitMessage(0)
    End Select
    Return DefWindowProc(hwnd,msg,wparam,lparam)
End Function

With wc
    .cbSize=SizeOf(WNDCLASSEX)
    .style=CS_HREDRAW Or CS_VREDRAW
    .lpfnWndProc=@wndproc
    .hInstance=Hinst
    .hIcon=LoadIcon(0,IDI_QUESTION)
    .hCursor=LoadCursor(0,IDC_ARROW)
    .hbrBackground=Cast(HBRUSH,COLOR_WINDOWFRAME)
    .lpszClassName=StrPtr(NameClass)
    .hIconSm=.hIcon
End With

If RegisterClassEx(@wc)=0 Then
    Print "Register error, press any key"
    Sleep
    End
Endif

CreateWindowEx(0,NameClass,"ComboBoxEX",_
WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,250,150,0,0,Hinst,0)

While GetMessage(@msg,0,0,0)
    TranslateMessage(@msg)
    DispatchMessage(@msg)
Wend


И последний пример редактируемого combobox. Сказать по чести у меня возникли некоторые трудности по реализации этого примера на системе Windows 7. Хотя на Windows XP все работало правильно. Я не мог понять в чем дело, и тут мне помог SARG, спасибо ему за это.

И так о том что реализовано в примере:

  • Создаем ComboBox
  • Заполняем его двумя строчками с помощью сообщения CB_ADDSTRING
  • получаем хендл редактора ComboBox с помощью ChildWindowFromPoint
  • Сабклассируем хендл редактора
  • В процедуре редактора отлавливаем сообщения WM_KEYDOWN (для получения сигнала нажатия клавиши ENTER) и WM_CHAR(чтобы контрол никак не реагировал на нажатие клавиши ENTER, в противном случае будет неприятный звук)
  • При получении сигнала нажатия клавиши отправляем оригинальное (выдуманное) сообщение (WM_ENTER) главному окну.
  • При получении этого сообщения извлекаем текст из редактора и заменяем его в пункте ComboBox (как и в случае с LISTBOX создаем новый пункт и удаляем старый)
  • Кроме того при выделении соответствующего Item в ComboBox, мы в сообщении WM_COMMAND сохраняем номер этого пункта и посылаем сообщение CB_SETEDITSEL для выделения текста

Пример редактируемого ComboBox:

#INCLUDE "windows.bi"
#INCLUDE "win/commctrl.bi"
#DEFINE wm_enter 55555
InitCommonControls()
Dim msg As MSG
Dim As WNDCLASSEX wc
Dim As String NameClass="MyClass"
Dim As HINSTANCE Hinst=GetModuleHandle(0)
Dim Shared As HWND hwndM
Dim Shared As wndproc oldproc

Function subclass(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
    If msg=WM_KEYDOWN Then
        If wparam=VK_RETURN Then
            SendMessage(hwndM, WM_ENTER, 0, 0)
            Return 0
        Endif
    Elseif msg=WM_CHAR Then
        If wparam= VK_RETURN Then
            Return 0
    Endif
    Endif
    Return CallWindowProc( oldproc,hwnd,msg,wparam,lparam)
End Function

Function wndproc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
    Static As HWND edit,combobox
    Static i As Integer
    Select Case msg
        Case WM_CREATE
            Combobox=CreateWindowEx( 0,_
            WC_COMBOBOX,_
            "ComboBox",_
            WS_VISIBLE Or WS_CHILD Or CBS_DROPDOWN Or WS_VSCROLL Or WS_TABSTOP,_
            10,10,200,200,hwnd,Cast(HMENU,1),0,0)
            SendMessage(Combobox,CB_ADDSTRING,0,Cast(LPARAM,@"HH"))
            SendMessage(Combobox,CB_ADDSTRING,0,Cast(LPARAM,@"TT"))
            Dim pt As Point
            pt.x = 3
            pt.y = 3
            Edit = ChildWindowFromPoint(Combobox, pt)
            oldproc=Cast(wndproc,SetWindowLong(Edit,GWL_WNDPROC,Cast(Long, @subclass)))
        Case WM_COMMAND
            If Loword(WPARAM)=1 Then
                Var index=SendMessage(Combobox,CB_GETCURSEL ,0 ,0)
                If index<>-1 Then
                    i=index
                    If Hiword(WPARAM)=LBN_SELCHANGE Then
                        SendMessage(Combobox,CB_SETEDITSEL,0,makelparam(1,-1))
                    Endif
                Endif
            Endif
        Case WM_ENTER
            Dim As ZString*32 st
            GetWindowText(edit,@st,32)
            SendMessage(combobox,CB_INSERTSTRING ,i ,Cast(LPARAM,@st))
            SendMessage(combobox,CB_DELETESTRING ,i+1 ,0)
        Case WM_DESTROY
            PostQuitMessage(0)
    End Select
    Return DefWindowProc(hwnd,msg,wparam,lparam)
End Function

With wc
    .cbSize=SizeOf(WNDCLASSEX)
    .style=CS_HREDRAW Or CS_VREDRAW
    .lpfnWndProc=@wndproc
    .hInstance=Hinst
    .hIcon=LoadIcon(0,IDI_QUESTION)
    .hCursor=LoadCursor(0,IDC_ARROW)
    .hbrBackground=Cast(HBRUSH,COLOR_WINDOWFRAME)
    .lpszClassName=StrPtr(NameClass)
    .hIconSm=.hIcon
End With

If RegisterClassEx(@wc)=0 Then
    Print "Register error, press any key"
    Sleep
    End
Endif

hwndM=CreateWindowEx(0,NameClass,"ComboBox",_
WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,250,300,0,0,Hinst,0)

While GetMessage(@msg,0,0,0)
    TranslateMessage(@msg)
    DispatchMessage(@msg)
Wend

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

содержание | назад | вперед