Update Context Menu Item for tray app on-the-fly. #337

Closed
opened 2026-01-29 16:36:54 +00:00 by claunia · 2 comments
Owner

Originally created by @Tum4ik on GitHub (Jun 1, 2019).

Originally assigned to: @GregorBiswanger on GitHub.

In my case it is related to the application localization, but it could be also useful for some other scenarios (like change item visibility or enable/disable item).

Currently I have this Desktoper class to bring the app to the desktop.

using System;
using System.Globalization;
using System.IO;
using System.Linq;
using ElectronNET.API;
using ElectronNET.API.Entities;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Tum4ik.RemoteControl.Server.Services;
using Tum4ik.RemoteControl.Server.Services.MQTT.MqttServer;

namespace Tum4ik.RemoteControl.Server.Desktop
{
  /// <inheritdoc />
  public class Desktoper : IDesktoper
  {
    private const string TrayRunIcon = "Desktop/icons/tray-run-icon.png";
    private const string TrayStopIcon = "Desktop/icons/tray-stop-icon.png";

    private readonly IServiceProvider _serviceProvider;
    private readonly IHostingEnvironment _env;
    private readonly IStringLocalizer<Desktoper> _localizer;
    private readonly IMqttServerService _mqttServerService;

    private BrowserWindow _settingsWindow;
    private readonly MenuItem[] _menuItems;

    public Desktoper(IServiceProvider serviceProvider,
                     IHostingEnvironment env,
                     IStringLocalizer<Desktoper> localizer,
                     IMqttServerService mqttServerService)
    {
      _serviceProvider = serviceProvider;
      _env = env;
      _localizer = localizer;
      _mqttServerService = mqttServerService;

      _menuItems = CreateMenuItems();
    }


    private MenuItem _runServerMenuItem;
    private MenuItem _stopServerMenuItem;
    private MenuItem _settingsMenuItem;
    private MenuItem _aboutMenuItem;
    private MenuItem _exitMenuItem;
    private readonly Func<IStringLocalizer, string> _runServerMenuItemLabelFunc = localizer => localizer["Run Server"];
    private readonly Func<IStringLocalizer, string> _stopServerMenuItemLabelFunc = localizer => localizer["Stop Server"];
    private readonly Func<IStringLocalizer, string> _settingsMenuItemLabelFunc = localizer => localizer["Settings"];
    private readonly Func<IStringLocalizer, string> _aboutMenuItemLabelFunc = localizer => localizer["About"];
    private readonly Func<IStringLocalizer, string> _exitMenuItemLabelFunc = localizer => localizer["Exit"];


    private MenuItem[] CreateMenuItems()
    {
      var trayMenuRunIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-run-icon.png");
      var trayMenuStopIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-stop-icon.png");
      var trayMenuSettingIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-settings-icon.png");
      var trayMenuAboutIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-about-icon.png");
      var trayMenuExitIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-exit-icon.png");

      var settingsWindowOptions = new BrowserWindowOptions
      {
        MinWidth = 800,
        MinHeight = 400,
        Width = 800,
        Height = 400,
        Frame = false,
        Icon = trayMenuSettingIcon,
        //        BackgroundColor = "#FF080F17",
        Fullscreenable = false,
        Center = true
      };

      _runServerMenuItem = new MenuItem
      {
        Label = _runServerMenuItemLabelFunc(_localizer),
        Icon = trayMenuRunIcon
      };
      _stopServerMenuItem = new MenuItem
      {
        Label = _stopServerMenuItemLabelFunc(_localizer),
        Icon = trayMenuStopIcon,
        Visible = false,
        Enabled = false
      };
      _runServerMenuItem.Click = async () =>
      {
        await _mqttServerService.StartAsync();
        _stopServerMenuItem.Visible = true;
        _stopServerMenuItem.Enabled = true;
        _runServerMenuItem.Visible = false;
        _runServerMenuItem.Enabled = false;
        Electron.Tray.SetImage(Path.Combine(_env.ContentRootPath, TrayRunIcon));
      };
      _stopServerMenuItem.Click = async () =>
      {
        await _mqttServerService.StopAsync();
        _runServerMenuItem.Visible = true;
        _runServerMenuItem.Enabled = true;
        _stopServerMenuItem.Visible = false;
        _stopServerMenuItem.Enabled = false;
        Electron.Tray.SetImage(Path.Combine(_env.ContentRootPath, TrayStopIcon));
      };
      _settingsMenuItem = new MenuItem
      {
        Label = _settingsMenuItemLabelFunc(_localizer),
        Icon = trayMenuSettingIcon,
        Click = async () =>
        {
          _settingsWindow = await Electron.WindowManager.CreateWindowAsync(settingsWindowOptions);
          _settingsWindow.Center();
        }
      };
      _aboutMenuItem = new MenuItem
      {
        Label = _aboutMenuItemLabelFunc(_localizer),
        Icon = trayMenuAboutIcon
      };
      _exitMenuItem = new MenuItem
      {
        Label = _exitMenuItemLabelFunc(_localizer),
        Icon = trayMenuExitIcon,
        Click = () => Electron.App.Quit()
      };
      var menuItems = new[]
      {
        _runServerMenuItem,
        _stopServerMenuItem,
        _settingsMenuItem,
        _aboutMenuItem,
        new MenuItem
        {
          Type = MenuType.separator
        },
        _exitMenuItem
      };

      return menuItems;
    }


