Show / Hide Table of Contents

Detachable Ribbons - Code Examples

This document provides practical code examples for common scenarios when working with detachable ribbons.


Table of Contents

  1. Basic Examples
  2. Event Handling
  3. User Preferences
  4. UI Integration
  5. Advanced Scenarios

Basic Examples

Example 1: Simple Enable and Detach

public partial class MainForm : KryptonForm
{
    private KryptonRibbon _ribbon;

    public MainForm()
    {
        InitializeComponent();
        
        _ribbon = new KryptonRibbon
        {
            Dock = DockStyle.Top,
            AllowDetach = true  // Enable feature
        };
        
        InitializeRibbonContent();
        Controls.Add(_ribbon);
    }

    private void DetachButton_Click(object sender, EventArgs e)
    {
        _ribbon.Detach();
    }

    private void ReattachButton_Click(object sender, EventArgs e)
    {
        _ribbon.Reattach();
    }
}

Example 2: Conditional Detach Based on State

private void ToggleRibbonDetach()
{
    if (_ribbon.IsDetached)
    {
        _ribbon.Reattach();
    }
    else
    {
        _ribbon.Detach();
    }
}

private void UpdateToggleButton()
{
    toggleButton.Text = _ribbon.IsDetached 
        ? "Reattach Ribbon" 
        : "Detach Ribbon";
    toggleButton.Enabled = _ribbon.AllowDetach;
}

Event Handling

Example 3: Update UI on State Change

public MainForm()
{
    InitializeComponent();
    InitializeRibbon();
    HookRibbonEvents();
}

private void HookRibbonEvents()
{
    _ribbon.RibbonDetached += OnRibbonDetached;
    _ribbon.RibbonReattached += OnRibbonReattached;
}

private void OnRibbonDetached(object? sender, EventArgs e)
{
    // Update menu items
    detachMenuItem.Enabled = false;
    reattachMenuItem.Enabled = true;
    
    // Update status bar
    statusLabel.Text = "Ribbon is detached";
    
    // Update toolbar buttons
    detachToolbarButton.Enabled = false;
    reattachToolbarButton.Enabled = true;
    
    // Adjust form layout
    PerformLayout();
}

private void OnRibbonReattached(object? sender, EventArgs e)
{
    // Update menu items
    detachMenuItem.Enabled = true;
    reattachMenuItem.Enabled = false;
    
    // Update status bar
    statusLabel.Text = "Ribbon is attached";
    
    // Update toolbar buttons
    detachToolbarButton.Enabled = true;
    reattachToolbarButton.Enabled = false;
    
    // Adjust form layout
    PerformLayout();
}

Example 4: Logging State Changes

private void HookRibbonEvents()
{
    _ribbon.RibbonDetached += (s, e) =>
    {
        Logger.Info("Ribbon detached by user");
        Analytics.TrackEvent("RibbonDetached");
    };

    _ribbon.RibbonReattached += (s, e) =>
    {
        Logger.Info("Ribbon reattached by user");
        Analytics.TrackEvent("RibbonReattached");
    };
}

User Preferences

Example 5: Save and Restore Detach State with Position Persistence

public partial class MainForm : KryptonForm
{
    private KryptonRibbon _ribbon;

    public MainForm()
    {
        InitializeComponent();
        InitializeRibbon();
        HookPreferenceEvents();
        LoadUserPreferences();
    }

    private void HookPreferenceEvents()
    {
        // Use the new DetachPreferencesChanged event for automatic saving
        _ribbon.DetachPreferencesChanged += OnDetachPreferencesChanged;
    }

    private void OnDetachPreferencesChanged(object? sender, DetachPreferencesEventArgs e)
    {
        // Save detached state
        Properties.Settings.Default.RibbonDetached = e.IsDetached;
        
        // Save window position if available
        if (e.FloatingWindowPosition.HasValue)
        {
            Properties.Settings.Default.RibbonFloatingX = e.FloatingWindowPosition.Value.X;
            Properties.Settings.Default.RibbonFloatingY = e.FloatingWindowPosition.Value.Y;
        }
        else
        {
            // Clear position if not available
            Properties.Settings.Default.RibbonFloatingX = 0;
            Properties.Settings.Default.RibbonFloatingY = 0;
        }
        
        Properties.Settings.Default.Save();
    }

    private void LoadUserPreferences()
    {
        _ribbon.AllowDetach = true;
        
        // Load saved preferences
        bool wasDetached = Properties.Settings.Default.RibbonDetached;
        Point? savedPosition = null;
        
        // Load saved position if available
        if (Properties.Settings.Default.RibbonFloatingX != 0 || 
            Properties.Settings.Default.RibbonFloatingY != 0)
        {
            savedPosition = new Point(
                Properties.Settings.Default.RibbonFloatingX,
                Properties.Settings.Default.RibbonFloatingY);
        }
        
        // Restore state using the new LoadDetachPreferences method
        _ribbon.LoadDetachPreferences(wasDetached, savedPosition);
    }
}

Example 6: Registry-Based Preferences

using Microsoft.Win32;

private void SavePreferenceToRegistry(bool isDetached)
{
    using (var key = Registry.CurrentUser.CreateSubKey(@"Software\YourApp\Ribbon"))
    {
        key?.SetValue("Detached", isDetached);
    }
}

private bool LoadPreferenceFromRegistry()
{
    using (var key = Registry.CurrentUser.OpenSubKey(@"Software\YourApp\Ribbon"))
    {
        return (bool)(key?.GetValue("Detached") ?? false);
    }
}

private void HookRegistryEvents()
{
    _ribbon.RibbonDetached += (s, e) => SavePreferenceToRegistry(true);
    _ribbon.RibbonReattached += (s, e) => SavePreferenceToRegistry(false);
}

UI Integration

Example 7: Context Menu Integration

private void CreateRibbonContextMenu()
{
    var contextMenu = new ContextMenuStrip();
    
    var detachItem = new ToolStripMenuItem("Detach Ribbon")
    {
        Enabled = !_ribbon.IsDetached && _ribbon.AllowDetach
    };
    detachItem.Click += (s, e) => _ribbon.Detach();
    
    var reattachItem = new ToolStripMenuItem("Reattach Ribbon")
    {
        Enabled = _ribbon.IsDetached
    };
    reattachItem.Click += (s, e) => _ribbon.Reattach();
    
    contextMenu.Items.Add(detachItem);
    contextMenu.Items.Add(reattachItem);
    contextMenu.Items.Add(new ToolStripSeparator());
    
    var allowDetachItem = new ToolStripMenuItem("Allow Detach")
    {
        Checked = _ribbon.AllowDetach
    };
    allowDetachItem.Click += (s, e) =>
    {
        _ribbon.AllowDetach = !_ribbon.AllowDetach;
        allowDetachItem.Checked = _ribbon.AllowDetach;
        UpdateContextMenuItems();
    };
    contextMenu.Items.Add(allowDetachItem);
    
    _ribbon.ContextMenuStrip = contextMenu;
    
    // Update menu when state changes
    _ribbon.RibbonDetached += (s, e) => UpdateContextMenuItems();
    _ribbon.RibbonReattached += (s, e) => UpdateContextMenuItems();
}

private void UpdateContextMenuItems()
{
    if (_ribbon.ContextMenuStrip != null)
    {
        var items = _ribbon.ContextMenuStrip.Items;
        if (items.Count >= 2)
        {
            items[0].Enabled = !_ribbon.IsDetached && _ribbon.AllowDetach;
            items[1].Enabled = _ribbon.IsDetached;
        }
    }
}

Example 8: Toolbar Button Integration

private void CreateToolbar()
{
    var toolbar = new ToolStrip();
    
    var detachButton = new ToolStripButton("Detach")
    {
        Image = Properties.Resources.DetachIcon,
        Enabled = !_ribbon.IsDetached && _ribbon.AllowDetach
    };
    detachButton.Click += (s, e) => _ribbon.Detach();
    
    var reattachButton = new ToolStripButton("Reattach")
    {
        Image = Properties.Resources.ReattachIcon,
        Enabled = _ribbon.IsDetached
    };
    reattachButton.Click += (s, e) => _ribbon.Reattach();
    
    toolbar.Items.Add(detachButton);
    toolbar.Items.Add(reattachButton);
    
    Controls.Add(toolbar);
    
    // Update buttons when state changes
    _ribbon.RibbonDetached += (s, e) =>
    {
        detachButton.Enabled = false;
        reattachButton.Enabled = true;
    };
    
    _ribbon.RibbonReattached += (s, e) =>
    {
        detachButton.Enabled = true;
        reattachButton.Enabled = false;
    };
}

Example 9: Status Bar Integration

private void UpdateStatusBar()
{
    statusBarLabel.Text = _ribbon.IsDetached
        ? "Ribbon: Detached (Floating Window)"
        : "Ribbon: Attached";
    
    statusBarLabel.ForeColor = _ribbon.IsDetached
        ? Color.Orange
        : Color.Black;
}

private void HookStatusBarUpdates()
{
    _ribbon.RibbonDetached += (s, e) => UpdateStatusBar();
    _ribbon.RibbonReattached += (s, e) => UpdateStatusBar();
}

Advanced Scenarios

Example 10: Multi-Monitor Support with Position Persistence (Simplified)

public class RibbonDetachManager
{
    private KryptonRibbon _ribbon;

    public RibbonDetachManager(KryptonRibbon ribbon)
    {
        _ribbon = ribbon;
        _ribbon.AllowDetach = true;
        HookEvents();
        LoadPreferences();
    }

    private void HookEvents()
    {
        // Use the built-in preference persistence system
        _ribbon.DetachPreferencesChanged += OnDetachPreferencesChanged;
    }

    private void OnDetachPreferencesChanged(object? sender, DetachPreferencesEventArgs e)
    {
        // Save preferences (position is automatically validated for multi-monitor support)
        Properties.Settings.Default.RibbonDetached = e.IsDetached;
        if (e.FloatingWindowPosition.HasValue)
        {
            Properties.Settings.Default.RibbonFloatingX = e.FloatingWindowPosition.Value.X;
            Properties.Settings.Default.RibbonFloatingY = e.FloatingWindowPosition.Value.Y;
        }
        Properties.Settings.Default.Save();
    }

    private void LoadPreferences()
    {
        // Load saved preferences
        bool wasDetached = Properties.Settings.Default.RibbonDetached;
        Point? savedPosition = null;
        
        if (Properties.Settings.Default.RibbonFloatingX != 0 || 
            Properties.Settings.Default.RibbonFloatingY != 0)
        {
            savedPosition = new Point(
                Properties.Settings.Default.RibbonFloatingX,
                Properties.Settings.Default.RibbonFloatingY);
        }
        
        // LoadDetachPreferences automatically handles multi-monitor position validation
        _ribbon.LoadDetachPreferences(wasDetached, savedPosition);
    }
}

Example 11: Screen Size-Based Auto-Detach

public class AdaptiveRibbonManager
{
    private KryptonRibbon _ribbon;
    private const int MIN_SCREEN_HEIGHT = 768;

    public AdaptiveRibbonManager(KryptonRibbon ribbon)
    {
        _ribbon = ribbon;
        _ribbon.AllowDetach = true;
        CheckScreenSize();
    }

    private void CheckScreenSize()
    {
        var primaryScreen = Screen.PrimaryScreen;
        var workingArea = primaryScreen.WorkingArea;
        
        if (workingArea.Height < MIN_SCREEN_HEIGHT)
        {
            // Suggest detaching ribbon for small screens
            var result = MessageBox.Show(
                $"Your screen height ({workingArea.Height}px) is limited.\n\n" +
                "Would you like to detach the ribbon to save vertical space?",
                "Optimize Layout",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question);
            
            if (result == DialogResult.Yes)
            {
                _ribbon.Detach();
            }
        }
    }

    public void HandleScreenChanged()
    {
        // Called when screen configuration changes
        if (!_ribbon.IsDetached)
        {
            CheckScreenSize();
        }
    }
}

Example 12: Command Pattern for Undo/Redo

public interface IRibbonCommand
{
    void Execute();
    void Undo();
}

public class DetachRibbonCommand : IRibbonCommand
{
    private KryptonRibbon _ribbon;
    private bool _wasDetached;

    public DetachRibbonCommand(KryptonRibbon ribbon)
    {
        _ribbon = ribbon;
        _wasDetached = ribbon.IsDetached;
    }

    public void Execute()
    {
        if (!_wasDetached)
        {
            _ribbon.Detach();
        }
    }

    public void Undo()
    {
        if (!_wasDetached && _ribbon.IsDetached)
        {
            _ribbon.Reattach();
        }
    }
}

public class RibbonCommandManager
{
    private Stack<IRibbonCommand> _undoStack = new();
    private Stack<IRibbonCommand> _redoStack = new();

    public void ExecuteCommand(IRibbonCommand command)
    {
        command.Execute();
        _undoStack.Push(command);
        _redoStack.Clear();
    }

    public void Undo()
    {
        if (_undoStack.Count > 0)
        {
            var command = _undoStack.Pop();
            command.Undo();
            _redoStack.Push(command);
        }
    }

    public void Redo()
    {
        if (_redoStack.Count > 0)
        {
            var command = _redoStack.Pop();
            command.Execute();
            _undoStack.Push(command);
        }
    }
}

Example 13: MVVM Pattern Integration

public class RibbonViewModel : INotifyPropertyChanged
{
    private KryptonRibbon _ribbon;
    private bool _isDetached;
    private bool _allowDetach;

    public RibbonViewModel(KryptonRibbon ribbon)
    {
        _ribbon = ribbon;
        _allowDetach = ribbon.AllowDetach;
        _isDetached = ribbon.IsDetached;
        
        HookEvents();
    }

    public bool IsDetached
    {
        get => _isDetached;
        private set
        {
            if (_isDetached != value)
            {
                _isDetached = value;
                OnPropertyChanged();
                OnPropertyChanged(nameof(CanDetach));
                OnPropertyChanged(nameof(CanReattach));
            }
        }
    }

    public bool AllowDetach
    {
        get => _allowDetach;
        set
        {
            if (_allowDetach != value)
            {
                _allowDetach = value;
                _ribbon.AllowDetach = value;
                OnPropertyChanged();
                OnPropertyChanged(nameof(CanDetach));
            }
        }
    }

    public bool CanDetach => AllowDetach && !IsDetached;
    public bool CanReattach => IsDetached;

    public ICommand DetachCommand { get; }
    public ICommand ReattachCommand { get; }

