Overview

BeepTabs is a fully custom tab control that extends the standard TabControl with advanced features including themed headers, closeable tabs, drag-and-drop tab reordering, and flexible header positioning. Unlike traditional tab controls, BeepTabs provides complete custom rendering with owner-drawn headers and smooth anti-aliased graphics.

Built as an enhanced TabControl, BeepTabs features SVG close buttons, rounded corners, custom color schemes, and supports header positioning on all four sides (Top, Bottom, Left, Right). It includes comprehensive drag-and-drop functionality for tab reordering and integrates seamlessly with the Beep theming system.

Key Features

🎨 Custom Owner-Drawn Headers

Fully custom-rendered tab headers with anti-aliasing and smooth graphics

❌ Closeable Tabs

SVG close buttons on each tab with TabRemoved event notification

🔄 Drag-and-Drop Reordering

Full drag-and-drop support for tab reordering with visual feedback

📐 Flexible Header Positioning

Headers can be positioned on Top, Bottom, Left, or Right sides

🔘 Rounded Corner Design

Modern rounded corners on tab headers for professional appearance

🎭 Complete Theme Integration

Full integration with Beep theming system for consistent styling

📏 Dynamic Tab Sizing

Automatic tab sizing based on content with configurable min/max limits

♿ Accessibility Support

Built-in accessibility features for screen readers and keyboard navigation

Basic Usage

Simple Tab Control

// Create a basic BeepTabs control
var tabControl = new BeepTabs
{
    Size = new Size(500, 400),
    Location = new Point(10, 10),
    HeaderPosition = TabHeaderPosition.Top,
    HeaderHeight = 30
};

// Create and add tab pages
var tabPage1 = new TabPage("General")
{
    BackColor = Color.White
};
var tabPage2 = new TabPage("Advanced")
{
    BackColor = Color.White
};
var tabPage3 = new TabPage("Settings")
{
    BackColor = Color.White
};

// Add content to tabs
var label1 = new Label { Text = "General Settings", Dock = DockStyle.Fill };
var label2 = new Label { Text = "Advanced Options", Dock = DockStyle.Fill };
var label3 = new Label { Text = "Configuration", Dock = DockStyle.Fill };

tabPage1.Controls.Add(label1);
tabPage2.Controls.Add(label2);
tabPage3.Controls.Add(label3);

// Add tabs to control
tabControl.TabPages.Add(tabPage1);
tabControl.TabPages.Add(tabPage2);
tabControl.TabPages.Add(tabPage3);

// Add to form
this.Controls.Add(tabControl);

Themed Tab Control with Event Handling

var tabControl = new BeepTabs
{
    Theme = "DarkTheme",
    HeaderPosition = TabHeaderPosition.Top,
    HeaderHeight = 35,
    Size = new Size(600, 450)
};

// Handle tab removal
tabControl.TabRemoved += (sender, e) => {
  //  Console.WriteLine($"Tab '{e.TabText}' was removed");
    
    // Perform cleanup if needed
    CleanupTabResources(e.TabText);
};

// Handle selection changes
tabControl.SelectedIndexChanged += (sender, e) => {
    var selectedTab = tabControl.SelectedTab;
    if (selectedTab != null)
    {
      //  Console.WriteLine($"Selected tab: {selectedTab.Text}");
        LoadTabContent(selectedTab);
    }
};

// Add tabs dynamically
for (int i = 1; i <= 5; i++)
{
    var tabPage = new TabPage($"Tab {i}");
    var content = new Panel { Dock = DockStyle.Fill, BackColor = Color.LightBlue };
    content.Controls.Add(new Label 
    { 
        Text = $"Content for Tab {i}", 
        Dock = DockStyle.Fill, 
        TextAlign = ContentAlignment.MiddleCenter 
    });
    
    tabPage.Controls.Add(content);
    tabControl.TabPages.Add(tabPage);
}

Tab Control with Different Header Positions

// Create tabs with different header positions
var topTabs = new BeepTabs
{
    HeaderPosition = TabHeaderPosition.Top,
    HeaderHeight = 30,
    Size = new Size(400, 300),
    Location = new Point(10, 10)
};

var leftTabs = new BeepTabs
{
    HeaderPosition = TabHeaderPosition.Left,
    HeaderHeight = 150, // Width when vertical
    Size = new Size(400, 300),
    Location = new Point(420, 10)
};

var bottomTabs = new BeepTabs
{
    HeaderPosition = TabHeaderPosition.Bottom,
    HeaderHeight = 30,
    Size = new Size(400, 300),
    Location = new Point(10, 320)
};

