Overview

BeepMenuBar is a sophisticated application menu bar control that provides traditional desktop application navigation with modern styling and features. It supports hierarchical menu structures, popup menus, custom icons, and seamless integration with application functionality through method binding.

Built on the BeepControl foundation, BeepMenuBar features automatic layout, intelligent sizing, popup management, theme integration, and support for complex menu hierarchies. Perfect for desktop applications, administrative interfaces, and professional software requiring traditional menu navigation.

Key Features

?? Hierarchical Menus

Support for multi-level menu structures with nested submenus and separators

??? Icon Integration

Custom icons for menu items with automatic sizing and theme-aware coloring

?? Smart Layout

Automatic button sizing and positioning with preferred size calculation

?? Popup Management

Advanced popup form handling with state tracking and automatic cleanup

? Method Binding

Direct method execution through global function handlers and dependency injection

?? Theme Integration

Complete theme support with menu-specific styling and hover effects

Visual Preview

BeepMenuBar with File menu expanded showing hierarchical structure

Basic Usage

Simple Menu Bar

// Create a basic menu bar
var menuBar = new BeepMenuBar
{
    Dock = DockStyle.Top,
    Height = 35,
    MenuItemWidth = 80,
    MenuItemHeight = 35
};

// Create menu items
var fileMenu = new SimpleItem
{
    Text = "File",
    ImagePath = "file-icon.svg",
    GuidId = Guid.NewGuid().ToString()
};

var editMenu = new SimpleItem
{
    Text = "Edit", 
    ImagePath = "edit-icon.svg",
    GuidId = Guid.NewGuid().ToString()
};

// Add to menu bar
menuBar.MenuItems.Add(fileMenu);
menuBar.MenuItems.Add(editMenu);

// Handle selection
menuBar.SelectedItemChanged += (sender, e) =>
{
    MessageBox.Show($"Selected: {e.SelectedItem.Text}");
};

this.Controls.Add(menuBar);

Hierarchical Menu Structure

// Create File menu with submenus
var fileMenu = new SimpleItem
{
    Text = "File",
    ImagePath = "folder-icon.svg",
    GuidId = Guid.NewGuid().ToString()
};

// Add File submenu items
fileMenu.Children.Add(new SimpleItem
{
    Text = "New",
    ImagePath = "new-file-icon.svg",
    MethodName = "CreateNewFile",
    GuidId = Guid.NewGuid().ToString()
});

fileMenu.Children.Add(new SimpleItem
{
    Text = "Open",
    ImagePath = "open-file-icon.svg",
    MethodName = "OpenFile",
    GuidId = Guid.NewGuid().ToString()
});

// Add separator
fileMenu.Children.Add(new SimpleItem
{
    Text = "-",
    IsSeparator = true,
    GuidId = Guid.NewGuid().ToString()
});

// Recent files submenu
var recentMenu = new SimpleItem
{
    Text = "Recent Files",
    ImagePath = "recent-icon.svg",
    GuidId = Guid.NewGuid().ToString()
};

// Add recent file items dynamically
for (int i = 1; i <= 5; i++)
{
    recentMenu.Children.Add(new SimpleItem
    {
        Text = $"Document{i}.txt",
        MethodName = "OpenRecentFile",
        Tag = $"document{i}.txt",
        GuidId = Guid.NewGuid().ToString()
    });
}

fileMenu.Children.Add(recentMenu);

// Add more File menu items
fileMenu.Children.Add(new SimpleItem
{
    Text = "Save",
    ImagePath = "save-icon.svg",
    MethodName = "SaveFile",
    Shortcut = "Ctrl+S",
    GuidId = Guid.NewGuid().ToString()
});

fileMenu.Children.Add(new SimpleItem
{
    Text = "Exit",
    ImagePath = "exit-icon.svg",
    MethodName = "ExitApplication",
    GuidId = Guid.NewGuid().ToString()
});

menuBar.MenuItems.Add(fileMenu);

Complete Application Menu

