Системное меню: Как скрыть / переместить стандартные пункты меню
В системном меню (в верхней части заголовка) я могу добавить свои собственные пункты меню. Я также могу удалить, например,DeleteMenu(SysMenu, SC_MINIMIZE, MF_BYCOMMAND) ;
Однако, если я удаляю стандартные [восстановить, минимизировать, максимизировать, изменить размер, закрыть], их функциональность теряется (т.е. кнопка максимизации больше не работает)
Есть ли способ скрыть эти элементы меню или убрать их с первого ранга системного меню? а) сделать их невидимыми б) перейти в подменю в) удалить, но по-прежнему получать сообщения кнопок
3 ответа
а) сделать их невидимыми
В API нет концепции скрытого / невидимого пункта меню.
б) перейти в подменю
Вы можете перемещать (или, скорее, удалять и добавлять) элементы в подменю, не влияя на функциональность.
Например, переместите "свернуть" в подменю:
var
SysMenu, SubMenu: HMENU;
StrMin: string;
StrMinLen: Integer;
begin
SysMenu := GetSystemMenu(Handle, False);
StrMinLen := GetMenuString(SysMenu, SC_MINIMIZE, nil, 0, MF_BYCOMMAND);
if StrMinLen > 0 then begin
Inc(StrMinLen);
SetLength(StrMin, StrMinLen);
GetMenuString(SysMenu, SC_MINIMIZE, PChar(StrMin), StrMinLen, MF_BYCOMMAND);
SubMenu := CreateMenu;
if SubMenu <> 0 then begin
DeleteMenu(SysMenu, SC_MINIMIZE, MF_BYCOMMAND);
AppendMenu(SubMenu, MF_STRING, SC_MINIMIZE, PChar(StrMin));
InsertMenu(SysMenu, 0, MF_BYPOSITION or MF_POPUP, SubMenu, 'Minimize->');
InsertMenu(SysMenu, 1, MF_BYPOSITION or MF_SEPARATOR, 0, nil);
end;
end;
Удалите подменю перед восстановлением системного меню:
var
Info: TMenuItemInfo;
begin
Info.cbSize := SizeOf(Info);
Info.fMask := MIIM_SUBMENU;
if GetMenuItemInfo(GetSystemMenu(Handle, False), 0, True, Info) then
DestroyMenu(Info.hSubMenu);
GetSystemMenu(Handle, True);
в) удалить, но по-прежнему получать сообщения кнопки
Если вы удалите, например, пункт "свернуть", система не отправит WM_SYSCOMMAND
сообщения для команды свернуть в окно. Так что не будет никакой команды, чтобы ответить.
Вы все еще можете прослушивать сообщения кнопок, например, левой кнопкой вниз. Но сообщение о нажатии кнопки вниз / вверх - это не то же самое, что нажатие кнопки. Нажатие кнопки состоит из трех действий: нажатие кнопки мыши, захват и повторное нажатие кнопки. Если вы хотите сделать это в любом случае, например:
procedure TForm1.WMNCLButtonDown(var Message: TWMNCLButtonDown);
begin
inherited;
if (Message.HitTest = HTMINBUTTON) and not IsIconic(Handle) then
ShowWindow(Handle, SW_MINIMIZE);
end;
Первоначально заголовок окна имел только две кнопки в верхнем правом углу, кнопки свертывания и максимизации, и они управлялись стилем окна. В Windows 95 добавлена кнопка "Закрыть", но возник вопрос о том, когда включать и отключать ее. Но подождите, мы уже знаем, когда включать и отключать его: приложение сообщало нам, когда оно включало и отключало пункт меню SC_CLOSE. Бинго, просто подключите кнопку "Закрыть" к существующему пункту меню (какие приложения уже привыкли обслуживать), и магия, это просто работает. Нет необходимости для приложений писать специальный код для поддержки кнопки "Закрыть".
Теперь вы знаете, почему SC_CLOSE привязан к кнопке. Поэтому правильным способом предотвратить закрытие пользователя во время какой-либо операции является отключение пункта меню.
Если вы настаиваете на том, чтобы удалить его по какой-либо причине, но по-прежнему разрешаете закрывать окно, то вы должны удалить этот пункт, когда должно отобразиться системное меню (WM_INITMENU
) и вернуть системное меню после закрытия меню (WM_UNINITMENUPOPUP
).
Sertac ответил на вопрос: вы можете только переместить их.
Вот полное окончательное решение для использования Sysmenu: a) Перемещает все стандартные элементы, кроме как близко к подменю, которое скрыто за разделителем. б) добавляет пункты меню из Form1.PopupMenu1 в SysMenu; в) удаляет их (перед тем, как сделать окно полноэкранным / без полей, так как это разрушает системное меню); г) показывает системное меню
procedure TForm1.SysMenuAddRemoveExtraItems(Add:boolean=true);
//
var
SysMenu, SubMenu : HMenu;
const NumItems:integer=0;
procedure InsertM(Position,I:integer;J:integer=-1;S:string='');
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647987(v=vs.85).aspx
var M:TMenuItem; H:thandle; Flags:cardinal;
begin
M:=PopupMenu1.Items;
if I>=0 then M:=M.Items[I];
Flags:=MF_BYPOSITION+MF_STRING;
if M.Count>1 then Flags:=Flags+MF_POPUP;
if J>=0 then M:=M.Items[J];
H:=M.Handle;
if S='' then S:=M.Caption;
InsertMenu(SysMenu,Position,Flags,H,pwidechar(S));
inc(NumItems);
end;
procedure InsertSeparator(Position:integer);
begin
InsertMenu(SysMenu,Position,MF_BYPOSITION,MF_SEPARATOR,0); {Add a seperator bar to main form-form1}
inc(NumItems);
end;
procedure RemoveItems;
var i:integer;
begin
for i := 1 to NumItems do //remove items from top
RemoveMenu(SysMenu,0,MF_BYPOSITION);
NumItems:=0;
end;
procedure DeleteAppend(ID:cardinal); //to move standard menuitems to submenu
var
Caption: string;
CaptionLen: Integer;
begin
CaptionLen := GetMenuString(SysMenu, ID, nil, 0, MF_BYCOMMAND);
if CaptionLen > 0 then begin
Inc(CaptionLen);
SetLength(Caption, CaptionLen);
GetMenuString(SysMenu, ID, PChar(Caption), CaptionLen, MF_BYCOMMAND);
DeleteMenu(SysMenu, ID, MF_BYCOMMAND);
AppendMenu(SubMenu, MF_STRING, ID, PChar(Caption));
end;
end;
procedure MoveDefaultSysMenuItemsToSubmenu(Caption:string='';JustSeparator:boolean=false);
//Can either have a a caption or JustSeparator (submenu will be inaccessible)
// https://stackru.com/questions/44735708/system-menu-how-to-hide-move-standard-menuitems/44743027#44743027
begin
SubMenu := CreateMenu; //make submenu to move them into
if SubMenu <> 0 then begin
DeleteAppend(SC_RESTORE);
DeleteAppend(SC_MOVE);
DeleteAppend(SC_SIZE);
DeleteAppend(SC_MINIMIZE);
DeleteAppend(SC_MAXIMIZE);
if JustSeparator then begin
DeleteMenu(SysMenu, 0, MF_BYPOSITION); //remove separator above CLOSE
InsertMenu(SysMenu, 0, MF_BYPOSITION or MF_POPUP or MF_SEPARATOR, SubMenu, '');
end else begin
DeleteMenu(SysMenu, 0, MF_BYPOSITION); //remove separator above CLOSE
InsertMenu(SysMenu, 0, MF_BYPOSITION or MF_POPUP, SubMenu, PChar(Caption));
InsertMenu(SysMenu, 1, MF_BYPOSITION or MF_SEPARATOR, 0, nil);
end;
end;
end;
procedure DestroySubmenu;
var Info: TMenuItemInfo;
begin
Info.cbSize := SizeOf(Info);
Info.fMask := MIIM_SUBMENU;
if GetMenuItemInfo(GetSystemMenu(Handle, False), 0, True, Info) then
DestroyMenu(Info.hSubMenu);
//GetSystemMenu(Handle, True);
end;
begin
SysMenu := GetSystemMenu(Handle, FALSE) ; {Get system menu}
//InsertMenu(SysMenu,1,MF_BYPOSITION+MF_STRING,SC_MyMenuItem2,'pqr');
if Add then begin
MoveDefaultSysMenuItemsToSubmenu('',true);
// InsertSeparator(0);
InsertM(0,PopupMenu1.Items.Count-2);
InsertM(0,-1,-1,'Menu'); //help
InsertM(0,7);
end
else begin //remove items
RemoveItems;
DestroySubmenu;
end;
end;
//------------------------------------
procedure TForm1.ShowSysMenu;
var P:TPoint;
begin
P:=ClientToScreen(Point(0,0));
TrackPopupMenu(GetSystemMenu(Handle, FALSE), TPM_LEFTALIGN+TPM_TOPALIGN+TPM_RETURNCMD+TPM_NONOTIFY,
P.X,P.Y,0,self.Handle,nil);
end;
//--------------------------------------------------------------
procedure TForm1.WMSysCommand(var Msg: TWMSysCommand);
// https://www.delphipower.xyz/guide_7/customizing_the_system_menu.html
var Item: TMenuItem;
begin
Item := PopupMenu1.FindItem (Msg.CmdType, fkCommand);
if assigned(Item) then Item.Click
else
// case Msg.CmdType of
// //put any other specials in here
// else
inherited;
// end;
end;