На этом шаге мы рассмотрим работу со списками.
Данный шаг посвящен диалоговому окну с двумя списками. Двойным щелчком по элементу левого списка заполняется правый список. При этом мы учитываем возможность повторного щелчка по одному и тому же элементу. В принципе, в программе нет ничего сложного. Ниже будет дан комментарий к ней. Но нам хотелось бы немного поговорить о таком элементе, как список, остановившись на некоторых важных моментах.
Средства управления списком можно разделить на сообщения и свойства. Свойства задаются в файле ресурсов. Например, свойство LBS_SORT приводит к тому, что содержимое списка будет автоматически сортироваться при добавлении туда элемента. Очень важным является свойство LBS_WANTKEYBOARDINPUT. При наличии такого свойства приложение получает сообщение WM_VKEYTOITEM, которое посылается приложению, когда нажимается какая-либо клавиша при наличии фокуса на данном списке. Вы можете выбрать самостоятельную обработку - клавиша PgUp или оставить стандартную обработку. В том случае, если стандартная обработка не нужна, следует возвратить из функции диалогового окна отрицательное значение.
//Файл diallst.rc. //Определение констант. //Стили окна. #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x00010000L //Элементы на окне должны быть изначально видимы. #define WS_VISIBLE 0x10000000L //При помощи TAB можно по очереди активизировать элементы. #define WS_TABSTOP 0x00010000L #define WS_VSCROLL 0x00200000L #define WS_THICKFRAME 0x00040000L #define LBS_NOTIFY 0x0001L #define LBS_SORT 0x0002L #define LBS_WANTKEYBOARDINPUT 0x0400L //Идентификаторы. #define LIST1 101 #define LIST2 102 #define IDI_ICON1 3 //Определили пиктограмму. IDI_ICON1 ICON "help.ico" //Определение диалогового окна. DIAL1 DIALOG 0, 0, 210, 110 STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX CAPTION "Пример диалогового окна" FONT 8, "Arial" { CONTROL "ListBox1", LIST1, "listbox", WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | WS_THICKFRAME | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT, 16, 16, 70, 75 CONTROL "ListBox2", LIST2, "listbox", WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | WS_THICKFRAME | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT, 116, 16, 70, 75 }
;Константы. ;Сообщение приходит при закрытии окна. WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_SETICON equ 80h WM_COMMAND equ 111h WM_VKEYTOITEM equ 2Eh LB_ADDSTRING equ 180h LBN_DBLCLK equ 2 LB_GETCURSEL equ 188h LB_GETTEXT equ 189h LB_FINDSTRING equ 18Fh VK_INSERT equ 2Dh ;Прототипы внешних процедур. EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN SendMessageA@16:NEAR EXTERN SendDlgItemMessageA@20:NEAR EXTERN MessageBoxA@16:NEAR ;Структуры. ;Структура сообщения. MSGSTRUCT STRUC MSHWND DD ? ;Идентификатор окна, получающего сообщение. MSMESSAGE DD ? ;Идентификатор сообщения. MSWPARAM DD ? ;Доп. информация о сообщении. MSLPARAM DD ? ;Доп. информация о сообщении. MSTIME DD ? ;Время посылки сообщения. MSPT DD ? ;Положение курсора во время посылки сообщения. MSGSTRUCT ENDS
.386P ;Плоская модель. .MODEL FLAT, STDCALL include pr43_1.asm ;Директивы компоновщику для подключения библиотек. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' MSG MSGSTRUCT <?> HINST DD 0 ;Дескриптор приложения. PA DB 'DIAL1',0 BUFER DB 100 DUP(0) STR1 DB 'Первый',0 STR2 DB "Второй",0 STR3 DB "Третий",0 STR4 DB "Четвертый",0 STR5 DB "Пятый",0 STR6 DB "Шестой",0 STR7 DB "Седьмой",0 STR8 DB "Восьмой",0 STR9 DB "Девятый",0 STR10 DB "Десятый",0 STR11 DB "Одиннадцатый",0 STR12 DB "Двенадцатый",0 STR13 DB "Тринадцатый",0 STR14 DB "Четырнадцатый",0 STR15 DB "Пятнадцатый",0 INDEX DD OFFSET STR1 DD OFFSET STR2 DD OFFSET STR3 DD OFFSET STR4 DD OFFSET STR5 DD OFFSET STR6 DD OFFSET STR7 DD OFFSET STR8 DD OFFSET STR9 DD OFFSET STR10 DD OFFSET STR11 DD OFFSET STR12 DD OFFSET STR13 DD OFFSET STR14 DD OFFSET STR15 _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ;Получить дескриптор приложения. PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ;------------------------------------- PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX,-1 JNE KOL ;Здесь можно разместить сообщение об ошибке. KOL: ;------------------------------------- PUSH 0 CALL ExitProcess@4 ;------------------------------------- ;------------------------------------- ;Процедура окна. ;Расположение параметров в стеке: ;[ЕВР+014Н] LPARAM ;[ЕВР+10Н] WAPARAM ;[ЕВР+0СН] MES ;[ЕВР+8] HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI ;------------------------------------- CMP DWORD PTR [EBP+0CH],WM_CLOSE JNE L1 PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH],WM_INITDIALOG JNE L2 ;Загрузить пиктограмму. PUSH 3 ;Идентификатор пиктограммы. PUSH [HINST] ;Идентификатор процесса. CALL LoadIconA@8 ;Установить пиктограмму. PUSH EAX PUSH 0 ;Тип пиктограммы (маленькая). PUSH WM_SETICON PUSH DWORD PTR [EBP+08H] CALL SendMessageA@16 ;Заполнить левый список. MOV ECX,15 MOV ESI,0 LO1: PUSH ECX ;Сохранить параметр цикла. PUSH INDEX[ESI] PUSH 0 PUSH LB_ADDSTRING PUSH 101 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA@20 ADD ESI,4 POP ECX LOOP LO1 JMP FINISH L2: CMP DWORD PTR [EBP+0CH],WM_COMMAND JNE L3 ;Не сообщение ли от левого списка? CMP WORD PTR [EBP+10H],101 JNE FINISH ;Не было ли двойного щелчка? CMP WORD PTR [EBP+12H], LBN_DBLCLK JNE FINISH ;Был двойной щелчок, теперь определим элемент. ;Получить индекс выбранного элемента. L4: PUSH 0 PUSH 0 PUSH LB_GETCURSEL PUSH 101 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA@20 ;Скопировать элемент списка в буфер. PUSH OFFSET BUFER PUSH EAX ;Индекс записи. PUSH LB_GETTEXT PUSH 101 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA@20 ;Определить, нет ли элемента в правом списке. PUSH OFFSET BUFER PUSH -1 ;Искать во всем списке. PUSH LB_FINDSTRING PUSH 102 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA@20 CMP EAX,-1 JNE FINISH ;Элемент нашли. ;Не нашли, можно добавлять. PUSH OFFSET BUFER PUSH 0 PUSH LB_ADDSTRING PUSH 102 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA@20 MOV EAX,-1 JMP FIN L3: ;Здесь проверка, не нажата ли клавиша. CMP DWORD PTR [EBP+0CH],WM_VKEYTOITEM JNE FINISH CMP WORD PTR [EBP+10H],VK_INSERT JE L4 MOV EAX,-1 JMP FIN FINISH: MOV EAX,0 FIN: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END START
Результат работы приложения можнео увидеть на рисунке 1:
Рис.1. Результат работы приложения
Прокомментируем приведенную программу.
В первую очередь обратите внимание на функцию SendDlgItemMessage. Для посылки сообщения элементам диалогового окна эта функция более удобна, чем SendMessage, так как элемент в ней идентифицируется не дескриптором (который еще надо узнать), а номером, определенным в файле ресурсов.
Взглянув на файл ресурсов, вы увидите, что второму (правому) списку присвоено свойство LBS_SORT. Если такое свойство присвоено списку, то при добавлении в него элемента (сообщение LB_ADDSTRING) этот элемент помещается в список так, что список остается упорядоченным. Свойство LBS_SORT стоит системе Windows довольно большой работы. Посредством сообщения WM_COMPAREITEM она определяет нужное положение нового элемента в списке, а затем вставляет его при помощи сообщения LB_INSERTSTRING.
Хотелось бы также обратить внимание на цикл заполнения левого списка. Нам приходится хранить регистр ЕСХ в стеке. Вы скажете, что это обычное дело при организации цикла с использованием команды LOOP. Отметим, что это совсем не очевидно. К сожалению, в документации по функциям API и сообщениям не указывается, какие регистры микропроцессора сохраняются, а какие - нет. Все это придется устанавливать экспериментально. Что и было сделано в данном примере.
Сообщение WM_VKEYTOITEM приходит при нажатии какой-либо клавиши, при наличии фокуса на списке. При этом список должен иметь свойство LBS_WANTKEYBOARDINPUT. Именно потому, что данное свойство установлено только у левого списка, у нас нет необходимости проверять, от какого списка пришло сообщение.
Со следующего шага мы начнем рассматривать работу с файлами.