Среда, 02.07.2025, 17:19
Приветствую Вас Гость

Soft by Warik

NEMO_CHAT
Категории раздела
Анекдот
Случайный анекдот:

Наши часики
Опрос
Нужем вам свой icq чат?
Всего ответов: 71
Статистика
Rambler's Top100




Онлайн всего: 2
Гостей: 2
Пользователей: 0
Главная » Статьи » Win API [ Добавить статью ]

Создание главного меню средствами Win API

Сегодня мы добавим в наше минимальное приложение главное меню. Но прежде рассмотрим некоторые API функции для работы с ним.

CreateMenu
CreatePopupMenu
AppendMenu
InsertMenu
SetMenu
DrawMenuBar
EnableMenuItem
CheckMenuItem

Code
CreateMenu

function CreateMenu: HMENU;

В случае успешного выполнения, функция возвращает дескриптор созданного меню,
иначе возвращает 0.

Code
CreatePopupMenu

function CreatePopupMenu: HMENU;

Как и в случае с CreateMenu, после успешного выполнения, функция возвращает
дескриптор созданного меню, иначе возвращает 0.

Code
AppendMenu

function AppendMenu( hMenu: HMENU; uFlags, uIDNewItem: UINT; lpNewItem: PChar ): BOOL;

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

hMenu
  Идентификатор строки меню, раскрывающееся меню или подменю, которое будет изменено.
uFlags
  Определяет флажки, управляющие появлением и поведением нового пункта меню.  
  Этот параметр может быть комбинация значений.
uIDNewItem
  Определяет или идентификатор нового пункта меню или, если uFlags параметр
  установлен в MF_POPUP, дескриптор раскрывающегося меню или подменю.
lpNewItem
  Определяет содержание нового пункта меню.

Code
InsertMenu

function AppendMenu( hMenu: HMENU; uPosition, uFlags, uIDNewItem: UINT; lpNewItem: PChar ): BOOL;

Функция InsertMenu вставляет новый пункт в меню, перемещая другие элементы меню вниз.

Параметры этой функции те же, что и у AppendMenu, за одним исключением.

uPosition
  Определяет место, в которое должен быть вставлен новый пункт меню.

Code
SetMenu

function SetMenu( hWnd: HWND; hMenu: HMENU ): BOOL;

Функция SetMenu связываем новое меню с окном.

hWnd
  Идентификатор окна, которому должно быть назначено новое меню.
hMenu
  Идентификатор меню, которое должно быть назначено окну.
  Если этот параметр нулевой, текущее меню окна удаляется.

Code
DrawMenuBar

function DrawMenuBar( hWnd: HWND ): BOOL;

Функция DrawMenuBar перерисовывает строку меню указанного окна.

hWnd
  Идентификатор окна, чья строка меню нуждается в изменении.

Code
EnableMenuItem

function EnableMenuItem( hMenu: HMENU; uIDEnableItem, uEnable: UINT ): BOOL;

Функция EnableMenuItem включает/отключает указанный пункт меню.

hMenu
  Идентификатор меню.
uIDEnableItem
  Определяет пункт меню, состояние которого нужно изменить.
uEnable
  Определяет флажки, управляющие состоянием пункта меню.

Code
CheckMenuItem

function CheckMenuItem( hMenu: HMENU; uIDEnableItem, uEnable: UINT ): BOOL;

Функция CheckMenuItem помечает пункт меню или снимает пометку (пометка в виде галочки).

hMenu
  Идентификатор меню.
uIDCheckItem
  Определяет пункт меню, чей атрибут пометки должен быть
  установлен в соответствии со значением параметра uCheck.
uCheck
  Определяет флажки, управляющие состоянием пункта меню.

Более полную и подробную информацию смотрите в хэлпе. Делая эту программу, я нашел пару способов создать меню (они не очень сильно отличаются друг от друга, но оба имеют право на существование). На первом способе я остановлюсь поподробнее, а, говоря о втором, просто скажу, чем он отличается от первого.

Главное меню программы, это строка, которая располагается в верхней части формы. Она состоит из пунктов, нажатие на любой из них приведет к раскрытию подменю, принадлежащего данному пункту. Это всплывающее меню в Windows называется PopupMenu. Обратите внимание, понятие PopupMenu в Delphi и Windows различаются. В Windows PopupMenu - это подменю, принадлежащее другому пункту меню (который отмечается треугольником справа от текста пункта) или одному из пунктов главного меню. В Delphi PopupMenu — это меню, которое может "всплывать" в любой точке формы. Осознав вышесказанное, приступаем к работе.

Откроем шаблон, написанный в прошлый раз, и дополним список констант и переменных

