платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 9 - Дочерние окна

Предыдущая тема Следующая тема Перейти вниз

платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 9 - Дочерние окна

Сообщение  electrik в Пн Июн 29, 2009 5:20 pm

Win32 API. Урок 9. Дочерние окна

В этом туториале мы изучим дочерние элементы управления (child window controls), которые являются важными частями ввода и вывода нашей программы.

Теория:

Windows предоставляет несколько предопределенных классов окон, которые мы можем сразу же использовать в своих программах. Как правило, мы будем использовать их как компоненты dialog box'ов, поэтому они носят название дочерних элементов управления. Эти элементы обрабатывают сообщения от клавиатуры и мыши и уведомляют родительское окно, если их состояние изменяется. Они снимают с программистов огромный груз, поэтому вам следует использовать их так часто, как это возможно.
В этом туториале, я положу их на обычное окно, только для того, чтобы продемонстрировать как их можно создать и использовать, но в реальности вам лучше класть их на dialog box.

Примерами предопределенных классов окон являются кнопки, списки, checkbox'ы, радиокнопки и т.д.

Чтобы использовать дочернее окно, вы должны создать его с помощью функции CreateWindow или createWindowEx. Заметьте, что вы не должны регистрировать класс окна, так как он уже был зарегистрирован Windows. Имя класса окна должно быть именем предопределенного класса. Скажем, если вы хотите создать кнопку, вы должны указать "button" в качестве имени класса в CreateWindowEx. Другие параметры, которые вы должны указать - это хэндл родительского окна и ID контрола. ID контрола должно быть уникальным. Вы используете его для того, чтобы отличать данный контрол от других.

После того, как контрол был создан, он посылает сообщение, уведомляющее родительское окно об изменении своего состояния. Обычно вы создаете дочернее окно во время обработки сообщения WM_CREATE главного окна. Дочернее окно посылает сообщение WM_COMMAND родительскому окну со своим ID в нижнем слове WParam'а, код уведомления в верхнем слове wParam'а, а ее хэндл в lParam'е. Каждое окно имеет разные коды уведомления, сверьтесь с вашим справочником по Win32 API, чтобы получить подробную информацию.

родительское окно также может посылать команды дочерним окнам, вызывая функцию SendMessage. Функция SendMessage посылает определенные сообщения с сопутствующими значениями в wParam и lParam окну, чей хэндл передается функции. Это очень полезная функция, так как она может посылать сообщения любому окну, хэндл которого у вас есть.

Поэтому, после создания дочерних окон, родительское окно должно обрабатывать сообщения WM_COMMAND, чтобы быть способным получать коды уведомления от дочерних окон.

Пример:

Мы создадим окно, которое содержит edit-контрол и pushbutton. Когда вы нажмете на кнопку, появится окно, отображающее текст, введенный в edit box'е. Также имеется меню с 4 пунктами:

1. Say Hello - ввести текстовую строку в edit box
2. Clear Edit Box - очистить содержимое edit box'а
3. Get Text - отобразить окно с текстом в edit box'е
4. Exit - закрыть программу
файл child.bas

Код:

'дочерние окна
#include "windows.bi"
#include "child.bi"
declare function WinMain ( byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
  byval szCmdLine as LPSTR, _
  byval iCmdShow as integer ) as integer

' начало программы
dim shared hModule as HINSTANCE ' хэндл программы
dim shared hwndButton as HWND ' хэндл кнопки
dim shared hwndEdit as HWND ' хэндл editboxа
dim shared as zString * 512 buffer 'буффер для editboxа
hModule =  GetModuleHandle( NULL ) ' получим хэндл программы
end WinMain(hModule,NULL,GetCommandLine() , SW_SHOWNORMAL ) ' вызвать основную функцию
' здесь заканчивается программа

' процедура окна
function  WndProc _
(byval hwnd as HWND, _ ' хэндл окна
byval uMsg as UINT, _ ' сообщение
byval wParam as WPARAM, _ ' дополнительный  параметр сообщений
byval lParam as LPARAM) as LRESULT ' дополнительный параметр сообщений

function = 0
select case uMsg  'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
case WM_CREATE ' если получили сообщение WM_CREATE

' создадим editBox
hwndEdit = CreateWindowEx _
(WS_EX_CLIENTEDGE, _ ' дополнительные стили
"edit", _ ' класс окна
NULL, _ 'заголовок окна
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL, _ ' стили окна
50, _ ' X
35, _ ' Y
200, _ ' ширина окна
25, _ ' высота окна
hWnd, _ ' хэндл родительского окна
cptr(HMENU,EditID), _ ' идентификатор
hModule, _ ' хэндл модуля(программы)
NULL)  ' указатель на структуру данных
 SetFocus(hwndEdit) ' установим фокус на editbox