var rightTabs = new BeepTabs
{
    HeaderPosition = TabHeaderPosition.Right,
    HeaderHeight = 150, // Width when vertical
    Size = new Size(400, 300),
    Location = new Point(420, 320)
};

// Add sample tabs to each
foreach (var tabs in new[] { topTabs, leftTabs, bottomTabs, rightTabs })
{
    for (int i = 1; i <= 3; i++)
    {
        var tabPage = new TabPage($"Tab {i}");
        var label = new Label 
        { 
            Text = $"Content {i}\nPosition: {tabs.HeaderPosition}", 
            Dock = DockStyle.Fill, 
            TextAlign = ContentAlignment.MiddleCenter 
        };
        tabPage.Controls.Add(label);
        tabs.TabPages.Add(tabPage);
    }
}

// Add all to form
this.Controls.AddRange(new Control[] { topTabs, leftTabs, bottomTabs, rightTabs });
💡 Usage Tip

BeepTabs automatically handles layout adjustments when switching header positions, making it easy to create flexible interfaces.

Header Positions

BeepTabs supports four different header positions for maximum layout flexibility:

📄 Top Headers

Traditional tab layout with headers above content

📋 Bottom Headers

Headers positioned below content area

📑 Left Headers

Vertical tab layout with headers on the left side

📄 Right Headers

Vertical tab layout with headers on the right side

Header Position Configuration

// Top headers (default)
var topHeaderTabs = new BeepTabs
{
    HeaderPosition = TabHeaderPosition.Top,
    HeaderHeight = 30 // Height of header area
};

// Bottom headers
var bottomHeaderTabs = new BeepTabs
{
    HeaderPosition = TabHeaderPosition.Bottom,
    HeaderHeight = 30 // Height of header area
};

// Left headers (vertical)
var leftHeaderTabs = new BeepTabs
{
    HeaderPosition = TabHeaderPosition.Left,
    HeaderHeight = 120 // Width of header area when vertical
};

// Right headers (vertical)
var rightHeaderTabs = new BeepTabs
{
    HeaderPosition = TabHeaderPosition.Right,
    HeaderHeight = 120 // Width of header area when vertical
};

// Dynamic header position change
private void ChangeHeaderPosition()
{
    switch (currentPosition)
    {
        case TabHeaderPosition.Top:
            tabControl.HeaderPosition = TabHeaderPosition.Right;
            tabControl.HeaderHeight = 150;
            break;
        case TabHeaderPosition.Right:
            tabControl.HeaderPosition = TabHeaderPosition.Bottom;
            tabControl.HeaderHeight = 30;
            break;
        case TabHeaderPosition.Bottom:
            tabControl.HeaderPosition = TabHeaderPosition.Left;
            tabControl.HeaderHeight = 150;
            break;
        case TabHeaderPosition.Left:
            tabControl.HeaderPosition = TabHeaderPosition.Top;
            tabControl.HeaderHeight = 30;
            break;
    }
}

Properties

Core Properties

Property Type Default Description
HeaderPosition TabHeaderPosition Top Position of tab headers (Top, Bottom, Left, Right)
HeaderHeight int 30 Height of header area (width for vertical positions)
Theme string Default Theme name for styling
SelectTab TabPage null Currently selected tab page
SelectTabByIndex int -1 Select tab by index (write-only)

Inherited TabControl Properties

Property Type Description
TabPages TabPageCollection Collection of tab pages
SelectedTab TabPage Currently selected tab page
SelectedIndex int Index of currently selected tab
TabCount int Number of tabs in the control

TabHeaderPosition Enumeration

Top

Headers at the top
HeaderHeight = height of header area

Bottom

Headers at the bottom
HeaderHeight = height of header area

Left

Headers on the left (vertical)
HeaderHeight = width of header area

Right

Headers on the right (vertical)
HeaderHeight = width of header area

Methods

Method Parameters Returns Description
ApplyTheme None void Apply current theme to all visual elements
SuspendFormLayout None void Suspend layout updates for performance
ResumeFormLayout None void Resume layout updates and refresh display
ReceiveMouseClick Point clientLocation void Handle mouse click from parent control
ReceiveMouseMove Point clientLocation void Handle mouse move from parent control

Method Usage Examples

var tabControl = new BeepTabs();

// Apply theme changes
tabControl.Theme = "DarkTheme";
tabControl.ApplyTheme();

