1

I am using Blazor Server. Inside my startup I am configuring the MenuItems like this:

var menu = new[]
{
    new MenuItem
    {
        Label = "File", Submenu = new[]
        {
            new MenuItem
            {
                Label = "Save", 
                Accelerator = "CmdOrCtrl+S", 
                Enabled = false,
                Click = () =>
                {
                    // How do I execute code inside my component?
                },
            }
        }
    }
};

Electron.Menu.SetApplicationMenu(menu);

My component lives on the root and is always rendered. It has a simple method called Save which I want to call.

public async Task SaveProject()
{
    await Project.Save();
}

Isn't there some kind of event inside the static Electron class that I could use? Something like OnMenuItemClicked.

Having a static property that I could access inside my component would not only be bad design, it would prevent me from accessing any instance properties.

Marco Siffert
  • 474
  • 1
  • 4
  • 14

1 Answers1

0

I came up with a more or less applicable solution myself. I have created a singleton service IMenuItemService which I am using in both my Startup and my Component. Since MenuItems do not have an ID per se, I created a seperate Enum MenuItemType to seperate them. The service looks something like this:

public class MenuItemService : IMenuItemService
{
    public Action<MenuItemType> MenuItemClicked { get; set; }

    private Dictionary<MenuItemType, MenuItem> ConfigurableMenuItems { get; }

    public MenuItemService()
    {
        ConfigurableMenuItems = new Dictionary<MenuItemType, MenuItem>();
        
        InitializeMenuItem(MenuItemType.Close, "Close Project", null, false);
    }

    private void InitializeMenuItem(MenuItemType type, string label, string accelerator, bool enabled)
    {
        ConfigurableMenuItems.Add(type, new MenuItem
        {
            Label = label,
            Accelerator = accelerator,
            Enabled = enabled,
            Click = () => { MenuItemClicked?.Invoke(type); },
        });
    }

    public void SetEnabled(MenuItemType menuItemType)
    {
        ConfigurableMenuItems[menuItemType].Enabled = true;
        RenderMenuItems();
    }
    
    public void SetDisabled(MenuItemType menuItemType)
    {
        ConfigurableMenuItems[menuItemType].Enabled = false;
        RenderMenuItems();
    }

    public void RenderMenuItems()
    {
        Electron.Menu.SetApplicationMenu(new[]
        {
            new MenuItem
            {
                Label = "File", Submenu = new []
                {
                    ConfigurableMenuItems[MenuItemType.Close]
                }
            }
        });
    }
}

With this approach I can call menuItemService.RenderMenuItems() from anywhere in my application including Startup.cs while in my Components I am setting the MenuItemClicked Action in order to listen to clicks.

[Inject]
public IMenuItemService MenuItemService { get; set; }

private void InitializeMenuItemActions()
{
    MenuItemService.SetEnabled(MenuItemType.Close);
    
    MenuItemService.MenuItemClicked = type =>
    {
        if (type == MenuItemType.Close)
        {
            ProjectManager.CloseProject();
            NavigationManager.NavigateTo("/");
        }
    };
}

In my case I intentionally used an Action Property instead on an EventHandler since I do not need multiple listeners for my MenuItem.

Marco Siffert
  • 474
  • 1
  • 4
  • 14