public void SetupApplicationMenu()
{
    var menuBar = new BeepMenuBar
    {
        Dock = DockStyle.Top,
        Height = 35,
        ImageSize = 16
    };

    // File Menu
    var fileMenu = CreateFileMenu();
    menuBar.MenuItems.Add(fileMenu);

    // Edit Menu
    var editMenu = CreateEditMenu();
    menuBar.MenuItems.Add(editMenu);

    // View Menu
    var viewMenu = CreateViewMenu();
    menuBar.MenuItems.Add(viewMenu);

    // Tools Menu
    var toolsMenu = CreateToolsMenu();
    menuBar.MenuItems.Add(toolsMenu);

    // Help Menu
    var helpMenu = CreateHelpMenu();
    menuBar.MenuItems.Add(helpMenu);

    this.Controls.Add(menuBar);
}

private SimpleItem CreateFileMenu()
{
    var fileMenu = new SimpleItem
    {
        Text = "File",
        ImagePath = "file-menu-icon.svg",
        GuidId = Guid.NewGuid().ToString()
    };

    fileMenu.Children.AddRange(new[]
    {
        new SimpleItem { Text = "New", ImagePath = "new.svg", MethodName = "NewDocument", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Open", ImagePath = "open.svg", MethodName = "OpenDocument", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Save", ImagePath = "save.svg", MethodName = "SaveDocument", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Save As", ImagePath = "save-as.svg", MethodName = "SaveDocumentAs", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Print", ImagePath = "print.svg", MethodName = "PrintDocument", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Exit", ImagePath = "exit.svg", MethodName = "ExitApplication", GuidId = Guid.NewGuid().ToString() }
    });

    return fileMenu;
}

private SimpleItem CreateEditMenu()
{
    var editMenu = new SimpleItem
    {
        Text = "Edit",
        ImagePath = "edit-menu-icon.svg",
        GuidId = Guid.NewGuid().ToString()
    };

    editMenu.Children.AddRange(new[]
    {
        new SimpleItem { Text = "Undo", ImagePath = "undo.svg", MethodName = "Undo", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Redo", ImagePath = "redo.svg", MethodName = "Redo", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Cut", ImagePath = "cut.svg", MethodName = "Cut", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Copy", ImagePath = "copy.svg", MethodName = "Copy", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Paste", ImagePath = "paste.svg", MethodName = "Paste", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Find", ImagePath = "find.svg", MethodName = "Find", GuidId = Guid.NewGuid().ToString() },
        new SimpleItem { Text = "Replace", ImagePath = "replace.svg", MethodName = "Replace", GuidId = Guid.NewGuid().ToString() }
    });

    return editMenu;
}

Properties

Core Properties

Property Type Default Description
MenuItems BindingList<SimpleItem> Empty Collection of top-level menu items
SelectedItem SimpleItem null Currently selected menu item (read-only)
SelectedIndex int -1 Index of currently selected menu item

Layout Properties

Property Type Default Description
MenuItemWidth int 60 Default width for menu items (pixels)
MenuItemHeight int 35 Height of menu items (pixels)
ImageSize int 20 Size of menu item icons (pixels)
TextFont Font Arial, 10pt Font used for menu item text

State Properties

Property Type Description
ActivePopupForm BeepPopupForm Currently active popup menu form
ActiveMenuButton BeepButton Currently active menu button
CurrenItemButton BeepButton Button for the current menu item

SimpleItem Structure

Property Type Description
Text string Display text for the menu item
ImagePath string Path to icon image file
MethodName string Method to execute when item is clicked
Children List<SimpleItem> Submenu items
GuidId string Unique identifier for the menu item
Tag object Additional data associated with the item
IsSeparator bool Whether the item is a menu separator

Events

Event Type Description
SelectedItemChanged EventHandler<SelectedItemChangedEventArgs> Raised when a menu item is selected

SelectedItemChangedEventArgs

Property Type Description
SelectedItem SimpleItem The menu item that was selected

Examples

Document Editor Application

public partial class DocumentEditor : Form
{
    private BeepMenuBar menuBar;
    private RichTextBox textEditor;
    private string currentFileName;

    public DocumentEditor()
    {
        InitializeComponent();
        SetupMenuBar();
        SetupEditor();
    }

    private void SetupMenuBar()
    {
        menuBar = new BeepMenuBar
        {
            Dock = DockStyle.Top,
            Height = 30,
            MenuItemHeight = 30,
            ImageSize = 16
        };

        // Setup menu structure
        CreateFileMenu();
        CreateEditMenu();
        CreateFormatMenu();
        CreateHelpMenu();

        menuBar.SelectedItemChanged += MenuBar_SelectedItemChanged;
        this.Controls.Add(menuBar);
    }

    private void CreateFileMenu()
    {
        var fileMenu = new SimpleItem
        {
            Text = "File",
            ImagePath = "file.svg",
            GuidId = Guid.NewGuid().ToString()
        };

        fileMenu.Children.AddRange(new[]
        {
            new SimpleItem 
            { 
                Text = "New", 
                ImagePath = "new.svg", 
                MethodName = "NewDocument",
                Shortcut = "Ctrl+N",
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "Open", 
                ImagePath = "open.svg", 
                MethodName = "OpenDocument",
                Shortcut = "Ctrl+O",
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "-", 
                IsSeparator = true, 
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "Save", 
                ImagePath = "save.svg", 
                MethodName = "SaveDocument",
                Shortcut = "Ctrl+S",
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "Save As...", 
                ImagePath = "save-as.svg", 
                MethodName = "SaveDocumentAs",
                Shortcut = "Ctrl+Shift+S",
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "-", 
                IsSeparator = true, 
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "Print", 
                ImagePath = "print.svg", 
                MethodName = "PrintDocument",
                Shortcut = "Ctrl+P",
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "-", 
                IsSeparator = true, 
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "Exit", 
                ImagePath = "exit.svg", 
                MethodName = "ExitApplication",
                GuidId = Guid.NewGuid().ToString() 
            }
        });

        menuBar.MenuItems.Add(fileMenu);
    }

    private void MenuBar_SelectedItemChanged(object sender, SelectedItemChangedEventArgs e)
    {
        var item = e.SelectedItem;
        if (item?.MethodName != null)
        {
            ExecuteMenuAction(item.MethodName, item);
        }
    }

    private void ExecuteMenuAction(string methodName, SimpleItem item)
    {
        switch (methodName)
        {
            case "NewDocument":
                NewDocument();
                break;
            case "OpenDocument":
                OpenDocument();
                break;
            case "SaveDocument":
                SaveDocument();
                break;
            case "SaveDocumentAs":
                SaveDocumentAs();
                break;
            case "PrintDocument":
                PrintDocument();
                break;
            case "ExitApplication":
                ExitApplication();
                break;
            // Add more cases for other menu actions
        }
    }

    private void NewDocument()
    {
        if (CheckForUnsavedChanges())
        {
            textEditor.Clear();
            currentFileName = null;
            this.Text = "Document Editor - New Document";
        }
    }

    private void OpenDocument()
    {
        if (CheckForUnsavedChanges())
        {
            using var openDialog = new OpenFileDialog
            {
                Filter = "Text Files (*.txt)|*.txt|Rich Text Files (*.rtf)|*.rtf|All Files (*.*)|*.*",
                Title = "Open Document"
            };

            if (openDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    textEditor.LoadFile(openDialog.FileName);
                    currentFileName = openDialog.FileName;
                    this.Text = $"Document Editor - {Path.GetFileName(currentFileName)}";
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Error opening file: {ex.Message}", "Error",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
    }

    private void SaveDocument()
    {
        if (string.IsNullOrEmpty(currentFileName))
        {
            SaveDocumentAs();
        }
        else
        {
            try
            {
                textEditor.SaveFile(currentFileName);
                this.Text = $"Document Editor - {Path.GetFileName(currentFileName)}";
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error saving file: {ex.Message}", "Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }

    private bool CheckForUnsavedChanges()
    {
        // Implementation for checking unsaved changes
        // Return true if it's safe to proceed
        return true;
    }
}

Dynamic Menu Creation

public class DynamicMenuBuilder
{
    private BeepMenuBar menuBar;
    private List plugins;

    public DynamicMenuBuilder(BeepMenuBar menuBar)
    {
        this.menuBar = menuBar;
        LoadPlugins();
    }

    public void BuildDynamicMenus()
    {
        // Create standard menus
        CreateStaticMenus();

        // Create plugin menus
        CreatePluginMenus();

        // Create recent files menu
        CreateRecentFilesMenu();

        // Create window menu
        CreateWindowMenu();
    }

    private void CreatePluginMenus()
    {
        if (plugins?.Count > 0)
        {
            var pluginsMenu = new SimpleItem
            {
                Text = "Plugins",
                ImagePath = "plugins.svg",
                GuidId = Guid.NewGuid().ToString()
            };

            foreach (var plugin in plugins)
            {
                var pluginItem = new SimpleItem
                {
                    Text = plugin.Name,
                    ImagePath = plugin.IconPath,
                    MethodName = "ExecutePlugin",
                    Tag = plugin,
                    GuidId = Guid.NewGuid().ToString()
                };

                // Add plugin-specific menu items
                foreach (var action in plugin.Actions)
                {
                    pluginItem.Children.Add(new SimpleItem
                    {
                        Text = action.Name,
                        ImagePath = action.IconPath,
                        MethodName = "ExecutePluginAction",
                        Tag = action,
                        GuidId = Guid.NewGuid().ToString()
                    });
                }

                pluginsMenu.Children.Add(pluginItem);
            }

            menuBar.MenuItems.Add(pluginsMenu);
        }
    }

    private void CreateRecentFilesMenu()
    {
        var recentFiles = GetRecentFiles();
        if (recentFiles?.Count > 0)
        {
            // Find File menu and add Recent Files submenu
            var fileMenu = menuBar.MenuItems.FirstOrDefault(m => m.Text == "File");
            if (fileMenu != null)
            {
                var recentMenu = new SimpleItem
                {
                    Text = "Recent Files",
                    ImagePath = "recent.svg",
                    GuidId = Guid.NewGuid().ToString()
                };

                foreach (var recentFile in recentFiles.Take(10))
                {
                    recentMenu.Children.Add(new SimpleItem
                    {
                        Text = Path.GetFileName(recentFile),
                        MethodName = "OpenRecentFile",
                        Tag = recentFile,
                        ToolTip = recentFile,
                        GuidId = Guid.NewGuid().ToString()
                    });
                }

                // Insert after "Open" menu item
                var openIndex = fileMenu.Children.FindIndex(c => c.Text == "Open");
                if (openIndex >= 0)
                {
                    fileMenu.Children.Insert(openIndex + 1, recentMenu);
                }
            }
        }
    }

    private void CreateWindowMenu()
    {
        var windowMenu = new SimpleItem
        {
            Text = "Window",
            ImagePath = "window.svg",
            GuidId = Guid.NewGuid().ToString()
        };

        // Add window management items
        windowMenu.Children.AddRange(new[]
        {
            new SimpleItem 
            { 
                Text = "Minimize", 
                MethodName = "MinimizeWindow",
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "Maximize", 
                MethodName = "MaximizeWindow",
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "-", 
                IsSeparator = true, 
                GuidId = Guid.NewGuid().ToString() 
            },
            new SimpleItem 
            { 
                Text = "Always on Top", 
                MethodName = "ToggleAlwaysOnTop",
                CheckBox = true,
                GuidId = Guid.NewGuid().ToString() 
            }
        });

        // Add list of open documents
        var openDocuments = GetOpenDocuments();
        if (openDocuments?.Count > 0)
        {
            windowMenu.Children.Add(new SimpleItem 
            { 
                Text = "-", 
                IsSeparator = true, 
                GuidId = Guid.NewGuid().ToString() 
            });

            foreach (var doc in openDocuments)
            {
                windowMenu.Children.Add(new SimpleItem
                {
                    Text = doc.Title,
                    MethodName = "ActivateDocument",
                    Tag = doc,
                    RadioButton = true,
                    Checked = doc.IsActive,
                    GuidId = Guid.NewGuid().ToString()
                });
            }
        }

        menuBar.MenuItems.Add(windowMenu);
    }

    private List GetRecentFiles()
    {
        // Implementation to retrieve recent files from settings
        return new List();
    }

    private List GetOpenDocuments()
    {
        // Implementation to get list of open documents
        return new List();
    }

    private void LoadPlugins()
    {
        // Implementation to load available plugins
        plugins = new List();
    }
}

public class Plugin
{
    public string Name { get; set; }
    public string IconPath { get; set; }
    public List Actions { get; set; } = new List();
}

public class PluginAction
{
    public string Name { get; set; }
    public string IconPath { get; set; }
    public string MethodName { get; set; }
}

public class DocumentInfo
{
    public string Title { get; set; }
    public bool IsActive { get; set; }
}

Theming

BeepMenuBar provides comprehensive theming with specialized menu styling and proper cascade to all child buttons:

Theme Application

var menuBar = new BeepMenuBar
{
    Theme = "DarkTheme",
    UseThemeFont = true
};

// Theme automatically applies menu-specific colors:
// - MenuBackColor: Main menu bar background
// - MenuForeColor: Menu text color
// - MenuBorderColor: Border colors
// - MenuItemHoverBackColor: Hover effects
// - MenuItemSelectedBackColor: Selected states

// Manual theme application
menuBar.ApplyTheme();

Menu-Specific Theme Properties

  • MenuBackColor - Menu bar background color
  • MenuForeColor - Menu item text color
  • MenuBorderColor - Menu borders and separators
  • MenuItemHoverBackColor - Hover state background
  • MenuItemSelectedBackColor - Selected item background
  • MenuGradiantStartColor/EndColor - Gradient backgrounds
  • MenuTitleFont - Typography for menu items

Custom Menu Styling

// Create custom menu theme
menuBar.Theme = "CustomMenuTheme";

// Override specific menu colors after theme application
foreach (Control control in menuBar.Controls)
{
    if (control is BeepButton menuButton)
    {
        menuButton.HoverBackColor = Color.FromArgb(45, 45, 48);
        menuButton.SelectedBackColor = Color.FromArgb(0, 122, 204);
        menuButton.ForeColor = Color.White;
    }
}

// Enable gradient background
menuBar.UseGradientBackground = true;
menuBar.GradientStartColor = Color.FromArgb(60, 60, 60);
menuBar.GradientEndColor = Color.FromArgb(40, 40, 40);

Button State Theming

Each menu button supports comprehensive state-based theming:

  • Normal state - Default appearance
  • Hover state - Mouse over effects
  • Selected state - Active menu button
  • Pressed state - Click feedback
  • Disabled state - Inactive menu items
  • Focus state - Keyboard navigation

Best Practices

?? Menu Structure

  • Logical grouping: Group related functionality in the same menu
  • Standard conventions: Follow platform conventions (File, Edit, View, Help)
  • Separators: Use separators to group related items within menus
  • Depth limit: Limit submenu depth to 3 levels maximum

?? Visual Design

  • Consistent icons: Use consistent icon style and sizing throughout
  • Clear text: Use clear, descriptive menu item names
  • Keyboard shortcuts: Display keyboard shortcuts in submenus
  • Visual feedback: Provide clear hover and selection states

? Performance

  • Lazy loading: Load submenus only when needed
  • Image optimization: Use appropriately sized icon images
  • Memory management: Dispose of popup forms properly
  • Method binding: Use efficient method resolution

?? Implementation

  • Method names: Use clear, descriptive method names for actions
  • Error handling: Implement proper error handling for menu actions
  • State management: Track and update menu item states appropriately
  • Accessibility: Support keyboard navigation and screen readers
?? Design Tip

Place the most commonly used menu items in the File and Edit menus, and use standard keyboard shortcuts (Ctrl+N, Ctrl+O, Ctrl+S) for familiar actions.

? Success Pattern

Implement a consistent method naming convention and use the global function handler system for clean separation of menu logic from UI code.

?? Memory Warning

Be careful with popup form management. Ensure proper disposal of popup forms to prevent memory leaks, especially in applications with frequent menu interactions.