    public void Desktopize()
    {
      var culture = GetCultureFromDb();
      UpdateMenuItems(culture);
      Electron.WindowManager.IsQuitOnWindowAllClosed = false;
      Electron.Tray.Show(TrayStopIcon, _menuItems);
    }


    public void CloseSettingsWindow()
    {
      _settingsWindow?.Close();
    }


    public void UpdateCulture(string culture)
    {
      UpdateMenuItems(culture);
      Electron.Tray.Destroy();
      Electron.Tray.Show(TrayStopIcon, _menuItems);
    }


    private void UpdateMenuItems(string culture)
    {
      var cultureInfo = new CultureInfo(culture);
      var localizer = _localizer.WithCulture(cultureInfo);
      _runServerMenuItem.Label = _runServerMenuItemLabelFunc(localizer);
      _stopServerMenuItem.Label = _stopServerMenuItemLabelFunc(localizer);
      _settingsMenuItem.Label = _settingsMenuItemLabelFunc(localizer);
      _aboutMenuItem.Label = _aboutMenuItemLabelFunc(localizer);
      _exitMenuItem.Label = _exitMenuItemLabelFunc(localizer);
    }


    private string GetCultureFromDb()
    {
      var database = _serviceProvider.CreateScope().ServiceProvider.GetService<DatabaseService>();
      var settings = database.Settings.FirstOrDefault();
      return settings?.Language ?? "en-US";
    }
  }
}

The Desktopize method is called from Startup Configure method.
The public UpdateCulture method is called from the settings controller when the culture is changed.
So I do this trick: destroy tray and then create it again with updated menu items. I guess it will be very useful to have such functionality, for example

Electron.Tray.UpdateMenuItem("menu_item_id", menuItem => 
{
  menuItem.Label = "changed_label",
  Enabled = false,
  Click = () => SomeNewBehavior()
});

Thanks!

