Blog. Just Blog

Меню с иконками на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Сегодня разберем кастомизацию такого элемента интерфейса, как выпадающее меню. Без меню обходится мало какое современное приложение, но вот работа с меню стандартными средствами обычно ограничивается установкой флага чекбокса, затенением неактивных пунктов или отрисовкой субменю. Попытка разнообразить меню, например, своими иконками, приводит к очень печальному результату. Ситуацию особо не спасают ни собственные битмапы, ни подгрузка изображений из списка ImageList. Очень странно, что на протяжении многих лет разработчики Windows так и не сделали инструментов "из коробки", чтобы можно было легко и комфортно работать с менюшками. К счастью, в системе есть "потайной ход", с помощью которого можно кастомизировать меню так, как вам захочется. Для своих проектов я постарался сделать как можно более универсальный инструмент для работы с меню и сейчас я с вами им поделюсь.

Начнем создание собственных элементов интерфейса с того, что для каждого настраиваемого меню резервируется структура с данными следующего формата:
  1. struct MYMENU
  2.   hMenu      dd ?
  3.   itemWidth  dd ?
  4.   itemHeight dd ?
  5.   hasIcons   db ?
  6. ends
В это же время, все меню, которые требуют кастомизации, формируют массив структур и указателей на них. Список заканчивается нулевым DWORD'ом:
  1. menus   dd Menu1
  2.         dd Menu2
  3.         dd ?
  4.  
  5. Menu1   MYMENU
  6. Menu2   MYMENU
Описание полей структуры: hMenu - хэндл меню, itemWidth - ширина пункта меню в пикселах, itemHeight - высота пункта меню в пикселах, hasIcons - флаг, определяющий, будут или нет использоваться в меню иконки (1 - да, 0 - нет). Если иконки будут использоваться, то слева от текста пунктов меню резервируется место для них, в противном случае текст пункта будет отрисовываться прямо от края меню.

Если вы хотите использовать значения размеров меню, соответствующие стандартным, то для этих целей описываются две константы с дефолтными значениями, определенными опытным путем. Еще одна константа используется в качестве идентификатора заголовков. Естественно, вы можете заменить их на ваши значения:
  1. DEFAULT_ITEM_WIDTH  = 80
  2. DEFAULT_ITEM_HEIGHT = 18
  3.  
  4. DEFAULT_HEADER_ID   = 9999
Для хранения иконок используется массив из пар DWORD'ов, определяющих ID пункта меню и хэндл иконки, который ему соответствует. Можно обернуть это в какую-нибудь структуру, но лично мне кажется это лишним.
  1. ICONS_COUNT = 100
  2. hIcons  rd ICONS_COUNT*2
Теперь ненадолго отвлечемся на теорию. Для того, чтобы можно было использовать собственный обработчик для кастомизации меню, его пункты должны быть добавлены с использованием флага MFT_OWNERDRAW. Перед отрисовкой такого пункта меню система отправляет окну-владельцу сообщение WM_MEASUREITEM. В lParam этого сообщения передается указатель на структуру MEASUREITEMSTRUCT, в которую, при необходимости, мы можем внести свои изменения, а именно установить ширину и высоту пунктов меню. Непосредственно при отрисовке пункта меню окну отправляется сообщение WM_DRAWITEM. В lParam сообщения передается указатель на структуру DRAWITEMSTRUCT, из которой мы можем получить все необходимые значения, например, текст пункта меню или состояние чекбокса, а также самостоятельно нарисовать пункт меню в том виде, в каком нам надо.

На этом можно заканчивать с теорией и переходить непосредственно к программированию. Начнем с создания меню. Обычно это делается на этапе инициализации окна. Создадим два меню с разными характеристиками.
  1.         ; Очистить память под массив иконок
  2.         invoke  RtlZeroMemory