AppendMenu (GetSystemMenu (Handle, FALSE), MF_SEPARATOR, 0, '');
AppendMenu (GetSystemMenu (Handle, FALSE), MF_STRING, idSysAbout, '&About...');
Этот фрагмент кода (извлеченный из обработчика события OnCreate программы SysMenu) добавляет в системное меню программы разделитель и новый элемент. Функция API под названием GetSystemMenu, принимающая в качестве пара метра дескриптор формы, возвращает дескриптор системного меню. Функция API под названием AppendMenu — это общая функция добавления нового пункта в меню, которую можно использовать для добавления новых пунктов или даже новых ниспадающих меню в любое меню (главное меню программы, системное меню или существующее ниспадающее меню). При добавлении элемента в меню вы должны указать его текст и численный идентификатор. В данном примере я определил этот идентификатор следующим образом:
const idSysAbout:=100;
Как видно, добавление пункта в системное меню выполняется просто, но как выполнить обработку команды в момент, когда пользователь выбирает этот пункт? При выборе пункта в обычном меню операционная система Windows генерирует сообщение wm_Command. Это сообщение обрабатывается внутренними механизмами Delphi, в результате активизируется событие OnClick компонента, соответствующего выбранному пункту меню. При выборе команд системного меню генерируется сообщение wm_SysCommand, которое среда Delphi передает обработчику сообщений по умолчанию. Как правило, в ответ на выбор одного из пунктов системного меню операционная система Windows должна выполнить некоторые служебные операции. Мы можем перехватить эту команду и проверить, совпадает ли идентификатор команды со значением idSysAbout. Идентификатор команды передается в поле CmdField параметра TWmSysCommand. В Delphi нет соответствующего события, поэтому мы должны определить в рамках класса формы новый метод реакции на сообщение системы:
public
procedure WMSysCommand (var Msg:TMessage);
message wm_SysCommand;
Код этой процедуры не так уж и сложен. Мы всего лишь должны проверить, является ли команда нашей собственной, после чего следует обратиться к обработчику по умолчанию:
procedure TForm1.WMSysCommand (var Msg: TWMSysCommand);
begin
if Msg.CmdType = idSysAbout then
ShowMessage ('Mastering Delphi: SysMenu example');
inherited;
end;
При построении более сложного системного меню добавление и обслуживание каждого пункта меню по отдельности может оказаться неудобным, поэтому допускается использовать иной
подход. Для этого предлагается добавить в форму компонент MainMenu, сформировать его структуру (любую удобную для вас структуру), затем добавить все необходимые обработчики
сообщений, а затем сбросить свойство Menu формы, отключив тем самым отображение и использование главного меню формы.
Теперь в пример SysMenu предлагается добавить некоторый код, который переносит элементы скрытого меню в системное меню формы. Эта операция выполняется в момент, когда
пользователь щелкает на кнопке формы. В соответствующем обработчике используется универсальный код, который не зависит от структуры обрабатываемого меню:
procedure TForm1.Button1Click(Sender: TObject);
var
I; Integer;
begin
// добавляем разделитель
AppendMenu (GetSystemMenu (Handle, FALSE), MF_SEPARATOR, 0,' ');
// добавляем содержимое MainMenu в состав системного меню
with MainMenu1 do
for I := 0 to Items.Count - 1 do
AppendMenu (GetSystemMenu (Self.Handle, FALSE),
mf_Popup, Items[I].Handle. PChar(Items[I].Caption));
// деактивируем кнопку
Buttonl.Enabled := False;
end;
Используемый в данном случае флаг mf_Popup указывает на то, что мы добавляем ниспадающее меню. В данном обращении к функции четвертый параметр интерпретируется как дескриптор ниспадающего меню, которое мы добавляем в системное меню (в предыдущем примере вместо этого мы передавали в функцию идентификатор меню). Таким образом, мы добавляем в системное меню пункты, составляющие подменю, поэтому результирующая структура системного меню будет двухуровневой.
После того как вы добавили новые пункты в системное меню вашего приложения, вы должны обеспечить обработку этих пунктов. Конечно же, для этой цели можно выполнить проверку
каждого из этих пунктов в методе WMSysCommand, однако можно использовать и более умный подход. В Delphi проще написать для каждого элемента обработчик события OnClick, поэтому
мы можем выполнить поиск элемента, который соответствует данному идентификатору в структуре меню. Для облегчения этой задачи можно использовать метод Findltem.
Когда (и если) мы найдем элемент главного меню, соответствующий элементу, выбранному в системном меню, мы можем обратиться к его методу Click (при этом будет вызван обработчик
события OnClick). Приведу код, который я добавил в метод WMSysCommand:
var
Item: TMenuItem;
begin
Item := MainMenu1.Findltem (Msg.CmdType, fkCommand);
if Item <> nil then
Item.Click;
В данном фрагменте кода поле CmdType структуры сообщения, переданного в процедуру WMSysCommand, содержит команду выбранного элемента меню. Вы можете также использовать
простые операторы if или case для того, чтобы выполнить обработку любого из заранее определенных пунктов системного меню, идентификаторам которых соответствуют специальные коды,
такие как sc_Close, sc_Minimize, sc_Maximize и т. п. Дополнительную информацию по этому вопросу можно найти в файле электронной помощи по Windows API.
Рассмотренное приложение работает, однако у него есть небольшой дефект. Если вы правой кнопкой мыши щелкаете на соответствующем приложению значке системной панели задач, на
экране отображается обычное системное меню (которое отличается от сформированного вами). Причина в том, что данное системное меню принадлежит другому окну, окну глобального
объекта Application.