BeepMultiChipGroup
Modern chip-based selection control with auto-wrapping layout, hover effects, and comprehensive data binding for tag-style interfaces
📋 Table of Contents
Overview
BeepMultiChipGroup provides a modern, flexible chip-based selection interface inspired by Material Design chip components. It displays a collection of selectable items as rounded "chips" that automatically wrap to fit the available space, making it perfect for tag selection, category filtering, and multi-choice interfaces.
Built on the BeepControl foundation, BeepMultiChipGroup features intelligent auto-sizing, smooth hover animations, comprehensive data binding, title display, and responsive layout behavior. Ideal for forms, filters, dashboards, and any interface requiring an intuitive selection mechanism.
Key Features
🎯 Smart Layout
Auto-wrapping layout with intelligent chip sizing and responsive behavior
✨ Hover Effects
Smooth hover animations with scaling, color transitions, and visual feedback
📝 Title Support
Configurable title display with alignment options and custom styling
🔗 Data Binding
Complete data binding support with SimpleItem collections and change events
🎨 Theme Integration
Full theme support with automatic color application and state management
📱 Responsive Design
Adaptive sizing with auto-height adjustment and container-aware layout
Visual Preview
Basic Chip Group
Select Categories
BeepMultiChipGroup with auto-wrapping layout and selection state
Hover and Selection States
Skill Tags
Different chip states: normal, hovered (scaled), and selected
Application Integration
Project Setup
Configure your new project settings
Technology Stack
Target Platforms
BeepMultiChipGroup integrated in a project setup form
Basic Usage
Simple Chip Group
// Create a multi-chip group
var chipGroup = new BeepMultiChipGroup
{
TitleText = "Select Categories",
Location = new Point(20, 20),
Width = 400
};
// Create chip items
var categories = new BindingList
{
new SimpleItem { Id = 1, Name = "Technology", Text = "Technology" },
new SimpleItem { Id = 2, Name = "Business", Text = "Business" },
new SimpleItem { Id = 3, Name = "Design", Text = "Design" },
new SimpleItem { Id = 4, Name = "Marketing", Text = "Marketing" },
new SimpleItem { Id = 5, Name = "Finance", Text = "Finance" },
new SimpleItem { Id = 6, Name = "Education", Text = "Education" }
};
// Bind data to chip group
chipGroup.ListItems = categories;
// Handle selection changes
chipGroup.SelectedItemChanged += (sender, e) =>
{
if (e.SelectedItem != null)
{
MessageBox.Show($"Selected: {e.SelectedItem.Text}", "Chip Selected");
}
};
// Add to form
this.Controls.Add(chipGroup);
Programmatic Selection
// Set initial selection
chipGroup.SelectedItem = categories.FirstOrDefault(c => c.Name == "Technology");
// Or set by index
chipGroup.SelectedIndex = 1;
// Or set by GUID ID
chipGroup.SetValue("specific-guid-here");
// Get current selection
var selected = chipGroup.SelectedItem;
if (selected != null)
{
// Console.WriteLine($"Current selection: {selected.Text}");
}
// Clear selection
chipGroup.ClearValue();
Dynamic Chip Management
// Add new chips dynamically
var newChip = new SimpleItem
{
Id = categories.Count + 1,
Name = "Science",
Text = "Science"
};
chipGroup.ListItems.Add(newChip);
// Remove chips
var chipToRemove = chipGroup.ListItems.FirstOrDefault(c => c.Name == "Finance");
if (chipToRemove != null)
{
chipGroup.ListItems.Remove(chipToRemove);
}
// Update chip text
var techChip = chipGroup.ListItems.FirstOrDefault(c => c.Name == "Technology");
if (techChip != null)
{
techChip.Text = "Tech & Innovation";
// The control will automatically refresh
}
// Bulk update
var newCategories = new BindingList
{
new SimpleItem { Id = 1, Name = "Frontend", Text = "Frontend Development" },
new SimpleItem { Id = 2, Name = "Backend", Text = "Backend Development" },
new SimpleItem { Id = 3, Name = "DevOps", Text = "DevOps & Infrastructure" },
new SimpleItem { Id = 4, Name = "Mobile", Text = "Mobile Development" }
};
chipGroup.ListItems = newCategories;
Properties
Data Properties
| Property | Type | Default | Description |
|---|---|---|---|
ListItems |
BindingList<SimpleItem> | Empty list | Collection of chip items to display |
SelectedItem |
SimpleItem | null | Currently selected chip item |
SelectedIndex |
int | -1 | Index of the currently selected chip |
Title Properties
| Property | Type | Default | Description |
|---|---|---|---|
TitleText |
string | "Multi-Select Chip Group" | Title text displayed above the chips |
TitleFont |
Font | Segoe UI, 12pt, Bold | Font used for the title text |
TitleColor |
Color | Black (theme-based) | Color of the title text |
TitleAlignment |
ContentAlignment | TopLeft | Alignment of the title text |
Layout Properties
| Property | Type | Default | Description |
|---|---|---|---|
AutoSize |
bool | true | Automatically size control to fit all chips |
AutoScroll |
bool | false (disabled) | Scrolling is disabled for chip groups |
Internal Constants
| Property | Value | Description |
|---|---|---|
| Chip Padding | 5 pixels | Spacing between individual chips |
| Chip Height | 30 pixels | Fixed height for all chips |
| Corner Radius | 15 pixels | Rounded corner radius for chips |
| Title Height | 30 pixels | Height reserved for title display |
Events
| Event | Event Args | Description |
|---|---|---|
SelectedItemChanged |
SelectedItemChangedEventArgs | Fired when the selected chip changes |
SelectedItemChangedEventArgs Properties
| Property | Type | Description |
|---|---|---|
SelectedItem |
SimpleItem | The newly selected chip item (null if cleared) |
Examples
Product Category Filter
public class ProductFilterForm : Form
{
private BeepMultiChipGroup categoryChips;
private BeepMultiChipGroup brandChips;
private BeepMultiChipGroup priceRangeChips;
private ListView productList;
private List allProducts;
private List filteredProducts;
public ProductFilterForm()
{
SetupInterface();
LoadData();
}
private void SetupInterface()
{
this.Size = new Size(800, 600);
this.Text = "Product Filter";
var mainPanel = new Panel { Dock = DockStyle.Fill };
var filterPanel = new Panel { Dock = DockStyle.Top, Height = 250, Padding = new Padding(20) };
// Category filter
categoryChips = new BeepMultiChipGroup
{
TitleText = "Product Categories",
Location = new Point(0, 0),
Width = 700,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right
};
var categories = new BindingList
{
new SimpleItem { Id = 1, Name = "electronics", Text = "Electronics" },
new SimpleItem { Id = 2, Name = "clothing", Text = "Clothing" },
new SimpleItem { Id = 3, Name = "books", Text = "Books" },
new SimpleItem { Id = 4, Name = "home", Text = "Home & Garden" },
new SimpleItem { Id = 5, Name = "sports", Text = "Sports & Outdoors" },
new SimpleItem { Id = 6, Name = "toys", Text = "Toys & Games" }
};
categoryChips.ListItems = categories;
categoryChips.SelectedItemChanged += CategoryChips_SelectedItemChanged;
// Brand filter
brandChips = new BeepMultiChipGroup
{
TitleText = "Brands",
Location = new Point(0, 80),
Width = 700,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right
};
var brands = new BindingList
{
new SimpleItem { Id = 1, Name = "apple", Text = "Apple" },
new SimpleItem { Id = 2, Name = "samsung", Text = "Samsung" },
new SimpleItem { Id = 3, Name = "nike", Text = "Nike" },
new SimpleItem { Id = 4, Name = "adidas", Text = "Adidas" },
new SimpleItem { Id = 5, Name = "sony", Text = "Sony" },
new SimpleItem { Id = 6, Name = "microsoft", Text = "Microsoft" }
};
brandChips.ListItems = brands;
brandChips.SelectedItemChanged += BrandChips_SelectedItemChanged;
// Price range filter
priceRangeChips = new BeepMultiChipGroup
{
TitleText = "Price Range",
Location = new Point(0, 160),
Width = 700,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right
};
var priceRanges = new BindingList
{
new SimpleItem { Id = 1, Name = "0-25", Text = "$0 - $25" },
new SimpleItem { Id = 2, Name = "25-50", Text = "$25 - $50" },
new SimpleItem { Id = 3, Name = "50-100", Text = "$50 - $100" },
new SimpleItem { Id = 4, Name = "100-250", Text = "$100 - $250" },
new SimpleItem { Id = 5, Name = "250+", Text = "$250+" }
};
priceRangeChips.ListItems = priceRanges;
priceRangeChips.SelectedItemChanged += PriceRangeChips_SelectedItemChanged;
filterPanel.Controls.AddRange(new Control[]
{
categoryChips, brandChips, priceRangeChips
});
// Product list
productList = new ListView
{
Dock = DockStyle.Fill,
View = View.Details,
FullRowSelect = true,
GridLines = true
};
productList.Columns.AddRange(new[]
{
new ColumnHeader { Text = "Name", Width = 200 },
new ColumnHeader { Text = "Category", Width = 120 },
new ColumnHeader { Text = "Brand", Width = 100 },
new ColumnHeader { Text = "Price", Width = 80 },
new ColumnHeader { Text = "Rating", Width = 80 }
});
mainPanel.Controls.AddRange(new Control[] { productList, filterPanel });
this.Controls.Add(mainPanel);
}
private void CategoryChips_SelectedItemChanged(object sender, SelectedItemChangedEventArgs e)
{
ApplyFilters();
}
private void BrandChips_SelectedItemChanged(object sender, SelectedItemChangedEventArgs e)
{
ApplyFilters();
}
private void PriceRangeChips_SelectedItemChanged(object sender, SelectedItemChangedEventArgs e)
{
ApplyFilters();
}
private void ApplyFilters()
{
filteredProducts = allProducts.Where(product =>
{
// Category filter
if (categoryChips.SelectedItem != null)
{
var selectedCategory = categoryChips.SelectedItem.Name;
if (!product.Category.ToLower().Contains(selectedCategory))
return false;
}
// Brand filter
if (brandChips.SelectedItem != null)
{
var selectedBrand = brandChips.SelectedItem.Name;
if (!product.Brand.ToLower().Contains(selectedBrand))
return false;
}
// Price range filter
if (priceRangeChips.SelectedItem != null)
{
var selectedRange = priceRangeChips.SelectedItem.Name;
if (!IsInPriceRange(product.Price, selectedRange))
return false;
}
return true;
}).ToList();
UpdateProductList();
}
private bool IsInPriceRange(decimal price, string range)
{
return range switch
{
"0-25" => price >= 0 && price <= 25,
"25-50" => price > 25 && price <= 50,
"50-100" => price > 50 && price <= 100,
"100-250" => price > 100 && price <= 250,
"250+" => price > 250,
_ => true
};
}
private void UpdateProductList()
{
productList.Items.Clear();
foreach (var product in filteredProducts)
{
var item = new ListViewItem(new[]
{
product.Name,
product.Category,
product.Brand,
product.Price.ToString("C"),
product.Rating.ToString("F1")
});
productList.Items.Add(item);
}
}
private void LoadData()
{
// Sample product data
allProducts = new List
{
new Product { Name = "iPhone 14", Category = "Electronics", Brand = "Apple", Price = 999, Rating = 4.8m },
new Product { Name = "Galaxy S23", Category = "Electronics", Brand = "Samsung", Price = 899, Rating = 4.6m },
new Product { Name = "Air Max 90", Category = "Shoes", Brand = "Nike", Price = 120, Rating = 4.4m },
new Product { Name = "Ultraboost 22", Category = "Shoes", Brand = "Adidas", Price = 180, Rating = 4.5m },
new Product { Name = "WH-1000XM4", Category = "Electronics", Brand = "Sony", Price = 279, Rating = 4.7m },
new Product { Name = "Surface Laptop", Category = "Electronics", Brand = "Microsoft", Price = 1299, Rating = 4.3m }
};
filteredProducts = allProducts.ToList();
UpdateProductList();
}
}
public class Product
{
public string Name { get; set; }
public string Category { get; set; }
public string Brand { get; set; }
public decimal Price { get; set; }
public decimal Rating { get; set; }
}
Skill Selection Form
public class SkillSelectionControl : UserControl
{
private BeepMultiChipGroup programmingChips;
private BeepMultiChipGroup frameworkChips;
private BeepMultiChipGroup databaseChips;
private BeepMultiChipGroup cloudChips;
private List selectedSkills = new List();
public event EventHandler SkillSelectionChanged;
public SkillSelectionControl()
{
SetupChipGroups();
LoadSkillData();
}
private void SetupChipGroups()
{
this.Size = new Size(600, 400);
this.AutoScroll = true;
// Programming Languages
programmingChips = CreateChipGroup("Programming Languages", new Point(20, 20));
// Frameworks
frameworkChips = CreateChipGroup("Frameworks & Libraries", new Point(20, 120));
// Databases
databaseChips = CreateChipGroup("Databases", new Point(20, 220));
// Cloud Platforms
cloudChips = CreateChipGroup("Cloud Platforms", new Point(20, 320));
this.Controls.AddRange(new Control[]
{
programmingChips, frameworkChips, databaseChips, cloudChips
});
}
private BeepMultiChipGroup CreateChipGroup(string title, Point location)
{
var chipGroup = new BeepMultiChipGroup
{
TitleText = title,
Location = location,
Width = 550,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right
};
chipGroup.SelectedItemChanged += ChipGroup_SelectedItemChanged;
return chipGroup;
}
private void LoadSkillData()
{
// Programming languages
var programmingLanguages = new BindingList
{
new SimpleItem { Id = 1, Name = "csharp", Text = "C#" },
new SimpleItem { Id = 2, Name = "javascript", Text = "JavaScript" },
new SimpleItem { Id = 3, Name = "python", Text = "Python" },
new SimpleItem { Id = 4, Name = "java", Text = "Java" },
new SimpleItem { Id = 5, Name = "typescript", Text = "TypeScript" },
new SimpleItem { Id = 6, Name = "go", Text = "Go" },
new SimpleItem { Id = 7, Name = "rust", Text = "Rust" },
new SimpleItem { Id = 8, Name = "kotlin", Text = "Kotlin" }
};
programmingChips.ListItems = programmingLanguages;
// Frameworks
var frameworks = new BindingList
{
new SimpleItem { Id = 1, Name = "react", Text = "React" },
new SimpleItem { Id = 2, Name = "angular", Text = "Angular" },
new SimpleItem { Id = 3, Name = "vue", Text = "Vue.js" },
new SimpleItem { Id = 4, Name = "aspnet", Text = "ASP.NET Core" },
new SimpleItem { Id = 5, Name = "django", Text = "Django" },
new SimpleItem { Id = 6, Name = "spring", Text = "Spring Boot" },
new SimpleItem { Id = 7, Name = "express", Text = "Express.js" },
new SimpleItem { Id = 8, Name = "fastapi", Text = "FastAPI" }
};
frameworkChips.ListItems = frameworks;
// Databases
var databases = new BindingList
{
new SimpleItem { Id = 1, Name = "sqlserver", Text = "SQL Server" },
new SimpleItem { Id = 2, Name = "mysql", Text = "MySQL" },
new SimpleItem { Id = 3, Name = "postgresql", Text = "PostgreSQL" },
new SimpleItem { Id = 4, Name = "mongodb", Text = "MongoDB" },
new SimpleItem { Id = 5, Name = "redis", Text = "Redis" },
new SimpleItem { Id = 6, Name = "sqlite", Text = "SQLite" },
new SimpleItem { Id = 7, Name = "oracle", Text = "Oracle" }
};
databaseChips.ListItems = databases;
// Cloud platforms
var cloudPlatforms = new BindingList
{
new SimpleItem { Id = 1, Name = "azure", Text = "Microsoft Azure" },
new SimpleItem { Id = 2, Name = "aws", Text = "Amazon AWS" },
new SimpleItem { Id = 3, Name = "gcp", Text = "Google Cloud" },
new SimpleItem { Id = 4, Name = "docker", Text = "Docker" },
new SimpleItem { Id = 5, Name = "kubernetes", Text = "Kubernetes" },
new SimpleItem { Id = 6, Name = "digitalocean", Text = "DigitalOcean" }
};
cloudChips.ListItems = cloudPlatforms;
}
private void ChipGroup_SelectedItemChanged(object sender, SelectedItemChangedEventArgs e)
{
UpdateSelectedSkills();
OnSkillSelectionChanged();
}
private void UpdateSelectedSkills()
{
selectedSkills.Clear();
// Collect selected skills from all chip groups
if (programmingChips.SelectedItem != null)
selectedSkills.Add($"Programming: {programmingChips.SelectedItem.Text}");
if (frameworkChips.SelectedItem != null)
selectedSkills.Add($"Framework: {frameworkChips.SelectedItem.Text}");
if (databaseChips.SelectedItem != null)
selectedSkills.Add($"Database: {databaseChips.SelectedItem.Text}");
if (cloudChips.SelectedItem != null)
selectedSkills.Add($"Cloud: {cloudChips.SelectedItem.Text}");
}
private void OnSkillSelectionChanged()
{
SkillSelectionChanged?.Invoke(this, new SkillSelectionChangedEventArgs
{
SelectedSkills = selectedSkills.ToList(),
ProgrammingLanguage = programmingChips.SelectedItem?.Text,
Framework = frameworkChips.SelectedItem?.Text,
Database = databaseChips.SelectedItem?.Text,
CloudPlatform = cloudChips.SelectedItem?.Text
});
}
public void ClearAllSelections()
{
programmingChips.ClearValue();
frameworkChips.ClearValue();
databaseChips.ClearValue();
cloudChips.ClearValue();
}
public void SetSelections(string programming, string framework, string database, string cloud)
{
if (!string.IsNullOrEmpty(programming))
{
var progItem = programmingChips.ListItems.FirstOrDefault(i => i.Text == programming);
if (progItem != null) programmingChips.SelectedItem = progItem;
}
if (!string.IsNullOrEmpty(framework))
{
var fwItem = frameworkChips.ListItems.FirstOrDefault(i => i.Text == framework);
if (fwItem != null) frameworkChips.SelectedItem = fwItem;
}
if (!string.IsNullOrEmpty(database))
{
var dbItem = databaseChips.ListItems.FirstOrDefault(i => i.Text == database);
if (dbItem != null) databaseChips.SelectedItem = dbItem;
}
if (!string.IsNullOrEmpty(cloud))
{
var cloudItem = cloudChips.ListItems.FirstOrDefault(i => i.Text == cloud);
if (cloudItem != null) cloudChips.SelectedItem = cloudItem;
}
}
}
public class SkillSelectionChangedEventArgs : EventArgs
{
public List SelectedSkills { get; set; }
public string ProgrammingLanguage { get; set; }
public string Framework { get; set; }
public string Database { get; set; }
public string CloudPlatform { get; set; }
}
Theming
BeepMultiChipGroup provides comprehensive theming with automatic application to all chips and states:
Theme Application
var chipGroup = new BeepMultiChipGroup
{
Theme = "DarkTheme",
TitleText = "Select Options"
};
// Theme automatically applies to:
// - Chip background colors
// - Chip text colors
// - Hover state colors
// - Selected state colors
// - Title text color
// - Border colors
// Manual theme refresh
chipGroup.ApplyTheme();
Chip Group Theme Properties
- ButtonBackColor - Normal chip background
- ButtonForeColor - Normal chip text color
- ButtonHoverBackColor - Chip hover background
- ButtonHoverForeColor - Chip hover text color
- ButtonSelectedForeColor - Selected chip colors
- CardTitleForeColor - Title text color
- BorderColor - Chip borders
- HoverLinkColor - Hover border colors
Custom Chip Styling
// Apply theme first
chipGroup.ApplyTheme();
// Customize title appearance
chipGroup.TitleColor = Color.FromArgb(52, 73, 94);
chipGroup.TitleFont = new Font("Segoe UI", 14, FontStyle.Bold);
chipGroup.TitleAlignment = ContentAlignment.TopCenter;
// The control handles individual chip styling automatically
// based on the current theme and interaction states
Best Practices
🎯 Chip Design
- Concise labels: Keep chip text short and descriptive
- Logical grouping: Group related chips in separate chip groups
- Consistent sizing: Let the control handle automatic sizing for consistency
- Clear titles: Use descriptive titles to explain chip purpose
📱 Layout Considerations
- Width allocation: Provide adequate width for chip wrapping
- AutoSize usage: Enable AutoSize for dynamic height adjustment
- Spacing management: Use proper margins between chip groups
- Responsive design: Consider how chips wrap on different screen sizes
🔗 Data Management
- SimpleItem usage: Properly populate SimpleItem properties (Id, Name, Text)
- Change detection: Use SelectedItemChanged event for responsive updates
- State preservation: Store selections appropriately for form persistence
- Dynamic updates: Use BindingList for automatic UI updates
✨ User Experience
- Visual feedback: Leverage hover and selection state animations
- Single selection: Remember this is a single-selection control
- Clear indication: Ensure selected state is visually distinct
- Accessibility: Ensure adequate color contrast for all states
Set a reasonable width (400-600px) to allow proper chip wrapping while maintaining readability. Too narrow causes excessive wrapping, too wide wastes space.
Use descriptive chip text that clearly indicates what selecting the chip will accomplish - avoid ambiguous labels like "Option 1" or "Choice A".
BeepMultiChipGroup supports single selection only. For multi-selection scenarios, consider using multiple chip groups or a different control type.