Overview

BeepMarquee is a sophisticated scrolling display control that mimics HTML marquee behavior with smooth continuous scrolling and seamless wrap-around effects. It can display multiple UI components that scroll horizontally across the control surface, perfect for news tickers, announcements, and dynamic content display.

Built on the BeepControl foundation, it supports any IBeepUIComponent for flexible content display, with configurable scrolling speed, direction, spacing, and timing. The control uses optimized double buffering for smooth animation performance.

Key Features

🔄 Continuous Scrolling

Seamless wrap-around scrolling with no visible breaks or gaps

🧩 Flexible Components

Support for any IBeepUIComponent - text, images, custom controls

✨ Smooth Animation

Double-buffered rendering for flicker-free smooth scrolling

🔀 Direction Control

Bidirectional scrolling - left to right or right to left

⏱️ Configurable Timing

Adjustable scroll speed and refresh intervals

📏 Smart Spacing

Customizable spacing between components for optimal layout

⚖️ Auto-Centering

Automatic vertical centering of components within the control

🔄 Dynamic Updates

Add, remove, or modify components during runtime

Basic Usage

Simple Text Marquee

// Create a basic marquee
var marquee = new BeepMarquee
{
    Size = new Size(400, 50),
    Location = new Point(10, 10),
    ScrollLeft = true,
    ScrollSpeed = 2f,
    ComponentSpacing = 20,
    BackColor = Color.Black
};

// Add text components (assuming you have a text component implementation)
marquee.AddMarqueeComponent("news1", new TextComponent("Breaking News: ..."));
marquee.AddMarqueeComponent("news2", new TextComponent("Weather Update: ..."));
marquee.AddMarqueeComponent("news3", new TextComponent("Sports: ..."));

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

Fast-Scrolling Ticker

var ticker = new BeepMarquee
{
    Dock = DockStyle.Bottom,
    Height = 30,
    ScrollLeft = true,
    ScrollSpeed = 5f,
    ScrollInterval = 16, // ~60 FPS
    ComponentSpacing = 50,
    BackColor = Color.DarkBlue
};

Right-to-Left Announcement

var announcement = new BeepMarquee
{
    Size = new Size(600, 40),
    ScrollLeft = false, // Right to left
    ScrollSpeed = 1.5f,
    ScrollInterval = 25,
    ComponentSpacing = 30
};
💡 Usage Tip

BeepMarquee automatically handles wrap-around calculations and provides smooth scrolling with minimal CPU usage when properly configured.

Properties

Scrolling Properties

Property Type Default Description
ScrollLeft bool true Scroll direction: true = right to left, false = left to right
ScrollSpeed float 2.0f Pixels to move per timer tick (higher = faster)
ScrollInterval int 30 Timer interval in milliseconds (~33 FPS)
ComponentSpacing int 20 Spacing between consecutive components in pixels

Internal Properties

Property Type Access Description
_marqueeComponents Dictionary<string, IBeepUIComponent> private Collection of components displayed in the marquee
_scrollOffset float private Current horizontal scroll position
_timer Timer private Animation timer that drives the scrolling

Methods

Method Parameters Description
AddMarqueeComponent string key, IBeepUIComponent component Add or replace a component in the marquee
RemoveMarqueeComponent string key Remove a component from the marquee by key
GetTotalComponentsWidth none Calculate the total width of all components plus spacing
DrawComponents Graphics graphics, float startX Draw all components starting from a given X coordinate
OnTimerTick none Timer event handler that updates scroll position

Events

Event Type Description
Paint PaintEventHandler Handles custom drawing of marquee components
Resize EventHandler Handles control resize events
Timer.Tick EventHandler Drives the animation by updating scroll position

Event Handling Examples

var marquee = new BeepMarquee();

// Handle mouse interactions (inherited from BeepControl)
marquee.Click += (s, e) => {
    // Pause/resume scrolling on click
    if (marquee._timer.Enabled)
        marquee._timer.Stop();
    else
        marquee._timer.Start();
};

// Handle resize to recalculate layout
marquee.Resize += (s, e) => {
    // Optional: adjust scroll speed based on control width
    marquee.ScrollSpeed = Math.Max(1, marquee.Width / 200f);
};

Examples

News Ticker Implementation

public class NewsTickerForm : Form
{
    private BeepMarquee newsTicker;
    private List newsItems;

    public NewsTickerForm()
    {
        InitializeNewsTicker();
        LoadNewsItems();
    }

    private void InitializeNewsTicker()
    {
        newsTicker = new BeepMarquee
        {
            Dock = DockStyle.Bottom,
            Height = 40,
            ScrollLeft = true,
            ScrollSpeed = 3f,
            ScrollInterval = 20,
            ComponentSpacing = 100,
            BackColor = Color.DarkBlue,
            ForeColor = Color.White
        };

        this.Controls.Add(newsTicker);
    }

