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

Navigation
?? Home
?? Dashboard
?? Settings
?? Profile

Menu slides down from trigger

Right Flyout

Tools
?? Settings
?? Editor
?? Search
?? Export

Menu slides right from trigger

Left Flyout

?? Projects
?? Tasks
?? Reports
?? Team
Projects

Menu slides left from trigger

BeepFlyoutMenu supporting multiple slide directions with dynamic icon updates

Toolbar Integration

File
Edit
View
Tools
?? New Document Ctrl+N
?? Open File Ctrl+O
?? Save Ctrl+S
??? Print Ctrl+P
?? Exit Alt+F4

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
?? Layout Tip

Consider the label position carefully - left positioning works well for most scenarios, while right positioning is useful for right-aligned interface elements.

? Best Practice

Always handle the MenuClicked event for immediate action execution, and use SelectedIndexChanged for state tracking and UI updates.

?? Integration Warning

Be mindful of parent form control management - ensure flyout menus are properly added/removed from parent containers to prevent visual and memory issues.