Typically we use a combobox or listbox to allow to the final user select and appy a VCL Style, Today I will show you a new way using the system menu of the form.
First you need to use the GetSystemMenu WinApi function to get a handle to the system menu of the form. Then using the AppendMenu or the InsertMenuItem methods you can customize the system menu, from here you must store the identifier of the new menu item added and finally process the WM_SYSCOMMAND message to launch an action.
Check the next commented code
uses System.Rtti, System.Classes, System.Generics.Collections, WinApi.Windows, WinApi.Messages, Vcl.Themes, Vcl.Styles, Vcl.Forms; type TMethodInfo=class; TProcCallback = reference to procedure(Info : TMethodInfo); TMethodInfo=class Value1 : TValue; Value2 : TValue; Method : TProcCallback; end; TVclStylesSystemMenu=class(TComponent) strict private FVCLStylesMenu : HMenu; FOrgWndProc: TWndMethod; FForm : TForm; FMethodsDict : TObjectDictionary<NativeUInt, TMethodInfo>; procedure CreateMenus; procedure DeleteMenus; procedure CreateMenuStyles; procedure WndProc(var Message: TMessage); public constructor Create(AOwner: TForm); reintroduce; destructor Destroy; override; end; implementation uses Vcl.Controls, System.SysUtils; const VCLStylesMenu=WM_USER + 666; //Add a new Menu Item function InsertMenuHelper(hMenu: HMENU; uPosition: UINT; uIDNewItem: UINT_PTR; lpNewItem, IconName: LPCWSTR) : BOOL; var LMenuItem : TMenuItemInfo; begin ZeroMemory(@LMenuItem, SizeOf(TMenuItemInfo)); LMenuItem.cbSize := SizeOf(TMenuItemInfo); LMenuItem.fMask := MIIM_FTYPE or MIIM_ID or MIIM_BITMAP or MIIM_STRING; LMenuItem.fType := MFT_STRING; LMenuItem.wID := uIDNewItem; LMenuItem.dwTypeData := lpNewItem; Result:=InsertMenuItem(hMenu, uPosition, True, LMenuItem); end; //Add a new separator procedure AddMenuSeparatorHelper(hMenu : HMENU; var MenuIndex : Integer); var LMenuInfo : TMenuItemInfo; Buffer : array [0..79] of char; begin ZeroMemory(@LMenuInfo, SizeOf(TMenuItemInfo)); LMenuInfo.cbSize := sizeof(LMenuInfo); LMenuInfo.fMask := MIIM_TYPE; LMenuInfo.dwTypeData := Buffer; LMenuInfo.cch := SizeOf(Buffer); if GetMenuItemInfo(hMenu, MenuIndex-1, True, LMenuInfo) then begin if (LMenuInfo.fType and MFT_SEPARATOR) = MFT_SEPARATOR then else begin InsertMenu(hMenu, MenuIndex, MF_BYPOSITION or MF_SEPARATOR, 0, nil); inc(MenuIndex); end; end; end; { TVclStylesSystemMenu } constructor TVclStylesSystemMenu.Create(AOwner: TForm); begin inherited Create(AOwner); //Get an instance to the form FForm:=AOwner; //Init the collection to store the menu ids and callbacks FMethodsDict:=TObjectDictionary<NativeUInt, TMethodInfo>.Create([doOwnsValues]); //store the original WndProc FOrgWndProc := FForm.WindowProc; //replace the WndProc of the form FForm.WindowProc := WndProc; //Modify the system menu CreateMenus; end; destructor TVclStylesSystemMenu.Destroy; begin DeleteMenus; FForm.WindowProc := FOrgWndProc; FMethodsDict.Free; inherited; end; procedure TVclStylesSystemMenu.CreateMenus; begin CreateMenuStyles; end; procedure TVclStylesSystemMenu.DeleteMenus; begin if IsMenu(FVCLStylesMenu) then while GetMenuItemCount(FVCLStylesMenu)>0 do DeleteMenu(FVCLStylesMenu, 0, MF_BYPOSITION); FMethodsDict.Clear; end; procedure TVclStylesSystemMenu.CreateMenuStyles; var LSysMenu : HMenu; LMenuItem: TMenuItemInfo; s : string; uIDNewItem, LSubMenuIndex : Integer; LMethodInfo : TMethodInfo; begin LSysMenu := GetSystemMenu(FForm.Handle, False); LSubMenuIndex:=GetMenuItemCount(LSysMenu); AddMenuSeparatorHelper(LSysMenu, LSubMenuIndex); FVCLStylesMenu := CreatePopupMenu(); s:='VCL Styles'; uIDNewItem := VCLStylesMenu; ZeroMemory(@LMenuItem, SizeOf(TMenuItemInfo)); LMenuItem.cbSize := SizeOf(TMenuItemInfo); LMenuItem.fMask := MIIM_SUBMENU or MIIM_FTYPE or MIIM_ID or MIIM_BITMAP or MIIM_STRING; LMenuItem.fType := MFT_STRING; LMenuItem.wID := VCLStylesMenu; LMenuItem.hSubMenu := FVCLStylesMenu; LMenuItem.dwTypeData := PWideChar(s); LMenuItem.cch := Length(s); //Add the new menu item to the system menu InsertMenuItem(LSysMenu, GetMenuItemCount(LSysMenu), True, LMenuItem); inc(uIDNewItem); LSubMenuIndex:=0; //Iterate over the registered styles and create a new menu entry for each style for s in TStyleManager.StyleNames do begin InsertMenuHelper(FVCLStylesMenu, LSubMenuIndex, uIDNewItem, PChar(s), nil); if SameText(TStyleManager.ActiveStyle.Name, s) then CheckMenuItem(FVCLStylesMenu, LSubMenuIndex, MF_BYPOSITION or MF_CHECKED); inc(LSubMenuIndex); inc(uIDNewItem); LMethodInfo:=TMethodInfo.Create; LMethodInfo.Value1:=s; //set the method to execute when the item is clicked LMethodInfo.Method:=procedure(Info : TMethodInfo) begin TStyleManager.SetStyle(Info.Value1.AsString); end; //register the menu id and the callback function. FMethodsDict.Add(uIDNewItem-1, LMethodInfo); end; end; procedure TVclStylesSystemMenu.WndProc(var Message: TMessage); var LVerb : NativeUInt; begin case Message.Msg of //Detect when the window handle is recreated CM_RECREATEWND: begin DeleteMenus; FOrgWndProc(Message); CreateMenus; end; //Track the system menu calls WM_SYSCOMMAND : begin if FMethodsDict.ContainsKey(TWMSysCommand(Message).CmdType) then begin LVerb:=TWMSysCommand(Message).CmdType; FMethodsDict.Items[LVerb].Method(FMethodsDict.Items[LVerb]); end else FOrgWndProc(Message); end else FOrgWndProc(Message); end; end; end.
And this the result
To use this class, only you need create an new instance passing a reference to the form.
procedure TForm1.FormCreate(Sender: TObject); begin TVclStylesSystemMenu.Create(Self); end;
You can check the full source code here.