    private void LoadNewsItems()
    {
        newsItems = new List
        {
            "BREAKING: Market reaches new highs",
            "WEATHER: Sunny skies expected this weekend",
            "SPORTS: Championship finals tonight at 8 PM",
            "TECH: New software update available"
        };

        for (int i = 0; i < newsItems.Count; i++)
        {
            var textComponent = CreateNewsComponent(newsItems[i]);
            newsTicker.AddMarqueeComponent($"news_{i}", textComponent);
        }
    }

    private IBeepUIComponent CreateNewsComponent(string text)
    {
        // Assuming you have a custom text component implementation
        return new NewsTextComponent
        {
            Text = text,
            Font = new Font("Arial", 12, FontStyle.Bold),
            ForeColor = Color.Yellow,
            Width = TextRenderer.MeasureText(text, new Font("Arial", 12)).Width,
            Height = 30
        };
    }

    public void UpdateNews(string newItem)
    {
        newsItems.Add(newItem);
        var component = CreateNewsComponent(newItem);
        newsTicker.AddMarqueeComponent($"news_{newsItems.Count - 1}", component);
    }
}

Stock Price Ticker

public class StockTicker : BeepMarquee
{
    private Timer updateTimer;
    private Random random = new Random();

    public StockTicker()
    {
        this.ScrollLeft = true;
        this.ScrollSpeed = 2f;
        this.ComponentSpacing = 50;
        this.BackColor = Color.Black;
        this.Height = 50;

        InitializeStocks();
        StartAutoUpdate();
    }

    private void InitializeStocks()
    {
        var stocks = new[]
        {
            new { Symbol = "AAPL", Price = 150.25m, Change = 2.5m },
            new { Symbol = "GOOGL", Price = 2800.50m, Change = -15.25m },
            new { Symbol = "MSFT", Price = 300.75m, Change = 5.10m },
            new { Symbol = "TSLA", Price = 800.00m, Change = -20.00m }
        };

        foreach (var stock in stocks)
        {
            var component = CreateStockComponent(stock.Symbol, stock.Price, stock.Change);
            AddMarqueeComponent(stock.Symbol, component);
        }
    }

    private IBeepUIComponent CreateStockComponent(string symbol, decimal price, decimal change)
    {
        Color changeColor = change >= 0 ? Color.Green : Color.Red;
        string changeText = change >= 0 ? $"+{change:F2}" : change.ToString("F2");

        return new StockDisplayComponent
        {
            Symbol = symbol,
            Price = price,
            Change = change,
            DisplayText = $"{symbol}: ${price:F2} ({changeText})",
            ForeColor = Color.White,
            ChangeColor = changeColor,
            Width = 150,
            Height = 40
        };
    }

    private void StartAutoUpdate()
    {
        updateTimer = new Timer { Interval = 5000 }; // Update every 5 seconds
        updateTimer.Tick += (s, e) => UpdateStockPrices();
        updateTimer.Start();
    }

    private void UpdateStockPrices()
    {
        var symbols = new[] { "AAPL", "GOOGL", "MSFT", "TSLA" };
        
        foreach (var symbol in symbols)
        {
            // Simulate price changes
            decimal priceChange = (decimal)(random.NextDouble() * 10 - 5); // -5 to +5
            decimal newPrice = 100 + (decimal)(random.NextDouble() * 200); // 100 to 300
            
            var component = CreateStockComponent(symbol, newPrice, priceChange);
            AddMarqueeComponent(symbol, component);
        }
    }
}

Image and Text Mixed Marquee

public void CreateMixedContentMarquee()
{
    var marquee = new BeepMarquee
    {
        Size = new Size(800, 60),
        ScrollLeft = true,
        ScrollSpeed = 1.5f,
        ComponentSpacing = 30,
        BackColor = Color.Navy
    };

    // Add text components
    marquee.AddMarqueeComponent("welcome", 
        new TextComponent("Welcome to our application!") 
        { 
            ForeColor = Color.White, 
            Font = new Font("Arial", 14, FontStyle.Bold) 
        });

    // Add image components
    marquee.AddMarqueeComponent("logo1", 
        new ImageComponent("company_logo.png") 
        { 
            Width = 50, 
            Height = 50 
        });

    // Add mixed components
    marquee.AddMarqueeComponent("promo", 
        new PromoComponent
        {
            Text = "50% OFF",
            Icon = "sale_icon.png",
            BackColor = Color.Red,
            ForeColor = Color.White,
            Width = 120,
            Height = 50
        });

    this.Controls.Add(marquee);
}