// Performance optimization during bulk updates
tabControl.SuspendFormLayout();
try
{
    // Add multiple tabs
    for (int i = 0; i < 10; i++)
    {
        var tabPage = new TabPage($"Tab {i}");
        // Add content...
        tabControl.TabPages.Add(tabPage);
    }
}
finally
{
    tabControl.ResumeFormLayout();
}

// Handle mouse events from parent container
protected override void OnMouseClick(MouseEventArgs e)
{
    base.OnMouseClick(e);
    
    // Forward click to tab control if within bounds
    if (tabControl.Bounds.Contains(e.Location))
    {
        Point relativeLocation = new Point(
            e.Location.X - tabControl.Left,
            e.Location.Y - tabControl.Top
        );
        tabControl.ReceiveMouseClick(relativeLocation);
    }
}

Events

Event Type Description
TabRemoved EventHandler<TabRemovedEventArgs> Fired when a tab is closed via close button
SelectedIndexChanged EventHandler Fired when the selected tab changes
DrawItem DrawItemEventHandler Fired for custom tab drawing (internal use)

Event Handling Examples

var tabControl = new BeepTabs();

// Handle tab removal
tabControl.TabRemoved += (sender, e) => {
  //  Console.WriteLine($"Tab '{e.TabText}' was removed");
    
    // Perform cleanup
    CleanupTabResources(e.TabText);
    
    // Save state
    SaveTabConfiguration();
    
    // Check if no tabs remain
    if (tabControl.TabCount == 0)
    {
        ShowWelcomeScreen();
    }
};

// Handle tab selection changes
tabControl.SelectedIndexChanged += (sender, e) => {
    var selectedTab = tabControl.SelectedTab;
    if (selectedTab != null)
    {
      //  Console.WriteLine($"Selected tab: {selectedTab.Text}");
        
        // Update status bar
        UpdateStatusBar($"Viewing: {selectedTab.Text}");
        
        // Load tab-specific data
        LoadTabContent(selectedTab);
        
        // Update menu state
        UpdateMenuForTab(selectedTab);
    }
};

// Advanced tab management
private void CleanupTabResources(string tabText)
{
    // Dispose of any resources associated with the tab
    if (tabResources.ContainsKey(tabText))
    {
        var resources = tabResources[tabText];
        resources.Dispose();
        tabResources.Remove(tabText);
    }
}

private void LoadTabContent(TabPage tabPage)
{
    // Lazy load content when tab is selected
    if (tabPage.Controls.Count == 0)
    {
        // Create and add content
        var content = CreateContentForTab(tabPage.Text);
        if (content != null)
        {
            tabPage.Controls.Add(content);
        }
    }
}

Examples

Document Editor with Closeable Tabs

public class DocumentEditor : Form
{
    private BeepTabs documentTabs;
    private Dictionary openDocuments;

    public DocumentEditor()
    {
        InitializeDocumentTabs();
        openDocuments = new Dictionary();
    }

    private void InitializeDocumentTabs()
    {
        documentTabs = new BeepTabs
        {
            Dock = DockStyle.Fill,
            HeaderPosition = TabHeaderPosition.Top,
            HeaderHeight = 32,
            Theme = "DefaultTheme"
        };

        // Handle tab removal
        documentTabs.TabRemoved += DocumentTabs_TabRemoved;
        documentTabs.SelectedIndexChanged += DocumentTabs_SelectedIndexChanged;

        this.Controls.Add(documentTabs);
    }

    public void OpenDocument(string filePath)
    {
        string fileName = Path.GetFileName(filePath);
        
        // Check if document is already open
        foreach (TabPage existingTab in documentTabs.TabPages)
        {
            if (existingTab.Text == fileName)
            {
                documentTabs.SelectedTab = existingTab;
                return;
            }
        }

        // Create new tab for document
        var tabPage = new TabPage(fileName);
        var textEditor = new RichTextBox
        {
            Dock = DockStyle.Fill,
            Font = new Font("Consolas", 11f),
            Text = File.ReadAllText(filePath)
        };

        // Track document state
        var docData = new DocumentData
        {
            FilePath = filePath,
            Editor = textEditor,
            IsModified = false
        };

        textEditor.TextChanged += (s, e) => {
            docData.IsModified = true;
            UpdateTabTitle(tabPage, fileName);
        };

        tabPage.Controls.Add(textEditor);
        documentTabs.TabPages.Add(tabPage);
        documentTabs.SelectedTab = tabPage;

        openDocuments[fileName] = docData;
    }