Code
const
  WndClass = 'TWinApiWnd';
  WndCaption = 'Главное меню формы на Win API';
  mFile = 100;
  mEdit = 200;
  mCheck = 300;
  sExit = 101;
  sCopy = 201;
  sCut = 202;
  sPaste = 203;
  sSelect = 301;
  sNextMenu = 302;
  sSecondLevel = 311;
  SEPARATOR = 1;

var
  Wc: TWndClassEx;
  Wnd: HWND;
  Msg: TMsg;
  MainMenu: HMENU;
  SubMenuFile: HMENU;
  SubMenuEdit: HMENU;
  SubMenuCheck: HMENU;
  SubMenuSecondLevel: HMENU;
  Check: boolean = false;

Сначала разберемся с константами. Каждый пункт меню должен иметь свой уникальный идентификационный номер. Так как именно по этим номерам мы и будем работать с меню, удобнее всего оформить их как константы. Идея следующая. В главном меню будет 3 пункта: Menu, Edit и Check. Константы для него начинаются с буквы m и имеют номера 100, 200 и 300 соответственно. Каждому из этих пунктов будет сопоставлено свое подменю. Константы для них начинаются с буквы s и номеруются следующим образом: те, которые относятся к первому пункту, начинаются со 100+1, те, которые относятся ко второму пункту, начинаются с 200+1, ну и по аналогии, те, которые относятся к третьему пункту, начинаются с 300+1. В третьем пункте будет подменю второго уровня, константа для него равна 300+10+1. Отдельное значение имеет константа SEPARATOR, это просто разделитель между пунктами подменю.

Переходим к разделу var. Здесь появилось 5 переменных типа HMENU, они содержат Hendle соответствующего меню (какого, понятно из их названия). Переменная Check показывает, установлен или сброшен пункт меню (читайте дальше, и все поймете).

При добавлении нового пункта приходится выполнить ряд действий, в том числе инициализацию структуры MENUITEMINFO. Так как эти действия нужно производить при добавлении каждого пункта, чтобы избавиться от избыточности кода, оформим эти действия в виде отдельной процедуры.

Code
function CreateMenuItem( hMenu, SubMenu: HMENU; Cap: PChar;
  _uID, _wID: UINT; Sep: boolean ): boolean;
var
  Mi: MENUITEMINFO;
begin
  with Mi do
  begin
  cbSize := SizeOf( Mi );
  fMask := MIIM_STATE or MIIM_TYPE or MIIM_SUBMENU or MIIM_ID;
  if not Sep then
  fType := MFT_STRING
  else
  fType := MFT_SEPARATOR;
  fState := MFS_ENABLED;
  wID := _wID;
  hSubMenu := SubMenu;
  dwItemData := 0;
  dwTypeData := Cap;
  cch := SizeOf( Cap );
  end;
  Result := InsertMenuItem( hMenu, _uID, false, Mi );
end;

Поясню входные параметры функции. hMenu - меню, в которое добавляется новый пункт; SubMenu - связанное с этим пунктом подменю (если оно есть); Cap - заголовок нового пункта; _uID - всегда 0 (этот параметр используется в функции InsertMenuItem); _wID - идентификатор, связанный с данным пунктом; Sep - признак, является ли новый пункт разделителем или нет.

Наше приложение должно реагировать на выбор того или иного пункта меню. Дополним оконную процедуру следующим образом:

Code
function WindowProc( Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM ): LRESULT; stdcall;
begin
  case Msg of
  WM_DESTROY: begin
  PostQuitMessage( 0 );
  Result := 0;
  Exit;
  end;
  WM_COMMAND: begin
  case LOWORD( wParam ) of
  sExit: PostMessage( Wnd, WM_QUIT, 0, 0 );
  sCopy: MessageBox( Wnd, 'Пункт: Copy', 'Меню: Edit', 0 );
  sCut: MessageBox( Wnd, 'Пункт: Cut', 'Меню: Edit', 0 );
  sPaste: MessageBox( Wnd, 'Пункт: Paste', 'Меню: Edit', 0 );
  sSelect: begin
  if Check then
  CheckMenuItem( SubMenuCheck, sSelect, MF_UNCHECKED )
  else
  CheckMenuItem( SubMenuCheck, sSelect, MF_CHECKED );
  Check := not Check;
  end;
  sSecondLevel: MessageBeep( MB_ICONHAND );
  end;
  end;
  else
  Result := DefWindowProc( Wnd, Msg, wParam, lParam );
  end;
end;

При выборе пункта Exit (константа sExit) программа будет закрыта. Выбор пунктов Copy, Cut, Paste (константы sCopy, sCut и sPaste соответственно) приведет к появлению сообщения, соответствующего выбранному пункту. Пункт Select (константа sSelect) работает аналогично TCheckBox, то есть может быть установлен или сброшен. При выборе пункта Beep (константа sSecondLevel) мы услышим звуковой сигнал.

