BeepMenuBar
Professional application menu bar with hierarchical menus and popup support
?? Table of Contents
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
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.
Implement a consistent method naming convention and use the global function handler system for clean separation of menu logic from UI code.
Be careful with popup form management. Ensure proper disposal of popup forms to prevent memory leaks, especially in applications with frequent menu interactions.