    private void DocumentTabs_TabRemoved(object sender, TabRemovedEventArgs e)
    {
        string fileName = e.TabText.TrimEnd('*'); // Remove modified indicator

        if (openDocuments.ContainsKey(fileName))
        {
            var docData = openDocuments[fileName];
            
            // Check for unsaved changes
            if (docData.IsModified)
            {
                var result = MessageBox.Show(
                    $"Save changes to {fileName}?",
                    "Unsaved Changes",
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Question);

                switch (result)
                {
                    case DialogResult.Yes:
                        SaveDocument(docData);
                        break;
                    case DialogResult.Cancel:
                        // Re-add the tab (user cancelled)
                        var tabPage = new TabPage(e.TabText);
                        tabPage.Controls.Add(docData.Editor);
                        documentTabs.TabPages.Add(tabPage);
                        return;
                }
            }

            openDocuments.Remove(fileName);
        }

        // Show welcome screen if no documents open
        if (documentTabs.TabCount == 0)
        {
            ShowWelcomeTab();
        }
    }

    private class DocumentData
    {
        public string FilePath { get; set; }
        public RichTextBox Editor { get; set; }
        public bool IsModified { get; set; }
    }
}

Settings Dialog with Multiple Categories

public class AdvancedSettingsDialog : Form
{
    private BeepTabs settingsTabs;

    public AdvancedSettingsDialog()
    {
        InitializeComponents();
        CreateSettingsTabs();
    }

    private void InitializeComponents()
    {
        this.Text = "Advanced Settings";
        this.Size = new Size(800, 600);
        this.StartPosition = FormStartPosition.CenterParent;
    }

    private void CreateSettingsTabs()
    {
        settingsTabs = new BeepTabs
        {
            HeaderPosition = TabHeaderPosition.Left,
            HeaderHeight = 180,
            Theme = "DefaultTheme",
            Dock = DockStyle.Fill
        };

        // Create setting categories
        CreateGeneralTab();
        CreateAdvancedTab();
        CreateAppearanceTab();
        CreateKeyboardTab();
        CreatePluginsTab();

        this.Controls.Add(settingsTabs);
    }

    private void CreateGeneralTab()
    {
        var tabPage = new TabPage("General Settings");
        var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(20) };

        // Add general settings controls
        var languageLabel = new Label { Text = "Language:", Location = new Point(0, 10), AutoSize = true };
        var languageCombo = new ComboBox 
        { 
            Location = new Point(100, 7), 
            Width = 150,
            Items = { "English", "Spanish", "French", "German" }
        };

        var startupCheckBox = new CheckBox 
        { 
            Text = "Start with Windows", 
            Location = new Point(0, 40), 
            AutoSize = true 
        };

        var autoSaveCheckBox = new CheckBox 
        { 
            Text = "Auto-save documents", 
            Location = new Point(0, 70), 
            AutoSize = true 
        };

        panel.Controls.AddRange(new Control[] 
        { 
            languageLabel, languageCombo, 
            startupCheckBox, autoSaveCheckBox 
        });