Originally created by @Tum4ik on GitHub (Jun 1, 2019). Originally assigned to: @GregorBiswanger on GitHub. In my case it is related to the application localization, but it could be also useful for some other scenarios (like change item visibility or enable/disable item). Currently I have this Desktoper class to bring the app to the desktop. ```csharp using System; using System.Globalization; using System.IO; using System.Linq; using ElectronNET.API; using ElectronNET.API.Entities; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Tum4ik.RemoteControl.Server.Services; using Tum4ik.RemoteControl.Server.Services.MQTT.MqttServer; namespace Tum4ik.RemoteControl.Server.Desktop { /// <inheritdoc /> public class Desktoper : IDesktoper { private const string TrayRunIcon = "Desktop/icons/tray-run-icon.png"; private const string TrayStopIcon = "Desktop/icons/tray-stop-icon.png"; private readonly IServiceProvider _serviceProvider; private readonly IHostingEnvironment _env; private readonly IStringLocalizer<Desktoper> _localizer; private readonly IMqttServerService _mqttServerService; private BrowserWindow _settingsWindow; private readonly MenuItem[] _menuItems; public Desktoper(IServiceProvider serviceProvider, IHostingEnvironment env, IStringLocalizer<Desktoper> localizer, IMqttServerService mqttServerService) { _serviceProvider = serviceProvider; _env = env; _localizer = localizer; _mqttServerService = mqttServerService; _menuItems = CreateMenuItems(); } private MenuItem _runServerMenuItem; private MenuItem _stopServerMenuItem; private MenuItem _settingsMenuItem; private MenuItem _aboutMenuItem; private MenuItem _exitMenuItem; private readonly Func<IStringLocalizer, string> _runServerMenuItemLabelFunc = localizer => localizer["Run Server"]; private readonly Func<IStringLocalizer, string> _stopServerMenuItemLabelFunc = localizer => localizer["Stop Server"]; private readonly Func<IStringLocalizer, string> _settingsMenuItemLabelFunc = localizer => localizer["Settings"]; private readonly Func<IStringLocalizer, string> _aboutMenuItemLabelFunc = localizer => localizer["About"]; private readonly Func<IStringLocalizer, string> _exitMenuItemLabelFunc = localizer => localizer["Exit"]; private MenuItem[] CreateMenuItems() { var trayMenuRunIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-run-icon.png"); var trayMenuStopIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-stop-icon.png"); var trayMenuSettingIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-settings-icon.png"); var trayMenuAboutIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-about-icon.png"); var trayMenuExitIcon = Path.Combine(_env.ContentRootPath, "Desktop/icons/tray-menu-exit-icon.png"); var settingsWindowOptions = new BrowserWindowOptions { MinWidth = 800, MinHeight = 400, Width = 800, Height = 400, Frame = false, Icon = trayMenuSettingIcon, // BackgroundColor = "#FF080F17", Fullscreenable = false, Center = true }; _runServerMenuItem = new MenuItem { Label = _runServerMenuItemLabelFunc(_localizer), Icon = trayMenuRunIcon }; _stopServerMenuItem = new MenuItem { Label = _stopServerMenuItemLabelFunc(_localizer), Icon = trayMenuStopIcon, Visible = false, Enabled = false }; _runServerMenuItem.Click = async () => { await _mqttServerService.StartAsync(); _stopServerMenuItem.Visible = true; _stopServerMenuItem.Enabled = true; _runServerMenuItem.Visible = false; _runServerMenuItem.Enabled = false; Electron.Tray.SetImage(Path.Combine(_env.ContentRootPath, TrayRunIcon)); }; _stopServerMenuItem.Click = async () => { await _mqttServerService.StopAsync(); _runServerMenuItem.Visible = true; _runServerMenuItem.Enabled = true; _stopServerMenuItem.Visible = false; _stopServerMenuItem.Enabled = false; Electron.Tray.SetImage(Path.Combine(_env.ContentRootPath, TrayStopIcon)); }; _settingsMenuItem = new MenuItem { Label = _settingsMenuItemLabelFunc(_localizer), Icon = trayMenuSettingIcon, Click = async () => { _settingsWindow = await Electron.WindowManager.CreateWindowAsync(settingsWindowOptions); _settingsWindow.Center(); } }; _aboutMenuItem = new MenuItem { Label = _aboutMenuItemLabelFunc(_localizer), Icon = trayMenuAboutIcon }; _exitMenuItem = new MenuItem { Label = _exitMenuItemLabelFunc(_localizer), Icon = trayMenuExitIcon, Click = () => Electron.App.Quit() }; var menuItems = new[] { _runServerMenuItem, _stopServerMenuItem, _settingsMenuItem, _aboutMenuItem, new MenuItem { Type = MenuType.separator }, _exitMenuItem }; return menuItems; } public void Desktopize() { var culture = GetCultureFromDb(); UpdateMenuItems(culture); Electron.WindowManager.IsQuitOnWindowAllClosed = false; Electron.Tray.Show(TrayStopIcon, _menuItems); } public void CloseSettingsWindow() { _settingsWindow?.Close(); } public void UpdateCulture(string culture) { UpdateMenuItems(culture); Electron.Tray.Destroy(); Electron.Tray.Show(TrayStopIcon, _menuItems); } private void UpdateMenuItems(string culture) { var cultureInfo = new CultureInfo(culture); var localizer = _localizer.WithCulture(cultureInfo); _runServerMenuItem.Label = _runServerMenuItemLabelFunc(localizer); _stopServerMenuItem.Label = _stopServerMenuItemLabelFunc(localizer); _settingsMenuItem.Label = _settingsMenuItemLabelFunc(localizer); _aboutMenuItem.Label = _aboutMenuItemLabelFunc(localizer); _exitMenuItem.Label = _exitMenuItemLabelFunc(localizer); } private string GetCultureFromDb() { var database = _serviceProvider.CreateScope().ServiceProvider.GetService<DatabaseService>(); var settings = database.Settings.FirstOrDefault(); return settings?.Language ?? "en-US"; } } } ``` The `Desktopize` method is called from Startup `Configure` method. The public `UpdateCulture` method is called from the settings controller when the culture is changed. So I do this trick: destroy tray and then create it again with updated menu items. I guess it will be very useful to have such functionality, for example ```csharp Electron.Tray.UpdateMenuItem("menu_item_id", menuItem => { menuItem.Label = "changed_label", Enabled = false, Click = () => SomeNewBehavior() }); ``` Thanks!
claunia added the Feature label 2026-01-29 16:36:54 +00:00
Author
Owner