'создадим кнопку
hwndButton = CreateWindowEx _
(NULL, _ ' дополнительные стили
"button", _ ' класс окна
"My First Button", _ 'заголовок окна
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, _ ' стили окна
75, _ ' X
70, _ ' Y
140, _ ' ширина окна
25, _ ' высота окна
hWnd, _ ' хэндл родительского окна
cptr(HMENU,ButtonID), _ ' идентификатор
hModule, _ ' хэндл модуля
NULL) ' указатель на структуру данных

case WM_COMMAND ' если получили сообщение WM_COMMAND
if lParam = 0 then ' если lParam = 0, значит идет сообщение от меню
select case loword(wParam)  ' начинаем обрабатывать элементы меню
case IDM_HELLO

' установим текст в editbox
SetWindowText _
(hwndEdit, _ ' хэндл editboxа
"Wow! I'm in an edit box now") ' строка которая отобразится в editboxе
case IDM_CLEAR
    
    ' очистим editbox
    SetWindowText _
    (hwndEdit, _ ' хэндл editboxа
    NULL) ' пустая строка
    case IDM_GETTEXT
        
        ' прочитаем текст из editboxа в наш буфер
        GetWindowText _
(hwndEdit, _ ' хэндл editboxа
@buffer, _ ' указатель на буфер
512) ' сколько надо прочитать байт
        
        'выведем нашу писанину в MessageBoxе
        MessageBox(NULL, @buffer,"Test EditBox", MB_OK)
    SetFocus(hwndEdit) ' установим фокус на editbox
    case IDM_EXIT
        DestroyWindow(hWnd) ' разрушим окно и выдем из программы
        end select
    end if
    select case loword(wParam) ' продолжаем проверять идентификаторы
case ButtonID ' если сообщение нашей кнопки
if hiword(wparam) = BN_CLICKED then ' если ее кликнули

' пошлем сообщение, как будто мы выбрали в менюшке get text
SendMessage _
(hWnd, _ ' хэндл окна
WM_COMMAND, _ ' тип сообщения
IDM_GETTEXT, _ ' ид меню
0)
end if
end select
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function


'функция WinMain
function WinMain _
(byval hInst as HINSTANCE, _ ' хэндл программы
byval hPrevInst as HINSTANCE, _ 'в win32 всегда 0
byval szCmdLine as LPSTR, _  'указатель на командную строку
byval iCmdShow as integer ) as integer  ' состояние окна при первом появлении

dim wc as WNDCLASSEX ' структура параметров окна
dim wMsg as MSG  ' структура сообщений
dim hWnd as HWND ' хэндл окна

 'структура класса окна wc
with wc ' заполняем структуру wc
.cbSize = SIZEOF( WNDCLASSEX )  ' размер структуры WNDCLASSEX
.style = CS_HREDRAW or CS_VREDRAW  ' Стиль окна
.lpfnWndProc = @WndProc ' Адрес процедуры окна WndProc
.cbClsExtra = NULL  ' резервирование  дополнительных байт за концом структуры
.cbWndExtra = NULL
.hInstance = hInst  ' хэндл модуля
.hbrBackground = cast(HGDIOBJ, COLOR_BTNFACE+1) ' Цвет фона
.lpszMenuName = @"FirstMenu" ' хэндл меню
.lpszClassName = @ClassName ' имя класса окна
.hIcon = LoadIcon( NULL,IDI_APPLICATION ) ' Хэндл иконки
.hIconSm = .hIcon 'Хэндл маленькой иконки
.hCursor = LoadCursor( NULL,IDC_ARROW) ' Хэндл курсора
end with

' регистрация нашего класса окна
if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if

' Создадим окно
hwnd = CreateWindowEx _
(WS_EX_CLIENTEDGE, _ ' дополнительные стили
ClassName, _ ' строка с именем класса окна
AppName, _ ' строка с именем окна
WS_OVERLAPPEDWINDOW, _ ' стиль окна
CW_USEDEFAULT, _ ' X
CW_USEDEFAULT, _ ' Y
300, _ ' ширина окна
200, _ ' высота окна
NULL, _ ' хэндл родительского окна
NULL, _ ' хэндл меню
hInst, _ ' хэндл модуля
NULL) ' указатель на структуру данных

ShowWindow( hwnd,iCmdShow) ' отобразить наше окно на десктопе
UpdateWindow( hwnd) ' обновить клиентскую область

while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )  'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend
function = wMsg.wParam
end function

файл child.bi
Код:

#define ClassName "SimpleWinClass"
#define AppName "Our First Window"
#define ButtonID  1
#define EditID  2
#define IDM_HELLO  32000
#define IDM_CLEAR  32001
#define IDM_GETTEXT  32002
#define IDM_EXIT  32003

файл child.rc
Код:

#include "child.bi"
FirstMenu MENU
{
POPUP "&MyMenu"
{
MENUITEM "&Hello",IDM_HELLO
MENUITEM "&Clear", IDM_CLEAR  
MENUITEM "&Get text", IDM_GETTEXT  
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
}

Анализ:

Давайте проанализируем программу.

case WM_CREATE

hwndEdit = CreateWindowEx _
(WS_EX_CLIENTEDGE, _
"edit", _
NULL, _
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL,
50, _
35, _
200, _
25, _
hWnd, _
cptr(HMENU,EditID), _
hModule, _
NULL)
SetFocus(hwndEdit)
hwndButton = CreateWindowEx _
(NULL, _
"button", _
"My First Button",
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, _
75, _
70, _
140, _
25, _
hWnd, _
cptr(HMENU,ButtonID), _
hModule, _
NULL)

Мы создаем контролы во время обработки сообщения WM_CREATE. Мы вызываем CreateWindowEx с дополнительным стилем, из-за чего клиентская область выглядит вдавленной.
Имя каждого контрола предопределенно - "edit" для edit-контрола, "button" для кнопки. Затем мы указываем стили дочерних окон. У каждого контрола есть дополнительные стили, кроме обычных стилей окна. Например, стили кнопок начинаются с "BS_", стили edit'а - с "ES_". Вы должны посмотреть информацию об этих стилях в вашем справочнике по Win32 API. Заметьте, что вместо хэндла меню вы передаете ID контрола. Это не вызывает никаких противоречий, поскольку дочерний элемент управления не может иметь меню. После создания каждого контрола, мы сохраняем его хэндл в соответствующей переменной для будущего использования.

SetFocus вызывается для того, чтобы направить фокус ввода на edit box, чтобы пользователь мог сразу начать вводить в него текст.

case WM_COMMAND ' если получили сообщение WM_COMMAND
if lParam = 0 then

Обратите внимание, что меню тоже шлем сообщение WM_COMMAND, чтобы уведомить окно о своем состоянии. Как мы можем провести различие между сообщениями WM_COMMAND, исходящими от меню и контролов? Вот ответ:

Нижнее слово wParam
Верхнее слово wParam
lParam
ID меню
0
0
ID контрола
Код уведомления
Хэндл дочернего окна

Вы можете видеть, что вы должны проверить lParam. Если он равен нулю, текущее сообщение WM_COMMAND было послано меню. Вы не можете использовать wParam, чтобы различать меню и контрол, так как ID меню и ID контрола могут быть идентичными и код уведомления должен быть равен нулю.

case IDM_HELLO
SetWindowText _
(hwndEdit, _
"Wow! I'm in an edit box now")
case IDM_CLEAR
   SetWindowText _
   (hwndEdit, _
   NULL)
   case IDM_GETTEXT
              GetWindowText _
(hwndEdit, _
@buffer, _
512)
       MessageBox(NULL, @buffer,"Test EditBox", MB_OK)
   SetFocus(hwndEdit)

Вы можете поместить текстовую строку в edit box с помощью вызова SetWindowText. Вы очищаете содержимое edit box'а с помощью вызова SetWindowText, передавая ей NULL. SetWindowText - это функция общего назначения. Вы можете использовать ее, чтобы изменить заголовок окна или текст на кнопке. Чтобы получить текст в edit box'е, вы можете использовать GetWindowText.

select case loword(wParam)
case ButtonID
if hiword(wparam) = BN_CLICKED then
SendMessage _
(hWnd, _
WM_COMMAND, _
IDM_GETTEXT, _
0)

Приведенный выше кусок кода является обработкой нажатия на кнопку. Сначала он проверяет нижнее слово wParam'а, чтобы убедиться, что ID контрола принадлежит кнопке. Если это так, он проверяет верхнее слово wParam'а, чтобы убедиться, что был послан код уведомления BN_CLICKED, то есть кнопка была нажата.

После этого идет собственно обработка нажатия на клавиш. Мы хотим получить текст из edit box'а и отобразить его в message box'е. Мы можем продублировать
код в секции IDM_GETTEXT выше, но это не имеет смысла. Если мы сможем каким-либо образом послать сообщение WM_COMMAND с нижним словом wparam, содержащим значение IDM_GETTEXT нашей процедуре окна, то избежим дублирования кода и упростим программу. Функция SendMessage - это ответ. Эта функция посылает любое сообщение любому окну с любым wparam'ом и lparam'ом, которые нам понадобятся. Поэтому вместо дублирования кода мы вызываем SendMessage с хэндлом родительского окна, WM_COMMAND, IDM_GETTEXT и 0. Это дает тот же эффект, что и выбор пункта меню "Get Text". Процедура окна не почувствует никакой разницы.

Вы должны использовать эту технику так часто, насколько возможно, чтобы сделать ваш код более упорядоченным.

И напоследок. Не забудьте функцию TranslateMessage в очереди сообщений. Так как вам нужно печатать текст в edit box'е, ваша программа должна транслировать ввод в читабельный текст. Если вы пропустите эту функцию, вы не сможете напечатать что-либо в вашем edit box'е.

[C] Iczelion, пер. Aquila.

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 37
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Предыдущая тема Следующая тема Вернуться к началу

- Похожие темы

 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения