BeepMarquee
Smooth scrolling text and component marquee with continuous animation and seamless wrap-around
📑 Table of Contents
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
};
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);
}
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
Continuous animations can impact battery life on mobile devices. Consider pausing when the application is not in focus.
BeepMarquee automatically handles wrap-around calculations and optimizes drawing for smooth performance.
For best results, implement IBeepUIComponent.Draw() method efficiently and avoid complex graphics operations during scrolling.