платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 22 - Суперклассинг
Страница 1 из 1
платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 22 - Суперклассинг
Win32 API. Урок 22. Суперклассинг
В этом туториале мы изучим суперклассинг, что это такое и для чего он служит. Вы также узнаете, как реализовать навигацию с помощью клавиши 'Tab' в вашем окне.
Теория:
Во время вашей программной карьеры, вы наверняка встретитесь с ситуацией, когда вам потребуется несколько контролов с *несколько* отличным поведением. Например, вам могут потребоваться 10 edit control'ов, которые принимают только число. Есть несколько путей достигнуть цели:
• Создать собственный класс и написать контролы с нуля
• Создать эти edit control'ы и сабклассировать каждый из них
• Суперклассировать edit control
Первый метод слишком сложен. Вам придется с нуля воплощать всю функциональность edit control'ов. Слишком трудоемкая задача, чтобы ее можно было быстро выполнить.
Второй метод лучше, чем первый, но, тем не менее, также требует немало работы. Все нормально, пока вам надо сабклассировать несколько контролов, но сабклассинг дюжины или еще большего количества контролов может превратиться в аде. Суперклассинг - это техника, которой вы должны владеть.
Суперклассинг - это метод, с помощью которого вы сможете взять контроль над определенным классом окна. Под взятием контроля я подразумеваю, что вы сможете изменить свойства класса, так чтобы они соответствовали вашим целям, после чего вы можете создать сколько угодно таких контролов.
Ниже приведены шаги для суперклассинга:
• вызвать функцию GetClassInfoEx, чтобы получить информацию о классе окна, который вы хотите суперклассировать. GetClassInfoEx требует указатель на структуру WNDCLASSEX, которая будет заполнена информацией, если вызов пройдет успешно.
• Изменить требуемые параметры WNDCLASSEX. Тем не менее, есть два члена, которые вы должны обязательно изменить:
• hInstance - Вы должны поместить в это поле хэндл программы.
• lpszClassName - вы должны поместить сюда указатель на новое имя класса.
• Вы не обязаны изменять параметр lpfnWndProc, но обычно вам будет это нужно делать. Главное не забудьте сохранить старое значение lpfnWndproc, если вам надо будет его вызывать с помощью CallWindowproc.
• Зарегистрировать измененную структуру WNDCLASSEX. У вас будет новый класс окна, который будет обладать некоторыми характеристиками старого класса.
• Создать окна с помощью нового класса.
Суперклассинг лучше, чем сабклассинг, если вы хотите создать много контролов с одинаковыми характеристиками.
Пример:
В этом туториале мы изучим суперклассинг, что это такое и для чего он служит. Вы также узнаете, как реализовать навигацию с помощью клавиши 'Tab' в вашем окне.
Теория:
Во время вашей программной карьеры, вы наверняка встретитесь с ситуацией, когда вам потребуется несколько контролов с *несколько* отличным поведением. Например, вам могут потребоваться 10 edit control'ов, которые принимают только число. Есть несколько путей достигнуть цели:
• Создать собственный класс и написать контролы с нуля
• Создать эти edit control'ы и сабклассировать каждый из них
• Суперклассировать edit control
Первый метод слишком сложен. Вам придется с нуля воплощать всю функциональность edit control'ов. Слишком трудоемкая задача, чтобы ее можно было быстро выполнить.
Второй метод лучше, чем первый, но, тем не менее, также требует немало работы. Все нормально, пока вам надо сабклассировать несколько контролов, но сабклассинг дюжины или еще большего количества контролов может превратиться в аде. Суперклассинг - это техника, которой вы должны владеть.
Суперклассинг - это метод, с помощью которого вы сможете взять контроль над определенным классом окна. Под взятием контроля я подразумеваю, что вы сможете изменить свойства класса, так чтобы они соответствовали вашим целям, после чего вы можете создать сколько угодно таких контролов.
Ниже приведены шаги для суперклассинга:
• вызвать функцию GetClassInfoEx, чтобы получить информацию о классе окна, который вы хотите суперклассировать. GetClassInfoEx требует указатель на структуру WNDCLASSEX, которая будет заполнена информацией, если вызов пройдет успешно.
• Изменить требуемые параметры WNDCLASSEX. Тем не менее, есть два члена, которые вы должны обязательно изменить:
• hInstance - Вы должны поместить в это поле хэндл программы.
• lpszClassName - вы должны поместить сюда указатель на новое имя класса.
• Вы не обязаны изменять параметр lpfnWndProc, но обычно вам будет это нужно делать. Главное не забудьте сохранить старое значение lpfnWndproc, если вам надо будет его вызывать с помощью CallWindowproc.
• Зарегистрировать измененную структуру WNDCLASSEX. У вас будет новый класс окна, который будет обладать некоторыми характеристиками старого класса.
• Создать окна с помощью нового класса.
Суперклассинг лучше, чем сабклассинг, если вы хотите создать много контролов с одинаковыми характеристиками.
Пример:
- Код:
' Суперклассинг
#include "windows.bi"
declare function EditWndProc (byval hEdit as HWND, byval uMsg as UINT, _
byval wParam as WPARAM, byval lParam as LPARAM) as LRESULT
declare function WinMain ( byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
byval szCmdLine as LPSTR, _
byval iCmdShow as integer ) as integer
' начало программы
#define ClassName "SuperclassWinClass" ' Имя нашего класса окна
#define AppName "Superclassing Demo" ' Имя нашего окна
#define EditClass "EDIT" ' класс edit box'а
#define OurClass "SUPEREDITCLASS" ' класс суперкласс edit
#define Message "You pressed Enter in the text box!"
#define WM_SUPERCLASS WM_USER+5
dim shared hModule as HINSTANCE ' Хэндл нашей программы
dim shared hwndEdit(5) as HWND ' хэндлы superClass edit control'а
dim shared OldWndProc as LONG ' адрес оригинальной процедуры
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 ' дополнительный параметр сообщений
dim wc as WNDCLASSEX ' структура WNDCLASSEX
dim y as integer ' координата y
dim countWin as integer ' счетчик окон
function = 0
select case uMsg 'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
case WM_CREATE ' если сообщение WM_CREATE
wc.cbSize = sizeof(WNDCLASSEX) ' размер структуры WNDCLASSEX
GetClassInfoEx _ ' заполним структуру
(NULL, _
EditClass, _ ' класс editbox'а
@wc) ' указатель на структуру WNDCLASSEX
OldWndproc = cPtr(LONG ,wc.lpfnWndproc)
wc.lpfnWndproc = @EditWndproc ' указатель на процедуру EditWndproc
wc.hInstance = hModule ' хэндл программы
wc.lpszClassName = @OurClass ' класс edit box
RegisterClassEx _ ' регистрируем класс
(@wc) ' структура WNDCLASSEX
y = 20
for countWin = 0 to 5 ' создаем 6 superClass edit box'ов
hwndEdit(countWin) = CreateWindowEx _ ' создаем Edit Box'ы
(WS_EX_CLIENTEDGE, _ ' дополнительные стили
OurClass, _ ' класс edit box'а
0, _
WS_CHILD+WS_VISIBLE+WS_BORDER, _ ' стили окна
20, _ ' x
y, _ ' y
300, _ ' ширина
25, _ ' высота
hWnd, _ ' хэндл родительского окна
cptr(HMENU,countWin), _
hModule, _ ' хэндл программы
NULL)
y = y + 25 ' добавим к координате y 25
next
SetFocus(hwndEdit(0)) ' установим фокус в Edit Box
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function
function EditWndProc _ ' процедура edit box'а
(byval hEdit as HWND, _ ' хэндл Edit control'а
byval uMsg as UINT, _ ' сообщение
byval wParam as WPARAM, _ ' дополнительный параметр сообщений
byval lParam as LPARAM) as LRESULT ' дополнительный параметр сообщений
dim focus as HWND ' хэндл фокуса
function = 0
dim ch as WPARAM = lobyte(wparam)
select case uMsg ' проверяем сообщения
case WM_CHAR ' если сообщение WM_CHAR
if (ch >= 48 and _ ' "0"
ch <= 57) or _ ' "9"
(ch >= 65 and _ ' "A"
ch <= 70) or _ ' "F"
(ch >= 97 and _ ' "a"
ch <= 102) or _ ' "f"
ch = VK_BACK then
if (ch >= 97 and _ ' "a"
ch <= 102) then ' "f"
ch =ch - &h20
end if
*cast(ubyte ptr,@wparam)=ch
function = CallWindowproc _ ' запускаем оригинальную процедуру окна
(cPtr(WNDPROC,OldWndproc), _ ' адрес оригинальной процедуры
hEdit, _ ' хэндл edit box
uMsg, _
wparam, _ ' символ
lparam)
exit function
end if
case WM_KEYDOWN ' сообщение о нажатии клавиши
select case lobyte(wparam)
case VK_RETURN ' если нажали enter
MessageBox(hEdit,Message,AppName,MB_OK+MB_ICONINFORMATION)
SetFocus(hEdit) ' установим фокус в edit box
case VK_TAB ' если нажата клавиша tab
if GetKeyState(VK_SHIFT) and &h80000000 then ' если нажата клавиша shift
focus = GetWindow _ ' получим фокус
(hEdit, _ ' хэндл superClass edit box'а
GW_HWNDPREV) ' предыдущего окна
if focus = NULL then ' если нет предыдущего
focus = GetWindow _ ' получим фокус
(hEdit, _ ' хэндл superClass edit box'а
GW_HWNDLAST) ' последнее окно
end if
else
focus = GetWindow _ ' получить фокус
(hEdit, _ ' хэндл superClass edit box'а
GW_HWNDNEXT) ' хэндл следующего superClass edit box
if focus = NULL then ' если нет следующего
focus = GetWindow _ ' получить фокус
(hEdit, _ ' хэндл superClass edit box'а
GW_HWNDFIRST) ' первое окно
end if
end if
SetFocus(focus) ' установим фокус
exit function
end select
function = CallWindowproc _ ' запускаем оригинальную процедуру окна
(cPtr(WNDPROC,OldWndproc), _ ' адрес оригинальной процедуры
hEdit, _ ' хэндл edit box
uMsg, _
wparam, _
lparam)
exit function
case else
function = CallWindowproc _ ' запускаем оригинальную процедуру окна
(cPtr(WNDPROC,OldWndproc), _ ' адрес оригинальной процедуры
hEdit, _ ' хэндл edit box
uMsg, _
wparam, _
lparam)
end select
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
' заполняем структуру wc
wc.cbSize = SIZEOF( WNDCLASSEX ) ' размер структуры WNDCLASSEX
wc.style = CS_HREDRAW or CS_VREDRAW ' Стиль окна
wc.lpfnWndProc = @WndProc ' Адрес процедуры окна WndProc
wc.cbClsExtra = NULL ' резервирование дополнительных байт за концом структуры
wc.cbWndExtra = NULL
wc.hInstance = hInst ' хэндл модуля
wc.hbrBackground = cast(HGDIOBJ, COLOR_ApPWORKSPACE) ' Цвет фона
wc.lpszMenuName = NULL ' Хэндл меню
wc.lpszClassName = @ClassName ' класс окна
wc.hIcon = LoadIcon( NULL,IDI_APPLICATION ) ' Хэндл иконки
wc.hIconSm = wc.hIcon 'Хэндл маленькой иконки
wc.hCursor = LoadCursor( NULL,IDC_ARROW) ' Хэндл курсора
' регистрация нашего класса окна
if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if
' Создадим окно
hwnd = CreateWindowEx _
(WS_EX_CLIENTEDGE, _ ' дополнительные стили
@ClassName, _ ' указатель на строку с именем класса окна
@AppName, _ ' указатель на строку с именем окна
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE, _ ' стиль окна
CW_USEDEFAULT, _ ' X
CW_USEDEFAULT, _ ' Y
350, _ ' ширина окна
220, _ ' высота окна
NULL, _ ' хэндл родительского окна
NULL, _ ' хэндл меню
hInst, _ ' хэндл модуля
NULL) ' указатель на структуру данных
while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE ) 'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend
function = wMsg.wParam
end function
Последний раз редактировалось: electrik (Вт Май 23, 2017 5:29 pm), всего редактировалось 1 раз(а)
electrik- Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 43
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург
Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 22 - Суперклассинг
Win32 API. Урок 22. Суперклассинг - продолжение
Анализ:
Программа создаст простое окно с "измененными" edit control'ами в своей клиентской области. Edit control'ы будут принимать только шестнадцатиричные числа.
Фактически, я адаптировал пример с сабклассингом. программа стартует как обычно, а самое интересное происходит, когда создается основное окно:
case WM_CREATE
wc.cbSize = sizeof(WNDCLASSEX)
GetClassInfoEx _
(NULL, _
EditClass, _
@wc)
Сначала мы заполним данными класса, который мы хотим суперклассировать, в нашем случае это класс edit'а. Помните, что вы должны установить параметр структуры WNDCLASSEX, перед тем, как вызвать GetClassInfoEx, в противном случае она будет заполнена неверно. После вызова GetClassInfoEx у нас будет иметься вся
необходимая для создания нового класса информация.
OldWndproc = cPtr(LONG ,wc.lpfnWndproc)
wc.lpfnWndproc = @EditWndproc
wc.hInstance = hModule
wc.lpszClassName = @OurClass
Теперь мы можем изменить некоторые члены wc. Первый из них - это указатель на процедуру окна. Так как нам нужно будет соединить вызовы новой и старой процедуры в цепь, нам необходимо сохранить старое значение в переменную, чтобы потом воспользоваться функцией CallWindowproc. Эта техника идентична с сабклассингом, не считая того, что вы напрямую изменяете структуру WNDCLASSEX не вызывая SetWindowLong. Следующие два поля должны быть изменены, иначе вам не удастся
зарегистрировать ваш новый класс окна, hInstance и lpszClassName. Вы должны заменить старое значение hInstance на хэндл вашей програмы, а также выбрать имя для нового класса.
RegisterClassEx _
(@wc)
Когда все готово, регистрируйте новый класс. Вы получите новый класс, обладающий некоторыми характеристиками старого.
y = 20
for countWin = 0 to 5
hwndEdit(countWin) = CreateWindowEx _
(WS_EX_CLIENTEDGE, _
OurClass, _
0, _
WS_CHILD+WS_VISIBLE+WS_BORDER, _
20, _
y, _
300, _
25, _
hWnd, _
cptr(HMENU,countWin), _
hModule, _
NULL)
y = y + 25
next
SetFocus(hwndEdit(0))
Теперь, когда мы зарегистрировали класс, мы можем создать основанные на нем окна. в вышеприведенном куске кода, я использовал countWin в качестве счетчика созданных
окон. y, используется как y-координата левого верхнего угла окна. Когда окно создано, его хэндл сохраняется в массиве хэндлов. Когда все окна созданы, устанавливаем фокус на первое окно. К этому моменту у вас есть 6 edit control'ов, которые принимают только шестнадцатиричные числа. Новая процедура окна,
заменившая старую, выполняет роль фильтра. Фактически, это работает точно также, как и в примере с сабклассингом, только вам не нужно выполнять лишнюю работу.
Я вставил кусок кода, который обрабатывает нажатия на Tab, чтобы сделать пример более полезным для вас. Обычно, если вы помещаете контролы на диалоговое окно, его внутренний менеджер сам обрабатывает нажатия на клавиши навигации. Увы, но это недоступно, когда вы помещаете контролы на обычное окно. Вам следует сабклассировать их, чтобы нажатия на Tab обрабатывались. В нашем примере нам нет нужды сабклассировать контролы по одному, так как мы уже суперклассировали, поэтому можем реализовать "центральный менеджер навигации контролов".
case VK_TAB
if GetKeyState(VK_SHIFT) and &h80000000 then
focus = GetWindow _
(hEdit, _
GW_HWNDPREV)
if focus = NULL then
focus = GetWindow _
(hEdit, _
GW_HWNDLAST)
end if
else
focus = GetWindow _
(hEdit, _
GW_HWNDNEXT)
if focus = NULL then
focus = GetWindow _
(hEdit, _
GW_HWNDFIRST)
end if
end if
SetFocus(focus)
exit function
Вышеприведенный код взят из процедуры EditWndClass. Он проверяет, нажал ли пользователь клавишу tab, если да, он вызывает GetKeyState, чтобы узнать, нажата ли также клавиша Shift. GetKeyState возвращает значение , которое определяет, нажата ли указанная клавиша или нет. Если клавиша нажата, верхний бит будет установлен. Если нет, он будет очищен. Поэтому мы проверяем полученное значение &H80000000. Если верхний бит установлен, это будет означать, что пользователь нажал shift и tab одновременно, и должны обработать это отдельно.
Если пользователь нажал клавишу Tab, мы вызываем GetWindow, чтобы получить хэндл следующего контрола. Мы используем флаг GW_HWNDNEXT, чтобы указать GetWindow получить хэндл следующего окна относительно текущего hEdit. Если эта функция возвращает NULL, то такого окна нет и мы устанавливаем фокус на первое окно,
вызвав GetWindow с флагом GW_HWNDFIRST. Shift-Tab работает так же, как и обычно нажатие на Tab, только передвигает фокус окна назад.
[C] Iczelion, пер. Aquila.
Анализ:
Программа создаст простое окно с "измененными" edit control'ами в своей клиентской области. Edit control'ы будут принимать только шестнадцатиричные числа.
Фактически, я адаптировал пример с сабклассингом. программа стартует как обычно, а самое интересное происходит, когда создается основное окно:
case WM_CREATE
wc.cbSize = sizeof(WNDCLASSEX)
GetClassInfoEx _
(NULL, _
EditClass, _
@wc)
Сначала мы заполним данными класса, который мы хотим суперклассировать, в нашем случае это класс edit'а. Помните, что вы должны установить параметр структуры WNDCLASSEX, перед тем, как вызвать GetClassInfoEx, в противном случае она будет заполнена неверно. После вызова GetClassInfoEx у нас будет иметься вся
необходимая для создания нового класса информация.
OldWndproc = cPtr(LONG ,wc.lpfnWndproc)
wc.lpfnWndproc = @EditWndproc
wc.hInstance = hModule
wc.lpszClassName = @OurClass
Теперь мы можем изменить некоторые члены wc. Первый из них - это указатель на процедуру окна. Так как нам нужно будет соединить вызовы новой и старой процедуры в цепь, нам необходимо сохранить старое значение в переменную, чтобы потом воспользоваться функцией CallWindowproc. Эта техника идентична с сабклассингом, не считая того, что вы напрямую изменяете структуру WNDCLASSEX не вызывая SetWindowLong. Следующие два поля должны быть изменены, иначе вам не удастся
зарегистрировать ваш новый класс окна, hInstance и lpszClassName. Вы должны заменить старое значение hInstance на хэндл вашей програмы, а также выбрать имя для нового класса.
RegisterClassEx _
(@wc)
Когда все готово, регистрируйте новый класс. Вы получите новый класс, обладающий некоторыми характеристиками старого.
y = 20
for countWin = 0 to 5
hwndEdit(countWin) = CreateWindowEx _
(WS_EX_CLIENTEDGE, _
OurClass, _
0, _
WS_CHILD+WS_VISIBLE+WS_BORDER, _
20, _
y, _
300, _
25, _
hWnd, _
cptr(HMENU,countWin), _
hModule, _
NULL)
y = y + 25
next
SetFocus(hwndEdit(0))
Теперь, когда мы зарегистрировали класс, мы можем создать основанные на нем окна. в вышеприведенном куске кода, я использовал countWin в качестве счетчика созданных
окон. y, используется как y-координата левого верхнего угла окна. Когда окно создано, его хэндл сохраняется в массиве хэндлов. Когда все окна созданы, устанавливаем фокус на первое окно. К этому моменту у вас есть 6 edit control'ов, которые принимают только шестнадцатиричные числа. Новая процедура окна,
заменившая старую, выполняет роль фильтра. Фактически, это работает точно также, как и в примере с сабклассингом, только вам не нужно выполнять лишнюю работу.
Я вставил кусок кода, который обрабатывает нажатия на Tab, чтобы сделать пример более полезным для вас. Обычно, если вы помещаете контролы на диалоговое окно, его внутренний менеджер сам обрабатывает нажатия на клавиши навигации. Увы, но это недоступно, когда вы помещаете контролы на обычное окно. Вам следует сабклассировать их, чтобы нажатия на Tab обрабатывались. В нашем примере нам нет нужды сабклассировать контролы по одному, так как мы уже суперклассировали, поэтому можем реализовать "центральный менеджер навигации контролов".
case VK_TAB
if GetKeyState(VK_SHIFT) and &h80000000 then
focus = GetWindow _
(hEdit, _
GW_HWNDPREV)
if focus = NULL then
focus = GetWindow _
(hEdit, _
GW_HWNDLAST)
end if
else
focus = GetWindow _
(hEdit, _
GW_HWNDNEXT)
if focus = NULL then
focus = GetWindow _
(hEdit, _
GW_HWNDFIRST)
end if
end if
SetFocus(focus)
exit function
Вышеприведенный код взят из процедуры EditWndClass. Он проверяет, нажал ли пользователь клавишу tab, если да, он вызывает GetKeyState, чтобы узнать, нажата ли также клавиша Shift. GetKeyState возвращает значение , которое определяет, нажата ли указанная клавиша или нет. Если клавиша нажата, верхний бит будет установлен. Если нет, он будет очищен. Поэтому мы проверяем полученное значение &H80000000. Если верхний бит установлен, это будет означать, что пользователь нажал shift и tab одновременно, и должны обработать это отдельно.
Если пользователь нажал клавишу Tab, мы вызываем GetWindow, чтобы получить хэндл следующего контрола. Мы используем флаг GW_HWNDNEXT, чтобы указать GetWindow получить хэндл следующего окна относительно текущего hEdit. Если эта функция возвращает NULL, то такого окна нет и мы устанавливаем фокус на первое окно,
вызвав GetWindow с флагом GW_HWNDFIRST. Shift-Tab работает так же, как и обычно нажатие на Tab, только передвигает фокус окна назад.
[C] Iczelion, пер. Aquila.
electrik- Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 43
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург
Страница 1 из 1
Права доступа к этому форуму:
Вы не можете отвечать на сообщения