@GregorBiswanger commented on GitHub (May 11, 2020):

It is now possible to simply change the existing menu. To do this, the existing menu must be set again. Available in Electron.NET 8.31.2.

var menu = new MenuItem[]
{
    new MenuItem
    {
        Label = "Hello",
        Click = () =>
        {
            Electron.Menu.ContextMenuItems[2][2].Enabled = !Electron.Menu.ContextMenuItems[2][2].Enabled;
            var mainWindow = Electron.WindowManager.BrowserWindows.FirstOrDefault();
            Electron.Menu.SetContextMenu(mainWindow, Electron.Menu.ContextMenuItems[2].ToArray());
        }
    },
    new MenuItem { Type = MenuType.separator },
    new MenuItem { Label = "Electron.NET", Type = MenuType.checkbox, Checked = true, Enabled = false }
};

var mainWindow = Electron.WindowManager.BrowserWindows.FirstOrDefault();
if (mainWindow == null) return;

Electron.Menu.SetContextMenu(mainWindow, menu);

electron-update-context-menu

@GregorBiswanger commented on GitHub (May 11, 2020): It is now possible to simply change the existing menu. To do this, the existing menu must be set again. Available in **Electron.NET 8.31.2**. ``` var menu = new MenuItem[] { new MenuItem { Label = "Hello", Click = () => { Electron.Menu.ContextMenuItems[2][2].Enabled = !Electron.Menu.ContextMenuItems[2][2].Enabled; var mainWindow = Electron.WindowManager.BrowserWindows.FirstOrDefault(); Electron.Menu.SetContextMenu(mainWindow, Electron.Menu.ContextMenuItems[2].ToArray()); } }, new MenuItem { Type = MenuType.separator }, new MenuItem { Label = "Electron.NET", Type = MenuType.checkbox, Checked = true, Enabled = false } }; var mainWindow = Electron.WindowManager.BrowserWindows.FirstOrDefault(); if (mainWindow == null) return; Electron.Menu.SetContextMenu(mainWindow, menu); ``` ![electron-update-context-menu](https://user-images.githubusercontent.com/7336300/81586599-01b21380-93b6-11ea-9aa7-ce4d4ad9ff6c.gif)
Author
Owner

@GregorBiswanger commented on GitHub (May 11, 2020):

For the tray menu:

var menu = new MenuItem[]
{
    new MenuItem
    {
        Label = "Remove",
        Click = () =>
        {
            var menu = Electron.Tray.MenuItems.ToArray();
            menu[1].Enabled = !menu[1].Enabled;

            Electron.Tray.Destroy();
            Electron.Tray.Show(Path.Combine(_env.ContentRootPath, "Assets/electron_32x32.png"), menu);
        }
    },
    new MenuItem
    {
        Label = "Hello World",
        Enabled = false,
        Click = async () => await Electron.Dialog.ShowMessageBoxAsync("Test")
    }
};

Electron.Tray.Show(Path.Combine(_env.ContentRootPath, "Assets/electron_32x32.png"), menu);
Electron.Tray.SetToolTip("Electron Demo in the tray.");

electron-net-update-tray-menu

@GregorBiswanger commented on GitHub (May 11, 2020): **For the tray menu:** ``` var menu = new MenuItem[] { new MenuItem { Label = "Remove", Click = () => { var menu = Electron.Tray.MenuItems.ToArray(); menu[1].Enabled = !menu[1].Enabled; Electron.Tray.Destroy(); Electron.Tray.Show(Path.Combine(_env.ContentRootPath, "Assets/electron_32x32.png"), menu); } }, new MenuItem { Label = "Hello World", Enabled = false, Click = async () => await Electron.Dialog.ShowMessageBoxAsync("Test") } }; Electron.Tray.Show(Path.Combine(_env.ContentRootPath, "Assets/electron_32x32.png"), menu); Electron.Tray.SetToolTip("Electron Demo in the tray."); ``` ![electron-net-update-tray-menu](https://user-images.githubusercontent.com/7336300/81601935-05509500-93cc-11ea-809c-f5262dd747e8.gif)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/Electron.NET#337