        tabPage.Controls.Add(panel);
        settingsTabs.TabPages.Add(tabPage);
    }

    private void CreateAdvancedTab()
    {
        var tabPage = new TabPage("Advanced Options");
        var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(20) };

        // Add advanced settings
        var debugCheckBox = new CheckBox 
        { 
            Text = "Enable debug mode", 
            Location = new Point(0, 10), 
            AutoSize = true 
        };

        var cacheLabel = new Label { Text = "Cache size (MB):", Location = new Point(0, 40), AutoSize = true };
        var cacheUpDown = new NumericUpDown 
        { 
            Location = new Point(120, 37), 
            Width = 80, 
            Minimum = 50, 
            Maximum = 1000, 
            Value = 200 
        };

        panel.Controls.AddRange(new Control[] { debugCheckBox, cacheLabel, cacheUpDown });
        tabPage.Controls.Add(panel);
        settingsTabs.TabPages.Add(tabPage);
    }

    private void CreateAppearanceTab()
    {
        var tabPage = new TabPage("Appearance");
        var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(20) };

        // Theme selection
        var themeLabel = new Label { Text = "Theme:", Location = new Point(0, 10), AutoSize = true };
        var themeCombo = new ComboBox 
        { 
            Location = new Point(60, 7), 
            Width = 150,
            DropDownStyle = ComboBoxStyle.DropDownList,
            Items = { "Light", "Dark", "Auto" }
        };

        panel.Controls.AddRange(new Control[] { themeLabel, themeCombo });
        tabPage.Controls.Add(panel);
        settingsTabs.TabPages.Add(tabPage);
    }

    private void CreateKeyboardTab()
    {
        var tabPage = new TabPage("Keyboard");
        var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(20) };

        var shortcutsListView = new ListView
        {
            Dock = DockStyle.Fill,
            View = View.Details,
            FullRowSelect = true,
            GridLines = true
        };

        shortcutsListView.Columns.Add("Action", 200);
        shortcutsListView.Columns.Add("Shortcut", 150);

        // Add sample shortcuts
        shortcutsListView.Items.Add(new ListViewItem(new[] { "New Document", "Ctrl+N" }));
        shortcutsListView.Items.Add(new ListViewItem(new[] { "Open Document", "Ctrl+O" }));
        shortcutsListView.Items.Add(new ListViewItem(new[] { "Save Document", "Ctrl+S" }));

        panel.Controls.Add(shortcutsListView);
        tabPage.Controls.Add(panel);
        settingsTabs.TabPages.Add(tabPage);
    }

    private void CreatePluginsTab()
    {
        var tabPage = new TabPage("Plugins");
        var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(20) };

        var pluginsCheckListBox = new CheckedListBox
        {
            Dock = DockStyle.Fill
        };

        // Add sample plugins
        pluginsCheckListBox.Items.Add("Spell Checker", true);
        pluginsCheckListBox.Items.Add("Code Highlighter", true);
        pluginsCheckListBox.Items.Add("Auto Complete", false);
        pluginsCheckListBox.Items.Add("Git Integration", true);

        panel.Controls.Add(pluginsCheckListBox);
        tabPage.Controls.Add(panel);
        settingsTabs.TabPages.Add(tabPage);
    }
}

Theming

BeepTabs provides comprehensive theming support for all visual elements:

Apply Theme

var tabControl = new BeepTabs
{
    Theme = "DarkTheme"
};

// Theme automatically applies:
// - TabBackColor (inactive tab background)
// - TabForeColor (inactive tab text)
// - TabSelectedBackColor (active tab background)
// - TabSelectedForeColor (active tab text)

// Apply theme manually if needed
tabControl.ApplyTheme();

Custom Theme Integration

// Apply theme to nested controls
tabControl.ApplyTheme();

// Theme is automatically applied to:
// - All TabPage backgrounds
// - Child controls implementing IBeepUIComponent
// - Addin controls and their children

// Custom theme application for specific scenarios
foreach (TabPage tabPage in tabControl.TabPages)
{
    tabPage.BackColor = currentTheme.TabBackColor;
    tabPage.ForeColor = currentTheme.TabForeColor;
    
    foreach (Control control in tabPage.Controls)
    {
        if (control is IBeepUIComponent beepControl)
        {
            beepControl.Theme = tabControl.Theme;
        }
    }
}
🎨 Theming Tip

BeepTabs automatically applies theme colors to tab headers, close buttons, and content areas, ensuring consistent visual appearance across your application.

Best Practices

🎨 User Experience

  • Tab Limits: Consider limiting the number of tabs to prevent overcrowding
  • Close Confirmation: Implement confirmation for closing modified content
  • Tab Ordering: Provide logical tab ordering and allow user customization
  • Visual Feedback: Use modified indicators (*) for unsaved content
  • Keyboard Navigation: Support Ctrl+Tab for tab switching

⚡ Performance

  • Lazy Loading: Load tab content only when selected
  • Resource Management: Dispose of tab resources when tabs are closed
  • Layout Suspension: Use SuspendFormLayout during bulk operations
  • Memory Optimization: Clear unused tab references

🎨 Visual Design

  • Header Position: Choose appropriate header position for your layout
  • Header Sizing: Set appropriate HeaderHeight for readability
  • Theme Consistency: Apply consistent theming across all tabs
  • Icon Usage: Consider adding icons to tab headers for better identification

♿ Accessibility

  • Keyboard Support: Ensure full keyboard navigation
  • Screen Readers: Use meaningful tab text and descriptions
  • Focus Indicators: Provide clear focus indication
  • High Contrast: Test with high contrast themes
⚠️ Performance Warning

Be careful with tab limits and resource management. Too many tabs can impact performance and user experience.

✅ Success Pattern

Use BeepTabs for document interfaces, settings dialogs, and any scenario requiring organized content sections with closeable tabs.