Теперь для создания меню все готово. Приступаем.

Code
begin
  // Создаем меню
  MainMenu := CreateMenu;
  // Заполняем структуру TWndClassEx
  with Wc do
  begin
  cbSize := SizeOf( Wc );
  style := CS_HREDRAW or CS_VREDRAW;
  lpfnWndProc := @WindowProc;
  cbClsExtra := 0;
  cbWndExtra := 0;
  hInstance := hInstance;
  hIcon := LoadIcon( 0, IDI_APPLICATION );
  hCursor := LoadCursor( 0, IDC_ARROW );
  hbrBackground := COLOR_WINDOW;
  lpszMenuName := @MainMenu;
  lpszClassName := WndClass;
  end;
  // Регистрируем класс в системе
  RegisterClassEx( Wc );
  // Создаем подменю
  SubMenuFile := CreatePopupMenu;
  SubMenuEdit := CreatePopupMenu;
  SubMenuCheck := CreatePopupMenu;
  SubMenuSecondLevel := CreatePopupMenu;
  // Создаем окно
  Wnd := CreateWindowEx( 0, WndClass, WndCaption, WS_OVERLAPPEDWINDOW,
  10, 10, 300, 100, 0, MainMenu, hInstance, nil );
  // Создаем пункты главного меню
  CreateMenuItem( MainMenu, subMenuFile, 'File', 0, mFile, false );
  CreateMenuItem( MainMenu, subMenuEdit, 'Edit', 0, mFile, false );
  CreateMenuItem( MainMenu, subMenuCheck, 'Check', 0, mFile, false );
  // Подменю для пункта File
  CreateMenuItem( SubMenuFile, 0, 'Exit', 0, sExit, false );
  // Подменю для пункта Edit
  CreateMenuItem( SubMenuEdit, 0, 'Copy', 0, sCopy, false );
  CreateMenuItem( SubMenuEdit, 0, 'Cut', 0, sCut, false );
  CreateMenuItem( SubMenuEdit, 0, '', 0, SEPARATOR, true );
  CreateMenuItem( SubMenuEdit, 0, 'Paste', 0, sPaste, false );
  // Подменю для пункта Check->NextMenu
  CreateMenuItem( SubMenuSecondLevel, 0, 'Beep', 0, sSecondLevel, false );
  // Подменю для пункта Check
  CreateMenuItem( SubMenuCheck, 0, 'Select', 0, sSelect, false );
  CreateMenuItem( SubMenuCheck, SubMenuSecondLevel, 'NextMenu', 0, sNextMenu, false );
  // Перерисовываем меню
  DrawMenuBar( Wnd );
  // Показываем окно
  ShowWindow( Wnd, SW_SHOWNORMAL );

  // Цикл обработки сообщений
  while GetMessage( Msg, 0, 0, 0 ) do
  begin
  TranslateMessage( Msg );
  DispatchMessage( Msg );
  end;
  Halt( Msg.wParam );
end.

Разбираемся. Первым делом создаем главное меню. Указатель на него присваиваем полю lpszMenuName структуры Wc (раньше оно было равно nil). После регистрации класса в системе создаем подменю. При создании окна, параметр hMenu функции CreateWindowEx равен MainMenu (Handle созданного меню, а не 0, как было в шаблоне). После создания всех пунктов, перерисовываем меню при помощи функции DrawMenuBar.

Теперь второй способ создания меню. Здесь я приведу его в сокращении, и поясню произведенные изменения (полный текст программы имеется в архиве с примерами в конце статьи).

Code
begin
  // Заполняем структуру TWndClassEx
  with Wc do
  begin
  ...
  lpszMenuName := nil;
  ...
  end;

  ...

  // Создаем меню
  MainMenu := CreateMenu;

  ...

  Wnd := CreateWindowEx( 0, WndClass, WndCaption, WS_OVERLAPPEDWINDOW,
  10, 10, 300, 100, 0, 0, hInstance, nil );

  ...

  // Устонавливаем меню
  SetMenu( Wnd, MainMenu );

  ...
end.

Поле lpszMenuName структуры Wc остается равным nil. Главное меню создаем после регистрации класса в системе. При создании окна, параметр hMenu функции CreateWindowEx можно оставить равным 0, т.к. здесь мы не перерисовываем меню, а назначаем его форме, используя функцию SetMenu.

Все. Теперь у нашей формы есть меню. Это оказалось совсем не сложно. До новых встреч, удачи в программировании.

Источник: http://nemo-chat.3dn.ru

Категория: Win API | Добавил: Warik (23.02.2010)
Просмотров: 438 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Мини-профиль

Гость !



Гость, мы рады вас видеть. Пожалуйста зарегистрируйтесь или авторизуйтесь!
Друзья сайта