- •Содержание
- •Глава 1 README.TXT
- •Вызов, брошенный программистам
- •Основные правила
- •Краткая история Windows
- •Краткая история этой книги
- •Начнем
- •Глава 2 Hello, Windows 95
- •Отличительная особенность Windows
- •Графический интерфейс пользователя
- •Концепции и обоснование GUI
- •Содержимое интерфейса пользователя
- •Преимущество многозадачности
- •Управление памятью
- •Независимость графического интерфейса от оборудования
- •Соглашения операционной системы Windows
- •Вызовы функций
- •Объектно-ориентированное программирование
- •Архитектура, управляемая событиями
- •Оконная процедура
- •Ваша первая программа для Windows
- •Что в этой программе неправильно?
- •Файлы HELLOWIN
- •Make-файл
- •Вызовы функций Windows
- •Идентификаторы, написанные прописными буквами
- •Новые типы данных
- •Описатели
- •Венгерская нотация
- •Точка входа программы
- •Регистрация класса окна
- •Создание окна
- •Отображение окна
- •Цикл обработки сообщений
- •Оконная процедура
- •Обработка сообщений
- •Воспроизведение звукового файла
- •Сообщение WM_PAINT
- •Сообщение WM_DESTROY
- •Сложности программирования для Windows
- •Не вызывай меня, я вызову тебя
- •Синхронные и асинхронные сообщения
- •Думайте о ближнем
- •Кривая обучения
- •Глава 3 Рисование текста
- •Рисование и обновление
- •Сообщение WM_PAINT
- •Действительные и недействительные прямоугольники
- •Введение в графический интерфейс устройства (GDI)
- •Контекст устройства
- •Структура информации о рисовании
- •Получение описателя контекста устройства. Второй метод
- •Функция TextOut. Подробности
- •Системный шрифт
- •Размер символа
- •Метрические параметры текста. Подробности
- •Форматирование текста
- •Соединим все вместе
- •Не хватает места!
- •Размер рабочей области
- •Полосы прокрутки
- •Диапазон и положение полос прокрутки
- •Сообщения полос прокрутки
- •Прокрутка в программе SYSMETS
- •Структурирование вашей программы для рисования
- •Создание улучшенной прокрутки
- •Мне не нравится пользоваться мышью
- •Глава 4 Главное о графике
- •Концепция GDI
- •Структура GDI
- •Типы функций
- •Примитивы GDI
- •Другие аспекты
- •Контекст устройства
- •Получение описателя контекста устройства
- •Программа DEVCAPS1
- •Размер устройства
- •О цветах
- •Атрибуты контекста устройства
- •Сохранение контекста устройства
- •Рисование отрезков
- •Ограничивающий прямоугольник
- •Сплайны Безье
- •Использование стандартных перьев
- •Создание, выбор и удаление перьев
- •Закрашивание пустот
- •Режимы рисования
- •Рисование закрашенных областей
- •Функция Polygon и режим закрашивания многоугольника
- •Закрашивание внутренней области
- •Режим отображения
- •Координаты устройства (физические координаты) и логические координаты
- •Системы координат устройства
- •Область вывода и окно
- •Работа в режиме MM_TEXT
- •Метрические режимы отображения
- •Ваши собственные режимы отображения
- •Программа WHATSIZE
- •Прямоугольники, регионы и отсечение
- •Работа с прямоугольниками
- •Случайные прямоугольники
- •Создание и рисование регионов
- •Отсечения: прямоугольники и регионы
- •Программа CLOVER
- •Пути
- •Создание и воспроизведение путей
- •Расширенные перья
- •Bits and Blts
- •Цвета и битовые образы
- •Файл DIB
- •Упакованный формат хранения DIB
- •Отображение DIB
- •Преобразование DIB в объекты "битовые образы"
- •Битовый образ — объект GDI
- •Создание битовых образов в программе
- •Формат монохромного битового образа
- •Формат цветного битового образа
- •Контекст памяти
- •Мощная функция BitBlt
- •Перенос битов с помощью функции BitBlt
- •Функция DrawBitmap
- •Использование других ROP кодов
- •Дополнительные сведения о контексте памяти
- •Растяжение битовых образов с помощью функции StretchBlt
- •Кисти и битовые образы
- •Метафайлы
- •Простое использование метафайлов памяти
- •Сохранение метафайлов на диске
- •Расширенные метафайлы
- •Делаем это лучше
- •Базовая процедура
- •Заглянем внутрь
- •Вывод точных изображений
- •Текст и шрифты
- •Вывод простого текста
- •Атрибуты контекста устройства и текст
- •Использование стандартных шрифтов
- •Типы шрифтов
- •Шрифты TrueType
- •Система EZFONT
- •Внутренняя работа
- •Форматирование простого текста
- •Работа с абзацами
- •Глава 5 Клавиатура
- •Клавиатура. Основные понятия
- •Игнорирование клавиатуры
- •Фокус ввода
- •Аппаратные и символьные сообщения
- •Аппаратные сообщения
- •Системные и несистемные аппаратные сообщения клавиатуры
- •Переменная lParam
- •Виртуальные коды клавиш
- •Использование сообщений клавиатуры
- •Модернизация SYSMETS: добавление интерфейса клавиатуры
- •Логика обработки сообщений WM_KEYDOWN
- •Посылка асинхронных сообщений
- •Символьные сообщения
- •Сообщения WM_CHAR
- •Сообщения немых символов
- •Каретка (не курсор)
- •Функции работы с кареткой
- •Программа TYPER
- •Наборы символов Windows
- •Набор символов OEM
- •Набор символов ANSI
- •Наборы символов OEM, ANSI и шрифты
- •Международные интересы
- •Работа с набором символов
- •Связь с MS-DOS
- •Использование цифровой клавиатуры
- •Решение проблемы с использованием системы UNICODE в Windows NT
- •Глава 6 Мышь
- •Базовые знания о мыши
- •Несколько кратких определений
- •Сообщения мыши, связанные с рабочей областью окна
- •Простой пример обработки сообщений мыши
- •Обработка клавиш <Shift>
- •Сообщения мыши нерабочей области
- •Сообщение теста попадания
- •Сообщения порождают сообщения
- •Тестирование попадания в ваших программах
- •Гипотетический пример
- •Пример программы
- •Эмуляция мыши с помощью клавиатуры
- •Добавление интерфейса клавиатуры к программе CHECKER
- •Использование дочерних окон для тестирования попадания
- •Дочерние окна в программе CHECKER
- •Захват мыши
- •Рисование прямоугольника
- •Решение проблемы — захват
- •Программа BLOKOUT2
- •Глава 7 Таймер
- •Основы использования таймера
- •Система и таймер
- •Таймерные сообщения не являются асинхронными
- •Использование таймера: три способа
- •Первый способ
- •Второй способ
- •Третий способ
- •Использование таймера для часов
- •Позиционирование и изменение размеров всплывающего окна
- •Получение даты и времени
- •Обеспечение международной поддержки
- •Создание аналоговых часов
- •Стандартное время Windows
- •Анимация
- •Класс кнопок
- •Создание дочерних окон
- •Сообщения дочерних окон родительскому окну
- •Сообщения родительского окна дочерним окнам
- •Нажимаемые кнопки
- •Флажки
- •Переключатели
- •Окна группы
- •Изменение текста кнопки
- •Видимые и доступные кнопки
- •Кнопки и фокус ввода
- •Дочерние окна управления и цвета
- •Системные цвета
- •Цвета кнопок
- •Сообщение WM_CTLCOLORBTN
- •Кнопки, определяемые пользователем
- •Класс статических дочерних окон
- •Класс полос прокрутки
- •Программа COLORS1
- •Интерфейс клавиатуры, поддерживаемый автоматически
- •Введение новой оконной процедуры
- •Закрашивание фона
- •Окрашивание полос прокрутки и статического текста
- •Класс редактирования
- •Стили класса редактирования
- •Коды уведомления управляющих окон редактирования
- •Использование управляющих окон редактирования
- •Сообщения управляющему окну редактирования
- •Класс окна списка
- •Стили окна списка
- •Добавление строк в окно списка
- •Выбор и извлечение элементов списка
- •Получение сообщений от окон списка
- •Простое приложение, использующее окно списка
- •Список файлов
- •Утилита Head для Windows
- •Компиляция ресурсов
- •Значки и курсоры
- •Редактор изображений
- •Получение описателя значков
- •Использование значков в вашей программе
- •Использование альтернативных курсоров
- •Битовые образы: картинки в пикселях
- •Использование битовых образов и кистей
- •Символьные строки
- •Использование ресурсов-символьных строк
- •Меню
- •Структура меню
- •Шаблон меню
- •Ссылки на меню в вашей программе
- •Меню и сообщения
- •Образец программы
- •Этикет при организации меню
- •Сложный способ определения меню
- •Третий подход к определению меню
- •Независимые всплывающие меню
- •Использование системного меню
- •Изменение меню
- •Другие команды меню
- •Использование в меню битовых образов
- •Два способа создания битовых образов для меню
- •Контекст памяти
- •Создание битового образа, содержащего текст
- •Масштабирование битовых образов
- •Соберем все вместе
- •Добавление интерфейса клавиатуры
- •Быстрые клавиши
- •Зачем нужны быстрые клавиши?
- •Некоторые правила назначения быстрых клавиш
- •Таблица быстрых клавиш
- •Преобразование нажатий клавиш клавиатуры
- •Получение сообщений быстрых клавиш
- •Программа POPPAD, имеющая меню и быстрые клавиши
- •Разрешение пунктов меню
- •Обработка опций меню
- •Глава 11 Окна диалога
- •Модальные окна диалога
- •Создание окна диалога About
- •Шаблон окна диалога
- •Диалоговая процедура
- •Вызов окна диалога
- •Дополнительная информация о стиле окна диалога
- •Дополнительная информация об определении дочерних окон элементов управления
- •Более сложное окно диалога
- •Работа с дочерними элементами управления окна диалога
- •Кнопки OK и Cancel
- •Позиции табуляции и группы
- •Рисование в окне диалога
- •Использование с окном диалога других функций
- •Определение собственных окон управления
- •Окна сообщений
- •Информация во всплывающих окнах
- •Немодальные окна диалога
- •Различия между модальными и немодальными окнами диалога
- •Новая программа COLORS
- •Программа HEXCALC: обычное окно или окно диалога?
- •Творческое использование идентификаторов дочерних окон элементов управления
- •Диалоговые окна общего пользования
- •Модернизированная программа POPPAD
- •Изменение шрифта
- •Поиск и замена
- •Программа для Windows, содержащая всего один вызов функции
- •Основы элементов управления общего пользования
- •Инициализация библиотеки
- •Создание элементов управления общего пользования
- •Стили элементов управления общего пользования
- •Уведомляющие сообщения от элементов управления общего пользования
- •Элементы управления главного окна
- •Панели инструментов
- •Создание панели инструментов
- •Строка состояния
- •Программа GADGETS
- •Наборы страниц свойств
- •Создание набора страниц свойств
- •Процедуры диалогового окна страницы свойств
- •Программа PROPERTY
282
Эти сообщения позволяют вам удалять, копировать или очищать текущую выделенную часть текста. Пользователь выделяет текст для обработки, используя мышь или клавишу <Shift> с нужной клавишей управления курсором, выбранный текст подсвечивается в окне редактирования:
SendMessage(hwndEdit, WM_CUT, 0, 0);
SendMessage(hwndEdit, WM_COPY, 0, 0);
SendMessage(hwndEdit, WM_CLEAR, 0, 0);
Сообщение WM_CUT удаляет выделенный текст из окна редактирования и посылает его в папку обмена. Сообщение WM_COPY копирует выделенный текст в папку обмена, оставляя его неизменным в окне редактирования. Сообщение WM_CLEAR удаляет выделенный текст из окна редактирования без копирования его в папку обмена.
Вы также можете вставить текст из папки обмена в месте, соответствующем позиции курсора в окне редактирования:
SendMessage(hwndEdit, WM_PASTE, 0, 0);
Вы можете получить начальное и конечное положения текущего выделения:
SendMessage(hwndEdit, EM_GETSEL,(WPARAM) &iStart,(LPARAM) &iEnd);
Конечным положением фактически является положение последнего выделенного символа плюс 1. Вы можете выделить текст:
SendMessage(hwndEdit, EM_SETSEL, iStart, iEnd);
Вы также можете заменить текущий выделенный текст другим текстом:
SendMessage(hwndEdit, EM_REPLACESEL, 0,(LPARAM) szString);
Для многострочных окон редактирования вы можете получить число строк:
iCount = SendMessage(hwndEdit, EM_GETLINECOUNT, 0, 0);
Для любой отдельной строки вы можете получить смещение текста от начала буфера редактирования:
iOffset = SendMessage(hwndEdit, EM_LINEINDEX, iLine, 0);
Строки нумеруются, начиная с 0. При значении iLine равном —1 функция возвращает смещение строки, содержащей курсор. Длину строки можно получить из:
iLength = SendMessage(hwndEdit, EM_LINELENGTH, iLine, 0);
и копировать саму строку в буфер можно таким образом:
iLength = SendMessage(hwndEdit, EM_GETLINE, iLine,(LPARAM) szBuffer);
Класс окна списка
Последними из предопределенных в Windows дочерних окон управления, о которых будет рассказано в этой главе, являются окна списков (list box). Список — это набор текстовых строк, который выводится на экран в виде прокручиваемого в прямоугольнике столбца текста. Программа может добавлять или удалять строки в списке путем посылки сообщений оконной процедуре списка. Окно списка посылает сообщения WM_COMMAND своему родительскому окну, когда в списке выбирается какой-либо пункт. Родительское окно может определить, какой пункт списка был выбран.
Чаще всего списки используются в окнах диалога, например, когда окно диалога вызывается при выборе опции Open в меню File. В окне списка отображаются имена файлов текущего каталога, а также другие подкаталоги. Список может быть как с одиночным выбором, так и с множественным. Последний позволяет пользователю выбирать более одного пункта списка. Если окно списка имеет фокус ввода, то один из пунктов списка выводится окруженным штриховой линией. Такая индикация не означает, что данный пункт списка выбран. Выбранный пункт индицируется световым выделением, что означает вывод его в инверсном виде.
Всписке с единичным выбором пользователь может выбрать пункт списка, на котором находится курсор, путем нажатия клавиши <Spacebar>. Клавиши управления курсором перемещают как курсор, так и текущую выборку, и с помощью них можно прокручивать содержимое списка. Клавиши <Page Up> и <Page Down> также позволяют прокручивать список, при этом перемещается не выборка, а только курсор. Нажатие буквенной клавиши приводит к перемещению курсора и выборки к первому (или следующему) пункту, который начинается с этой буквы. Пункт списка также можно выбрать путем щелчка или двойного щелчка мыши.
Всписке со множественным выбором клавиша <Spacebar> переключает состояние выборки того пункта, на котором находится курсор. (Если пункт уже был выбран, то выборка отменяется.) Клавиши управления курсором
283
отменяют выборку всех ранее выбранных пунктов и перемещают курсор и выборку точно также, как в случае списка с единичной выборкой. Однако клавиша <Ctrl> совместно с клавишами управления курсором позволяет перемещать курсор без перемещения выборки. Клавиша <Shift> совместно с клавишами управления курсором позволяет расширять выборку.
Щелчок и двойной щелчок мыши на элементе списка с множественной выборкой отменяют все ранее выбранные пункты и выбирают тот пункт списка, на котором был щелчок. Однако щелчок мыши при нажатой клавише <Shift> добавляет в выборку указанный пункт без изменения состояния выборки других пунктов.
Стили окна списка
Дочернее окно списка вы создаете с помощью вызова функции CreateWindow, используя имя "listbox" в качестве имени класса окна и WS_CHILD в качестве идентификатора стиля. Однако при этом задаваемом по умолчанию стиле сообщения WM_COMMAND родительскому окну не посылаются. Это означает, что программе следует опрашивать окно списка (посредством сообщений к нему) относительно выбранных в списке пунктов. Поэтому окно списка почти всегда включает идентификатор стиля окна LBS_NOTIFY, что позволяет родительскому окну получать от окна списка сообщения WM_COMMAND. Если вы хотите получить возможность сортировки элементов списка, вам необходимо использовать в окне списка и другой часто используемый идентификатор стиля
— LBS_SORT.
По умолчанию, в списке допускается выбор только одного пункта. Если вы хотите создать список с возможностью выборки сразу нескольких пунктов, вам необходимо использовать идентификатор стиля LBS_MULTIPLESEL.
Обычно, если к списку добавляется новый элемент, то окно списка обновляется. Вы можете предотвратить это, если включите стиль LBS_NOREDRAW. Скорее всего вы не захотите использовать этот стиль. Вы можете временно запретить перерисовку окна списка, используя сообщение WM_SETREDRAW, о котором будет рассказано чуть позже.
По умолчанию, оконная процедура окна списка выводит на экран только список элементов без какой-либо рамки вокруг него. Рамку окна вы можете добавить с помощью идентификатора стиля окна WS_BORDER. Для прокрутки содержимого списка с помощью мыши и вертикальной полосы прокрутки используйте идентификатор стиля окна
WS_VSCROLL.
В заголовочных файлах Windows определяется стиль окна списка, который называется LBS_STANDART и включает в себя наиболее часто употребляемые стили. Он определяется следующим образом:
(LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER)
Вы также можете пользоваться идентификаторами WS_SIZEBOX и WS_CAPTION, которые дают возможность менять размер окна списка и перемещать его по рабочей области родительского окна.
Ширина окна списка должна подбираться с учетом ширины самой длинной строки списка плюс ширина полосы прокрутки. Ширину вертикальной полосы прокрутки можно получить с помощью вызова функции:
GetSystemMetrics(SM_CXVSCROLL);
Высоту окна списка можно рассчитать путем перемножения высоты символа на число пунктов, которые вы одновременно хотите видеть в окне. В окне списка при размещении строк текста величина межстрочного пространства не используется.
Добавление строк в окно списка
После того, как вы создали окно списка, следующим шагом должно стать добавление в список строк текста. Вы делаете это, используя функцию SendMessage для отправки сообщения окну списка. Ссылка на строки текста обычно осуществляется через индекс, который начинается с 0, что соответствует самому верхнему элементу списка. В приводимых далее примерах hwndList — это описатель дочернего окна управления, т. е. окна списка, а iIndex — это значение индекса. В тех случаях, когда вы с помощью вызова функции SendMessage передаете строку текста, параметр lParam является указателем на эту оканчивающуюся нулем строку.
В большинстве приводимых примеров функция SendMessage может вернуть значение LB_ERRSPACE (равное — 2), если оконной процедуре не хватит памяти для сохранения содержимого списка. Если случится любая другая ошибка, то возвращаемым значением функции SendMessage будет LB_ERR (—1), а при ее нормальной работе — LB_OKAY (0). Вы можете проверять функцию SendMessage на 0, чтобы определить наличие каждой из этих двух ошибок.
Если вы используете стиль LBS_SORT (или, если вы располагаете строки в списке в том порядке, в котором вы хотите, чтобы они появлялись), то простейшим способом заполнить список будет использование сообщения
LB_ADDSTRING:
284
SendMessage(hwndList, LB_ADDSTRING, 0,(LPARAM) szString);
Если вы не используете стиль LBS_SORT, то можете вставить строку в ваш список, задав индекс и используя сообщение LB_INSERTSTRING:
SendMessage(hwndList, LB_INSERTSTRING, iIndex,(LPARAM) szString);
Например, если iIndex равен 4, то szString становится новой, пятой строкой, начиная от вершины списка (поскольку счет начинается с 0) со значением индекса, равным 4. Все расположенные ниже строки сдвигаются вниз. При iIndex равном —1 строка становится последней строкой списка. Сообщение LB_INSERTSTRING можно использовать и со списком стиля LBS_SORT, но тогда содержимое списка не будет пересортировано. (Вы также можете вставить строку с помощью сообщения LB_DIR, о котором более подробно будет рассказано в конце главы.)
Удалить строку из списка можно с помощью сообщения LB_DELETESTRING, указав значение индекса:
SendMessage(hwndList, LB_DELETESTRING, iIndex, 0);
Полностью очистить список можно с помощью сообщения LB_RESETCONTENT:
SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
Оконная процедура окна списка обновляет окно, если к списку добавляется или из списка удаляется элемент. Если вам известно число строк, которые необходимо добавить или удалить, то вам может понадобиться временно приостановить это обновление, сбросив флаг обновления окна:
SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
После окончания работы по изменению списка, вам следует восстановить флаг обновления окна:
SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
Окно списка, созданное со стилем LBS_NOREDRAW, открывается со сброшенным флагом обновления окна.
Выбор и извлечение элементов списка
Вызовы функции SendMessage, которые выполняются в представленных ниже задачах, обычно возвращают какоето значение. Если имеет место ошибка, то это значение устанавливается в LB_ERR (определено как —1).
После того, как вы вставили в список несколько элементов, вы можете определить количество элементов в списке:
iCount = SendMessage(hwndList, LB_GETCOUNT, 0, 0);
Некоторые другие вызовы различаются для списков с единичной выборкой и для списков с множественной выборкой. Сначала рассмотрим список с единичной выборкой.
Обычно вы разрешаете пользователю выбирать из списка. Но если вы хотите выделить элемент, выбираемый по умолчанию, то можете использовать такой вызов:
SendMessage(hwndList, LB_SETCURSEL, iIndex, 0);
При установки в качестве iIndex значения —1, выборка для всех элементов отменяется. Вы также можете выбирать элемент списка на основе его первых символов:
iIndex = SendMessage(hwndList, LB_SELECTSTRING, iIndex,(LPARAM) szSearchString);
Величина iIndex, заданная в качестве параметра lParam функции SendMessage, является номером пункта, с которого начинается поиск пункта, начальные символы которого заданы в szSearchString. При значении iIndex равном —1, поиск начинается с начала списка. Возвращаемым значением функции SendMessage является индекс выбранного элемента или LB_ERR, если в списке нет элементов, с начальными символами из строки szSearchString.
Когда вы получаете от окна списка сообщение WM_COMMAND (или в любое другое время), с помощью сообщения LB_GETCURSEL вы можете определить индекс текущего выбранного элемента:
iIndex = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
Если возвращаемое значение функции SendMessage равно LB_ERR, то это означает отсутствие выбранных элементов.
Вы можете определить длину строки любого элемента списка:
iLength = SendMessage(hwndList, LB_GETTEXTLEN, iIndex, 0);
Копировать выбранную строку в буфер можно следующим образом:
iLength = SendMessage(hwndList, LB_GETTEXT, iIndex,(LPARAM) szBuffer);
285
Вобоих случаях возвращаемое значение функции SendMessage, т. е. iLength, это длина строки. Массив szBuffer должен быть достаточно большим, чтобы вместить строку и завершающий NULL-символ. Для предварительного выделения памяти для хранения строки, можно использовать сообщение LB_GETTEXTLEN.
Всписке с множественным выбором нельзя использовать сообщения LB_SETCURSEL, LB_GETCURSEL или LB_SELECTSTRING. Вместо них вы используете сообщение LB_SETSEL для установки состояния выборки конкретного элемента, при этом не оказывая влияния на другие элементы, которые могли бы быть выбраны:
SendMessage(hwndList, LB_SETSEL, wParam, iIndex);
Параметр wParam не равен 0 для выбора и выделения элемента списка и равен 0 для отмены выбора. Если параметр lParam равен —1, то все элементы списка либо выбираются, либо их выбор отменяется. Определить, выбран или нет конкретный элемент списка, можно с помощью вызова:
iSelect = SendMessage(hwndList, LB_GETSEL, iIndex, 0);
где iSelect не равно нулю, если пункт с номером iIndex выбран, и равно 0 — в противном случае.
Получение сообщений от окон списка
Когда пользователь щелкает мышью над окном списка, окно списка получает фокус ввода. Родительское окно может предоставить управляющему (listbox control) окну списка фокус ввода с помощью вызова функции:
SetFocus(hwndList);
Если окно списка имеет фокус ввода, то для выбора пунктов списка также могут использоваться клавиши управления курсором, буквенные клавиши и клавиша <Spacebar>.
Управляющее окно списка посылает сообщения WM_COMMAND своему родительскому окну. Значение параметров сообщения lParam и wParam то же, что и для кнопок управления и управляющих окон редактирования:
LOWORD (wParam) |
Идентификатор дочернего окна |
HIWORD (wParam) |
Код уведомления |
lParam |
Описатель дочернего окна |
Ниже перечисляются коды уведомления и их значения: |
|
LBN_ERRSPACE |
-2 |
LBN_SELCHANGE |
1 |
LBN_DBLCLK |
2 |
LBN_SELCANCEL |
3 |
LBN_SELFOCUS |
4 |
LBN_KILLFOCUS |
5 |
Окно списка посылает своему родительскому окну коды LBN_SELCHANGE и LBN_DBLCLK только в том случае, если в стиль дочернего окна включен идентификатор LBS_NOTIFY.
Код LBN_ERRSPACE показывает, что превышен размер памяти, отведенный для списка. Код LBN_SELCHANGE показывает, что был изменен текущий выбор; эти сообщения имеют место, когда пользователь перемещает подсветку по списку, изменяет состояние выборки с помощью клавиши <Spacebar> или выбирает нужный элемент списка с помощью щелчка мыши. Код LBN_DBLCLK показывает, что на данном пункте списка имел место двойной щелчок мыши. (Значение кодов уведомления для LBN_SELCHANGE и LBN_DBLCLK соответствует количеству щелчков мыши.)
В зависимости от вашего приложения, вы можете использовать либо сообщения LBN_SELCHANGE, либо сообщения LBN_DBLCLK, либо оба вместе. Ваша программа будет получать много сообщений LBN_SELCHANGE, что же касается сообщений LBN_DBLCLK, то они будут случаться только при двойных щелчках мышью. Если в вашей программе используются двойные щелчки мышью, то вам понадобиться обеспечить соответствующий интерфейс клавиатуры для дублирования сообщений LBN_DBLCLK.
Простое приложение, использующее окно списка
Теперь, когда вы знаете как создать список, как заполнить его текстовыми элементами, как получать сообщения от окна списка и как извлекать строки, самое время создать приложение, использующее список. Программа ENVIRON, представленная на рис. 8.8, использует окно списка в рабочей области окна для вывода имен текущих
286
переменных окружения MS-DOS (таких, как PATH, COMSPEC и PROMPT). При выборе переменной, имя и строка окружения выводятся в верхней части рабочей области.
ENVIRON.MAK
#-----------------------
# ENVIRON.MAK make file
#-----------------------
environ.exe : environ.obj
$(LINKER) $(GUIFLAGS) -OUT:environ.exe environ.obj $(GUILIBS)
environ.obj : environ.c
$(CC) $(CFLAGS) environ.c
ENVIRON.C
/*----------------------------------------
ENVIRON.C -- Environment List Box
(c) Charles Petzold, 1996
----------------------------------------*/
#include <windows.h> #include <stdlib.h> #include <string.h>
#define MAXENV 4096
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static char szAppName[] = "Environ";
HWND |
hwnd; |
MSG |
msg; |
WNDCLASSEX |
wndclass; |
wndclass.cbSize |
= sizeof(wndclass); |
wndclass.style |
= CS_HREDRAW | CS_VREDRAW; |
wndclass.lpfnWndProc |
= WndProc; |
wndclass.cbClsExtra |
= 0; |
wndclass.cbWndExtra |
= 0; |
wndclass.hInstance |
= hInstance; |
wndclass.hIcon |
= LoadIcon(NULL, IDI_APPLICATION); |
wndclass.hCursor |
= LoadCursor(NULL, IDC_ARROW); |
wndclass.hbrBackground |
=(HBRUSH)(COLOR_WINDOW + 1); |
wndclass.lpszMenuName |
= NULL; |
wndclass.lpszClassName |
= szAppName; |
wndclass.hIconSm |
= LoadIcon(NULL, IDI_APPLICATION); |
RegisterClassEx(&wndclass);
hwnd = CreateWindow(szAppName, "Environment List Box", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
287
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static char szBuffer[MAXENV + 1]; static HWND hwndList, hwndText;
HDC |
hdc; |
int |
i; |
TEXTMETRIC |
tm; |
switch(iMsg)
{
case WM_CREATE :
hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); ReleaseDC(hwnd, hdc);
hwndList = CreateWindow("listbox", NULL,
WS_CHILD | WS_VISIBLE | LBS_STANDARD, tm.tmAveCharWidth, tm.tmHeight * 3, tm.tmAveCharWidth * 16 +
GetSystemMetrics(SM_CXVSCROLL), tm.tmHeight * 5,
hwnd,(HMENU) 1,
(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
hwndText = CreateWindow("static", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT,
tm.tmAveCharWidth, tm.tmHeight, tm.tmAveCharWidth * MAXENV, tm.tmHeight, hwnd,(HMENU) 2,
(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
for(i = 0; environ[i]; i++)
{
if(strlen(environ [i]) > MAXENV) continue;
*strchr(strcpy(szBuffer, environ [i]), '=') = '\0'; SendMessage(hwndList, LB_ADDSTRING, 0,(LPARAM) szBuffer);
}
return 0;
case WM_SETFOCUS : SetFocus(hwndList); return 0;
case WM_COMMAND :
if(LOWORD(wParam) == 1 && HIWORD(wParam) == LBN_SELCHANGE)
{
i = SendMessage(hwndList, LB_GETCURSEL, 0, 0); i = SendMessage(hwndList, LB_GETTEXT, i,
(LPARAM) szBuffer);
strcpy(szBuffer + i + 1, getenv(szBuffer)); *(szBuffer + i) = '=';
SetWindowText(hwndText, szBuffer);
}
return 0;
case WM_DESTROY :