This post will take the existing sample Electron.NET application used in Create a Desktop Application using ASP.NET Core and Electron.NET and Electron.NET with a Web API and expand it to customize the application menu. I leaned heavily on the Electron.NET API Demos repo to guide how this should be done. The code before any changes can be found here.
Menu Controller
While not a requirement I follow the API Demo example of putting the application level menus in its own controller. Add a MenusController
to the Controllers
directory. The following is the full class.
public class MenusController : Controller { public IActionResult Index() { if (HybridSupport.IsElectronActive) { var menu = new MenuItem[] { new MenuItem { Label = "Edit", Submenu = new MenuItem[] { new MenuItem { Label = "Undo", Accelerator = "CmdOrCtrl+Z", Role = MenuRole.undo }, new MenuItem { Label = "Redo", Accelerator = "Shift+CmdOrCtrl+Z", Role = MenuRole.redo }, new MenuItem { Type = MenuType.separator }, new MenuItem { Label = "Cut", Accelerator = "CmdOrCtrl+X", Role = MenuRole.cut }, new MenuItem { Label = "Copy", Accelerator = "CmdOrCtrl+C", Role = MenuRole.copy }, new MenuItem { Label = "Paste", Accelerator = "CmdOrCtrl+V", Role = MenuRole.paste }, new MenuItem { Label = "Select All", Accelerator = "CmdOrCtrl+A", Role = MenuRole.selectall } } }, new MenuItem { Label = "View", Submenu = new MenuItem[] { new MenuItem { Label = "Reload", Accelerator = "CmdOrCtrl+R", Click = () => { // on reload, start fresh and close any old // open secondary windows Electron.WindowManager.BrowserWindows.ToList().ForEach(browserWindow => { if(browserWindow.Id != 1) { browserWindow.Close(); } else { browserWindow.Reload(); } }); } }, new MenuItem { Label = "Toggle Full Screen", Accelerator = "CmdOrCtrl+F", Click = async () => { bool isFullScreen = await Electron.WindowManager.BrowserWindows.First().IsFullScreenAsync(); Electron.WindowManager.BrowserWindows.First().SetFullScreen(!isFullScreen); } }, new MenuItem { Label = "Open Developer Tools", Accelerator = "CmdOrCtrl+I", Click = () => Electron.WindowManager.BrowserWindows.First().WebContents.OpenDevTools() }, new MenuItem { Type = MenuType.separator }, new MenuItem { Label = "App Menu Demo", Click = async () => { var options = new MessageBoxOptions("This demo is for the Menu section, showing how to create a clickable menu item in the application menu."); options.Type = MessageBoxType.info; options.Title = "Application Menu Demo"; await Electron.Dialog.ShowMessageBoxAsync(options); } } } }, new MenuItem { Label = "Window", Role = MenuRole.window, Submenu = new MenuItem[] { new MenuItem { Label = "Minimize", Accelerator = "CmdOrCtrl+M", Role = MenuRole.minimize }, new MenuItem { Label = "Close", Accelerator = "CmdOrCtrl+W", Role = MenuRole.close } } }, new MenuItem { Label = "Contacts", Role = MenuRole.window, Submenu = new MenuItem[] { new MenuItem { Label = "Create", Accelerator = "Shift+CmdOrCtrl+C", Click = () => Electron.WindowManager.BrowserWindows.First().LoadURL($"http://localhost:{BridgeSettings.WebPort}/Contacts/Create") } } } }; Electron.Menu.SetApplicationMenu(menu); } return Ok(); } }
What the above comes down to is building an array of MenuItem
types and then using Electron.Menu.SetApplicationMenu(menu)
to pass the array to Electron which handles replacing the default set of menus with the ones defined in the array.
For most of the items that were on the default set of menus all that is needed to add back the default functionality is to set the Role
to the function you want. For example in the above for a Copy menu item, we can assign Role
to MenuRole.copy
and Electron will handle the implementation of a copy without us having to write any additional code.
Navigate to a page from the application menu
One thing I wanted to be able to do was from a menu create a new contact. It was easy enough to add a top-level menu for Contacts and a sub-item for Create. It took me a while, but I finally figured out how to build a URL that would work. The following code is the menu items for the Contacts menu.
new MenuItem { Label = "Contacts", Role = MenuRole.window, Submenu = new MenuItem[] { new MenuItem { Label = "Create", Accelerator = "Shift+CmdOrCtrl+C", Click = () => Electron.WindowManager.BrowserWindows.First().LoadURL($"http://localhost:{BridgeSettings.WebPort}/Contacts/Create") } } }
The ASP.NET Core backend is running on localhost, the key that took me a while to locate was the port. In the end, I found that the port being used can be found using BridgeSettings.WebPort
.
Include the menu
The final change that is needed is to make sure the new set of menus get rendered. For the sample application open the _Layout.cshtml
file in the Views/Shared
directory. Inside the head
tag add the following line which will force a call to the MenusController
when the application loads.
<link rel="import" href="menus">
Wrapping Up
Customizing the application menu ended up being pretty easy. If I hadn’t wanted to navigate to a specific page I would have been done in no time, but hitting the issue with navigation helped me learn more about how Electron.NET is working. You can check out the finished code here.
Also published on Medium.
Hi,
thank you for your article.
I’d like to ask:
In your example you replaced the existing (default) menu with your new menu.
Ok.
But how to add yet another menu to the app?
For example: the default menu is
File, Edit, View, Window, Help
I’d like to add my menu item + subitems
File, Edit, View, Tools (Options, Customize), Window, Help
How to do it?
When I do as in example, sipmle :
var menu = new MenuItem[]
{
new MenuItem
{
Label = “Tools”,
Click = async () =>
{
await Electron.Dialog.ShowMessageBoxAsync(“Go-go Pockemon!”);
}
}
};
Electron.Menu.SetApplicationMenu(menu);
it replaces entire application menu.
I’m not sure the answer to the question Oleg. You could rebuild the standard menu, but I know that isn’t a fun answer. There may be a way to tap into the existing menu, but I didn’t go deep enough when I was looking at this topic to say for sure.
Ok, I see. Thank you Eric. I have asked the Electron.NET author, hope he will answer. There is:
https://github.com/ElectronNET/Electron.NET/issues/248
I hope so too! Good luck Oleg.