// Custom component that combines text and image
public class PromoComponent : IBeepUIComponent
{
    public string Text { get; set; }
    public string Icon { get; set; }
    public Color BackColor { get; set; }
    public Color ForeColor { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }

    public void Draw(Graphics graphics, Rectangle bounds)
    {
        // Draw background
        using (var brush = new SolidBrush(BackColor))
        {
            graphics.FillRectangle(brush, bounds);
        }

        // Draw icon (if available)
        if (!string.IsNullOrEmpty(Icon))
        {
            // Load and draw icon
            var iconRect = new Rectangle(bounds.X + 5, bounds.Y + 5, 20, 20);
            // graphics.DrawImage(iconImage, iconRect);
        }

        // Draw text
        var textRect = new Rectangle(bounds.X + 30, bounds.Y, bounds.Width - 30, bounds.Height);
        using (var brush = new SolidBrush(ForeColor))
        {
            var format = new StringFormat 
            { 
                Alignment = StringAlignment.Center,
                LineAlignment = StringAlignment.Center 
            };
            graphics.DrawString(Text, SystemFonts.DefaultFont, brush, textRect, format);
        }
    }
}

Pause/Resume Control

public class ControllableMarquee : BeepMarquee
{
    public bool IsPaused { get; private set; }

    public ControllableMarquee()
    {
        // Add mouse interaction for pause/resume
        this.Click += TogglePause;
        this.MouseEnter += OnMouseEnter;
        this.MouseLeave += OnMouseLeave;
    }

    public void Pause()
    {
        if (!IsPaused)
        {
            _timer.Stop();
            IsPaused = true;
        }
    }

    public void Resume()
    {
        if (IsPaused)
        {
            _timer.Start();
            IsPaused = false;
        }
    }

    private void TogglePause(object sender, EventArgs e)
    {
        if (IsPaused)
            Resume();
        else
            Pause();
    }

    private void OnMouseEnter(object sender, EventArgs e)
    {
        // Slow down on hover
        var originalSpeed = ScrollSpeed;
        ScrollSpeed = originalSpeed * 0.3f;
    }

    private void OnMouseLeave(object sender, EventArgs e)
    {
        // Restore normal speed
        ScrollSpeed = 2f; // Or store original speed
    }
}

Theming

BeepMarquee inherits theming capabilities from BeepControl and can be styled consistently:

Theme Integration

var themedMarquee = new BeepMarquee
{
    Theme = "DarkTheme",
    ComponentSpacing = 25
};

// Theme properties are automatically applied:
// - BackColor uses theme.BackColor
// - Component colors should match theme palette

Custom Styled Marquee

public void CreateThemedMarquee(string themeName)
{
    var marquee = new BeepMarquee
    {
        Theme = themeName,
        Dock = DockStyle.Top,
        Height = 35
    };

    // Add components with theme-aware colors
    var components = CreateThemedComponents(themeName);
    foreach (var component in components)
    {
        marquee.AddMarqueeComponent(component.Key, component.Value);
    }

    this.Controls.Add(marquee);
}
🎨 Theming Tip

BeepMarquee automatically applies theme background colors. Ensure your IBeepUIComponent implementations respect theme colors for consistency.

Best Practices

⚡ Performance Guidelines

  • Timer intervals: Use appropriate intervals (16-30ms) for smooth animation without excessive CPU usage
  • Component complexity: Keep IBeepUIComponent implementations lightweight for better performance
  • Double buffering: The control uses optimized double buffering - avoid additional buffering in components
  • Memory management: Dispose of graphics resources properly in custom components

🎨 Design Guidelines

  • Scroll speed: Use moderate speeds (1-5 pixels) for readability
  • Component spacing: Provide adequate spacing (20-50px) between components
  • Color contrast: Ensure text components have sufficient contrast against background
  • Content length: Keep text content concise for better readability while scrolling

🔧 Implementation Tips

  • Component keys: Use descriptive, unique keys for easy component management
  • Dynamic updates: Update components during low-activity periods to avoid visual glitches
  • Error handling: Handle null or invalid components gracefully
  • Responsive design: Adjust scroll speed and spacing based on control size

♿ Accessibility

  • Motion sensitivity: Provide options to pause or disable scrolling for motion-sensitive users
  • Screen readers: Consider alternative text-only content for screen readers
  • Keyboard control: Implement keyboard shortcuts for pause/resume functionality
  • High contrast: Ensure components work well in high contrast modes
⚠️ Performance Warning

Continuous animations can impact battery life on mobile devices. Consider pausing when the application is not in focus.

✅ Automatic Features

BeepMarquee automatically handles wrap-around calculations and optimizes drawing for smooth performance.

💡 Implementation Tip

For best results, implement IBeepUIComponent.Draw() method efficiently and avoid complex graphics operations during scrolling.