    private void HookEvents()
    {
        _ribbon.RibbonDetached += (s, e) => IsDetached = true;
        _ribbon.RibbonReattached += (s, e) => IsDetached = false;
        
        DetachCommand = new RelayCommand(
            () => _ribbon.Detach(),
            () => CanDetach);
        
        ReattachCommand = new RelayCommand(
            () => _ribbon.Reattach(),
            () => CanReattach);
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Example 14: Async Operations with Progress

public async Task<bool> DetachRibbonAsync(IProgress<string>? progress = null)
{
    progress?.Report("Preparing to detach ribbon...");
    
    await Task.Delay(50); // Small delay for UI responsiveness
    
    if (!_ribbon.AllowDetach)
    {
        progress?.Report("Detach is not enabled");
        return false;
    }
    
    progress?.Report("Detaching ribbon...");
    
    var result = await Task.Run(() => _ribbon.Detach());
    
    if (result)
    {
        progress?.Report("Ribbon detached successfully");
    }
    else
    {
        progress?.Report("Failed to detach ribbon");
    }
    
    return result;
}

Example 15: Validation Before Detach

public class RibbonDetachValidator
{
    private KryptonRibbon _ribbon;

    public RibbonDetachValidator(KryptonRibbon ribbon)
    {
        _ribbon = ribbon;
    }

    public ValidationResult ValidateDetach()
    {
        var result = new ValidationResult();

        if (!_ribbon.AllowDetach)
        {
            result.AddError("Detach functionality is not enabled");
        }

        if (_ribbon.IsDetached)
        {
            result.AddError("Ribbon is already detached");
        }

        if (_ribbon.Parent == null)
        {
            result.AddError("Ribbon has no parent control");
        }

        if (_ribbon.FindForm() == null)
        {
            result.AddError("Ribbon is not contained within a form");
        }

        // Check if ribbon has unsaved changes (example)
        if (HasUnsavedChanges())
        {
            result.AddWarning("There are unsaved changes. Detaching may affect workflow.");
        }

        return result;
    }

    private bool HasUnsavedChanges()
    {
        // Implement your logic here
        return false;
    }
}

public class ValidationResult
{
    private List<string> _errors = new();
    private List<string> _warnings = new();

    public bool IsValid => _errors.Count == 0;
    public IEnumerable<string> Errors => _errors;
    public IEnumerable<string> Warnings => _warnings;

    public void AddError(string message) => _errors.Add(message);
    public void AddWarning(string message) => _warnings.Add(message);
}

Testing Examples

Example 16: Unit Test Structure

[TestClass]
public class DetachableRibbonTests
{
    [TestMethod]
    public void Detach_WhenAllowDetachIsFalse_ReturnsFalse()
    {
        // Arrange
        var ribbon = new KryptonRibbon { AllowDetach = false };
        var form = new KryptonForm();
        form.Controls.Add(ribbon);

        // Act
        var result = ribbon.Detach();

        // Assert
        Assert.IsFalse(result);
        Assert.IsFalse(ribbon.IsDetached);
    }

    [TestMethod]
    public void Detach_WhenAllowDetachIsTrue_ReturnsTrue()
    {
        // Arrange
        var ribbon = new KryptonRibbon { AllowDetach = true };
        var form = new KryptonForm();
        form.Controls.Add(ribbon);

        // Act
        var result = ribbon.Detach();

        // Assert
        Assert.IsTrue(result);
        Assert.IsTrue(ribbon.IsDetached);
    }

    [TestMethod]
    public void Reattach_WhenDetached_ReturnsTrue()
    {
        // Arrange
        var ribbon = new KryptonRibbon { AllowDetach = true };
        var form = new KryptonForm();
        form.Controls.Add(ribbon);
        ribbon.Detach();

        // Act
        var result = ribbon.Reattach();

        // Assert
        Assert.IsTrue(result);
        Assert.IsFalse(ribbon.IsDetached);
    }
}

Adding Custom Buttons to Ribbon

Example 17: Detach/Reattach Buttons in Ribbon

public partial class MainForm : KryptonForm
{
    private KryptonRibbon _ribbon;
    private ButtonSpecAny? _detachButton;
    private ButtonSpecAny? _reattachButton;

    public MainForm()
    {
        InitializeComponent();
        InitializeRibbon();
        AddRibbonButtons();
    }

    private void AddRibbonButtons()
    {
        // Detach button - appears when ribbon is attached
        _detachButton = new ButtonSpecAny
        {
            Text = "Detach",
            ToolTipTitle = "Detach Ribbon",
            ToolTipBody = "Move the ribbon into a floating window",
            UniqueName = "DetachButton",
            Edge = PaletteRelativeEdgeAlign.Far  // Right side, next to minimize/expand
        };
        _detachButton.Click += (s, e) => _ribbon.Detach();
        _ribbon.ButtonSpecs.Add(_detachButton);

        // Reattach button - appears when ribbon is detached
        _reattachButton = new ButtonSpecAny
        {
            Text = "Reattach",
            ToolTipTitle = "Reattach Ribbon",
            ToolTipBody = "Move the ribbon back to the form",
            UniqueName = "ReattachButton",
            Edge = PaletteRelativeEdgeAlign.Far,
            Visible = false  // Initially hidden
        };
        _reattachButton.Click += (s, e) => _ribbon.Reattach();
        _ribbon.ButtonSpecs.Add(_reattachButton);

        // Update button visibility when state changes
        _ribbon.RibbonDetached += (s, e) => UpdateRibbonButtons();
        _ribbon.RibbonReattached += (s, e) => UpdateRibbonButtons();
    }

    private void UpdateRibbonButtons()
    {
        if (_detachButton != null)
            _detachButton.Visible = !_ribbon.IsDetached;
        if (_reattachButton != null)
            _reattachButton.Visible = _ribbon.IsDetached;
        _ribbon.PerformNeedPaint(true);
    }
}

Example 18: Custom Button with Image

private void AddCustomHelpButton()
{
    var helpButton = new ButtonSpecAny
    {
        Text = "Help",
        ToolTipTitle = "Help",
        ToolTipBody = "Open help documentation",
        UniqueName = "HelpButton",
        Edge = PaletteRelativeEdgeAlign.Far,
        // ImageSmall = yourImage16x16,  // Optional: 16x16 image
        // ImageLarge = yourImage32x32    // Optional: 32x32 image
    };
    helpButton.Click += (s, e) => ShowHelp();
    _ribbon.ButtonSpecs.Add(helpButton);
}

See Also

  • Full Documentation - Comprehensive feature documentation
  • API Reference - Quick API reference
  • Issue #595 - Original feature request
Back to top Krypton Component Suite BSD 3-Clause License © Component Factory Pty Ltd, 2006 - 2016, All rights reserved. Modifications by Peter Wagner (aka Wagnerp), Simon Coghlan (aka Smurf-IV), Giduac, Tobitege, Lesarndro, KamaniAR & Ahmed Abdelhameed et al. 2017 - 2025. All rights reserved. https://github.com/Krypton-Suite