BeepFlyoutMenu
Versatile flyout menu control with multi-directional sliding and animated interactions for modern navigation systems
?? Table of Contents
Overview
BeepFlyoutMenu provides a sophisticated flyout menu control that combines a labeled trigger button with a sliding menu panel that can appear from multiple directions. It offers seamless integration with parent forms, intelligent positioning, and smooth animations for creating modern, space-efficient navigation interfaces.
Built on the BeepControl foundation, BeepFlyoutMenu features multi-directional sliding (left, right, bottom), configurable label positioning, automatic form integration, intelligent collision detection, and comprehensive theme support. Perfect for toolbars, sidebar navigation, contextual menus, and space-constrained interface layouts.
Key Features
?? Multi-Directional Flyouts
Slide menus from left, right, or bottom with intelligent positioning and collision detection
?? Configurable Layout
Customizable label and button positioning with flexible sizing and alignment options
?? Integrated Label
Built-in label control with customizable text, fonts, and positioning relative to the trigger button
?? Smart Form Integration
Automatic parent form detection and menu placement for proper layering and positioning
?? Dynamic Icons
Automatic icon switching based on flyout direction and expansion state
? Performance Optimized
Efficient menu management with proper cleanup and resource handling
Visual Preview
Flyout Directions
Bottom Flyout
Menu slides down from trigger
Right Flyout
Menu slides right from trigger
Left Flyout
Menu slides left from trigger
BeepFlyoutMenu supporting multiple slide directions with dynamic icon updates
Toolbar Integration
Toolbar integration showing File menu flyout with keyboard shortcuts
Basic Usage
Simple Flyout Menu
// Create a BeepFlyoutMenu
var flyoutMenu = new BeepFlyoutMenu();
flyoutMenu.Text = "Navigation";
flyoutMenu.Size = new Size(120, 25);
flyoutMenu.Location = new Point(20, 20);
flyoutMenu.FlyoutDirection = SlideDirection.Bottom;
flyoutMenu.LabelPosition = LabelPosition.Left;
// Add menu items
flyoutMenu.ListItems.Add(new SimpleItem
{
Text = "Home",
ImagePath = "home-icon.svg",
GuidId = Guid.NewGuid().ToString()
});
flyoutMenu.ListItems.Add(new SimpleItem
{
Text = "Dashboard",
ImagePath = "dashboard-icon.svg",
GuidId = Guid.NewGuid().ToString()
});
flyoutMenu.ListItems.Add(new SimpleItem
{
Text = "Settings",
ImagePath = "settings-icon.svg",
GuidId = Guid.NewGuid().ToString()
});
// Handle menu item selection
flyoutMenu.SelectedIndexChanged += (sender, e) =>
{
if (flyoutMenu.SelectedIndex >= 0)
{
var selectedItem = flyoutMenu.ListItems[flyoutMenu.SelectedIndex];
// Console.WriteLine($"Selected: {selectedItem.Text}");
}
};
// Handle menu click events
flyoutMenu.MenuClicked += (sender, args) =>
{
if (args.EventData is SimpleItem clickedItem)
{
MessageBox.Show($"Clicked: {clickedItem.Text}");
}
};
// Add to form
this.Controls.Add(flyoutMenu);
Multi-Direction Flyout Setup
public void SetupMultiDirectionalFlyouts()
{
// Bottom flyout for main navigation
var mainNav = new BeepFlyoutMenu
{
Text = "Main Menu",
Size = new Size(100, 25),
Location = new Point(20, 20),
FlyoutDirection = SlideDirection.Bottom,
LabelPosition = LabelPosition.Left
};
mainNav.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Home", ImagePath = "home.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Projects", ImagePath = "projects.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Reports", ImagePath = "reports.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Settings", ImagePath = "settings.svg", GuidId = Guid.NewGuid().ToString() }
});
// Right flyout for tools
var toolsMenu = new BeepFlyoutMenu
{
Text = "Tools",
Size = new Size(80, 25),
Location = new Point(140, 20),
FlyoutDirection = SlideDirection.Right,
LabelPosition = LabelPosition.Left
};
toolsMenu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Calculator", ImagePath = "calc.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Text Editor", ImagePath = "editor.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Color Picker", ImagePath = "color.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Screen Capture", ImagePath = "capture.svg", GuidId = Guid.NewGuid().ToString() }
});
// Left flyout for recent items (positioned on right side)
var recentMenu = new BeepFlyoutMenu
{
Text = "Recent",
Size = new Size(80, 25),
Location = new Point(this.Width - 100, 20),
FlyoutDirection = SlideDirection.Left,
LabelPosition = LabelPosition.Right,
Anchor = AnchorStyles.Top | AnchorStyles.Right
};
recentMenu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Document1.txt", ImagePath = "file.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Project2.beep", ImagePath = "project.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Report3.pdf", ImagePath = "pdf.svg", GuidId = Guid.NewGuid().ToString() }
});
// Wire up event handlers
mainNav.MenuClicked += HandleMainNavigation;
toolsMenu.MenuClicked += HandleToolsSelection;
recentMenu.MenuClicked += HandleRecentFile;
// Add to form
this.Controls.AddRange(new Control[] { mainNav, toolsMenu, recentMenu });
}
private void HandleMainNavigation(object sender, BeepEventDataArgs e)
{
if (e.EventData is SimpleItem item)
{
switch (item.Text)
{
case "Home":
NavigateToHome();
break;
case "Projects":
ShowProjectsView();
break;
case "Reports":
OpenReportsModule();
break;
case "Settings":
ShowSettingsDialog();
break;
}
}
}
private void HandleToolsSelection(object sender, BeepEventDataArgs e)
{
if (e.EventData is SimpleItem item)
{
switch (item.Text)
{
case "Calculator":
Process.Start("calc.exe");
break;
case "Text Editor":
OpenTextEditor();
break;
case "Color Picker":
ShowColorPicker();
break;
case "Screen Capture":
StartScreenCapture();
break;
}
}
}
private void HandleRecentFile(object sender, BeepEventDataArgs e)
{
if (e.EventData is SimpleItem item)
{
OpenRecentFile(item.Text);
}
}
Toolbar Integration
public class ToolbarPanel : Panel
{
private List toolbarMenus;
public ToolbarPanel()
{
Height = 35;
Dock = DockStyle.Top;
BackColor = SystemColors.Control;
BorderStyle = BorderStyle.FixedSingle;
SetupToolbarMenus();
}
private void SetupToolbarMenus()
{
toolbarMenus = new List();
// File menu
var fileMenu = CreateToolbarMenu("File", 0, new[]
{
new MenuItem("New Document", "new.svg", "Ctrl+N"),
new MenuItem("Open File", "open.svg", "Ctrl+O"),
new MenuItem("Save", "save.svg", "Ctrl+S"),
new MenuItem("Save As", "save-as.svg", "Ctrl+Shift+S"),
new MenuItem("-", "", ""), // Separator
new MenuItem("Print", "print.svg", "Ctrl+P"),
new MenuItem("Print Preview", "print-preview.svg", ""),
new MenuItem("-", "", ""), // Separator
new MenuItem("Recent Files", "recent.svg", ""),
new MenuItem("Exit", "exit.svg", "Alt+F4")
});
// Edit menu
var editMenu = CreateToolbarMenu("Edit", 1, new[]
{
new MenuItem("Undo", "undo.svg", "Ctrl+Z"),
new MenuItem("Redo", "redo.svg", "Ctrl+Y"),
new MenuItem("-", "", ""), // Separator
new MenuItem("Cut", "cut.svg", "Ctrl+X"),
new MenuItem("Copy", "copy.svg", "Ctrl+C"),
new MenuItem("Paste", "paste.svg", "Ctrl+V"),
new MenuItem("-", "", ""), // Separator
new MenuItem("Select All", "select-all.svg", "Ctrl+A"),
new MenuItem("Find", "find.svg", "Ctrl+F"),
new MenuItem("Replace", "replace.svg", "Ctrl+H")
});
// View menu
var viewMenu = CreateToolbarMenu("View", 2, new[]
{
new MenuItem("Zoom In", "zoom-in.svg", "Ctrl++"),
new MenuItem("Zoom Out", "zoom-out.svg", "Ctrl+-"),
new MenuItem("Actual Size", "zoom-actual.svg", "Ctrl+0"),
new MenuItem("-", "", ""), // Separator
new MenuItem("Full Screen", "fullscreen.svg", "F11"),
new MenuItem("Show Toolbar", "toolbar.svg", ""),
new MenuItem("Show Status Bar", "statusbar.svg", ""),
new MenuItem("-", "", ""), // Separator
new MenuItem("Light Theme", "light-theme.svg", ""),
new MenuItem("Dark Theme", "dark-theme.svg", "")
});
// Tools menu
var toolsMenu = CreateToolbarMenu("Tools", 3, new[]
{
new MenuItem("Options", "options.svg", ""),
new MenuItem("Customize", "customize.svg", ""),
new MenuItem("-", "", ""), // Separator
new MenuItem("Extensions", "extensions.svg", ""),
new MenuItem("Plugin Manager", "plugins.svg", ""),
new MenuItem("-", "", ""), // Separator
new MenuItem("Import Data", "import.svg", ""),
new MenuItem("Export Data", "export.svg", "")
});
toolbarMenus.AddRange(new[] { fileMenu, editMenu, viewMenu, toolsMenu });
// Add all menus to panel
foreach (var menu in toolbarMenus)
{
this.Controls.Add(menu);
}
}
private BeepFlyoutMenu CreateToolbarMenu(string title, int index, MenuItem[] items)
{
var menu = new BeepFlyoutMenu
{
Text = title,
Size = new Size(60, 25),
Location = new Point(10 + (index * 65), 5),
FlyoutDirection = SlideDirection.Bottom,
LabelPosition = LabelPosition.Left
};
foreach (var item in items)
{
if (item.Text == "-")
{
// Add separator
menu.ListItems.Add(new SimpleItem
{
Text = "-",
IsSeparator = true,
GuidId = Guid.NewGuid().ToString()
});
}
else
{
menu.ListItems.Add(new SimpleItem
{
Text = item.Text,
ImagePath = item.IconPath,
Tag = item.Shortcut,
GuidId = Guid.NewGuid().ToString()
});
}
}
menu.MenuClicked += (sender, e) =>
{
if (e.EventData is SimpleItem clickedItem)
{
HandleToolbarAction(title, clickedItem.Text);
}
};
return menu;
}
private void HandleToolbarAction(string menuName, string actionName)
{
// Console.WriteLine($"Menu: {menuName}, Action: {actionName}");
// Implement specific actions based on menu and item
switch (menuName)
{
case "File":
HandleFileAction(actionName);
break;
case "Edit":
HandleEditAction(actionName);
break;
case "View":
HandleViewAction(actionName);
break;
case "Tools":
HandleToolsAction(actionName);
break;
}
}
private void HandleFileAction(string action) { /* File action implementation */ }
private void HandleEditAction(string action) { /* Edit action implementation */ }
private void HandleViewAction(string action) { /* View action implementation */ }
private void HandleToolsAction(string action) { /* Tools action implementation */ }
private struct MenuItem
{
public string Text { get; }
public string IconPath { get; }
public string Shortcut { get; }
public MenuItem(string text, string iconPath, string shortcut)
{
Text = text;
IconPath = iconPath;
Shortcut = shortcut;
}
}
}
Properties
Core Properties
| Property | Type | Default | Description |
|---|---|---|---|
ListItems |
BindingList<SimpleItem> | Empty | Collection of flyout menu items |
SelectedIndex |
int | -1 | Index of currently selected item |
FlyoutDirection |
SlideDirection | Bottom | Direction from which the flyout menu appears |
LabelPosition |
LabelPosition | Left | Position of label relative to dropdown button |
Layout Properties
| Property | Type | Default | Description |
|---|---|---|---|
FlyoutMenuWidth |
int | 200 | Width of the flyout menu panel |
CollapsedHeight |
int | 25 | Height of the control when collapsed |
MaxMenuHeight |
int | 200 | Maximum height of the expanded menu |
Component Properties
| Property | Type | Description |
|---|---|---|
DropDownButton |
BeepButton | Internal button that triggers the flyout |
DropDownLabel |
BeepLabel | Internal label displaying the menu text |
Menu |
BeepListBox | Internal menu list control |
IsExpanded |
bool | Whether the flyout menu is currently expanded |
Enum Values
| Enum | Values | Description |
|---|---|---|
SlideDirection |
Left, Right, Bottom | Direction from which the flyout menu slides |
LabelPosition |
Left, Right | Position of label relative to the trigger button |
Events
Selection Events
| Event | Event Args | Description |
|---|---|---|
SelectedIndexChanged |
EventHandler | Fired when the selected item index changes |
MenuClicked |
EventHandler<BeepEventDataArgs> | Fired when a menu item is clicked |
BeepEventDataArgs Properties
| Property | Type | Description |
|---|---|---|
EventName |
string | Name of the event that occurred |
EventData |
object | Data associated with the event (usually SimpleItem) |
Examples
Dynamic Menu Content
public class DynamicFlyoutMenu : BeepFlyoutMenu
{
private readonly Timer refreshTimer;
private readonly List projects;
public DynamicFlyoutMenu()
{
Text = "Active Projects";
FlyoutDirection = SlideDirection.Bottom;
projects = new List();
// Setup automatic refresh
refreshTimer = new Timer { Interval = 5000 }; // Refresh every 5 seconds
refreshTimer.Tick += RefreshProjectList;
refreshTimer.Start();
LoadInitialProjects();
}
private void LoadInitialProjects()
{
// Simulate loading projects from database
projects.AddRange(new[]
{
new Project { Id = 1, Name = "Website Redesign", Status = "Active", Priority = "High" },
new Project { Id = 2, Name = "Mobile App", Status = "In Progress", Priority = "Medium" },
new Project { Id = 3, Name = "Database Migration", Status = "Planning", Priority = "Low" }
});
UpdateMenuItems();
}
private void RefreshProjectList(object sender, EventArgs e)
{
// Simulate getting updated project list
var updatedProjects = GetProjectsFromAPI();
if (ProjectsChanged(updatedProjects))
{
projects.Clear();
projects.AddRange(updatedProjects);
UpdateMenuItems();
}
}
private void UpdateMenuItems()
{
ListItems.Clear();
foreach (var project in projects.OrderBy(p => p.Priority).ThenBy(p => p.Name))
{
var icon = project.Priority switch
{
"High" => "priority-high.svg",
"Medium" => "priority-medium.svg",
"Low" => "priority-low.svg",
_ => "project.svg"
};
var statusColor = project.Status switch
{
"Active" => "??",
"In Progress" => "??",
"Planning" => "??",
"On Hold" => "??",
"Completed" => "?",
_ => "?"
};
ListItems.Add(new SimpleItem
{
Text = $"{statusColor} {project.Name}",
ImagePath = icon,
Tag = project,
ToolTip = $"Status: {project.Status}\nPriority: {project.Priority}",
GuidId = Guid.NewGuid().ToString()
});
}
// Add separator and management options
if (projects.Any())
{
ListItems.Add(new SimpleItem
{
Text = "-",
IsSeparator = true,
GuidId = Guid.NewGuid().ToString()
});
}
ListItems.Add(new SimpleItem
{
Text = "Create New Project",
ImagePath = "add-project.svg",
Tag = "new_project",
GuidId = Guid.NewGuid().ToString()
});
ListItems.Add(new SimpleItem
{
Text = "Manage Projects",
ImagePath = "manage-projects.svg",
Tag = "manage_projects",
GuidId = Guid.NewGuid().ToString()
});
}
protected override void OnMenuClicked(BeepEventDataArgs e)
{
if (e.EventData is SimpleItem item)
{
if (item.Tag is Project project)
{
OpenProjectDetails(project);
}
else if (item.Tag is string action)
{
switch (action)
{
case "new_project":
ShowCreateProjectDialog();
break;
case "manage_projects":
OpenProjectManager();
break;
}
}
}
base.OnMenuClicked(e);
}
private List GetProjectsFromAPI()
{
// Simulate API call
return new List
{
new Project { Id = 1, Name = "Website Redesign", Status = "Active", Priority = "High" },
new Project { Id = 2, Name = "Mobile App", Status = "In Progress", Priority = "Medium" },
new Project { Id = 3, Name = "Database Migration", Status = "Completed", Priority = "Low" },
new Project { Id = 4, Name = "API Integration", Status = "Planning", Priority = "High" }
};
}
private bool ProjectsChanged(List newProjects)
{
if (projects.Count != newProjects.Count) return true;
return projects.Any(existing =>
!newProjects.Any(updated =>
updated.Id == existing.Id &&
updated.Status == existing.Status &&
updated.Name == existing.Name));
}
private void OpenProjectDetails(Project project) { /* Implementation */ }
private void ShowCreateProjectDialog() { /* Implementation */ }
private void OpenProjectManager() { /* Implementation */ }
protected override void Dispose(bool disposing)
{
if (disposing)
{
refreshTimer?.Stop();
refreshTimer?.Dispose();
}
base.Dispose(disposing);
}
}
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public string Priority { get; set; }
}
Theming
BeepFlyoutMenu provides comprehensive theming for all internal components including label, button, and menu:
Theme Application
var flyoutMenu = new BeepFlyoutMenu
{
Theme = "DarkTheme",
Text = "Navigation",
FlyoutDirection = SlideDirection.Bottom
};
// Theme automatically applies to:
// - DropDownLabel styling
// - DropDownButton appearance
// - Menu (BeepListBox) theming
// - Icon color adjustments
// Manual theme application
flyoutMenu.ApplyTheme();
Component Theming
- Label Theming - Text color, font, and background styling
- Button Theming - Button colors, hover states, and icon styling
- Menu Theming - Full BeepListBox theme inheritance
- Animation Integration - Theme-aware animation effects
Best Practices
?? Direction Selection
- Screen boundaries: Choose flyout direction based on available screen space
- User expectations: Use bottom flyouts for toolbar menus, side flyouts for panels
- Content length: Consider menu item count when choosing direction
- Parent container: Ensure adequate space for menu expansion
?? Visual Design
- Consistent sizing: Use consistent flyout widths across application
- Clear labeling: Provide descriptive labels that indicate menu purpose
- Icon usage: Use consistent icon styles and meaningful representations
- Animation performance: Test animations on target hardware for smoothness
? Performance
- Dynamic content: Update menu items efficiently without recreation
- Form integration: Manage parent form controls properly
- Event handling: Avoid heavy operations in event handlers
- Resource cleanup: Dispose of timers and dynamic content appropriately
?? Implementation
- Event binding: Handle both SelectedIndexChanged and MenuClicked appropriately
- Error handling: Gracefully handle empty menu states
- Accessibility: Support keyboard navigation and screen readers
- State management: Track expansion state for complex scenarios
Consider the label position carefully - left positioning works well for most scenarios, while right positioning is useful for right-aligned interface elements.
Always handle the MenuClicked event for immediate action execution, and use SelectedIndexChanged for state tracking and UI updates.
Be mindful of parent form control management - ensure flyout menus are properly added/removed from parent containers to prevent visual and memory issues.