BeepPopupListFormStrip
Modern context menu control with popup functionality and theme integration for contextual actions and navigation
?? Table of Contents
Overview
BeepPopupListFormStrip provides a sophisticated context menu control that extends BeepListBox functionality to create modern, theme-aware popup menus. It offers flexible positioning, automatic sizing, click-outside-to-close behavior, and seamless integration with the BeepPopupForm system for professional contextual interfaces.
Built on the BeepListBox foundation, BeepPopupListFormStrip features automatic popup management, smart positioning, item selection handling, theme integration, and comprehensive event support. Perfect for right-click menus, dropdown options, action lists, and contextual navigation in modern Windows Forms applications.
Key Features
??? Smart Popup System
Automatic popup positioning with BeepPopupForm integration and outside-click handling
?? Flexible Positioning
Show context menus at any screen location with automatic boundary detection
?? Theme Integration
Complete theme support with modern styling and consistent visual appearance
?? BeepListBox Heritage
Inherits all BeepListBox functionality including icons, custom items, and layouts
? Event-Driven
Comprehensive event handling for item clicks, selection changes, and menu lifecycle
?? Responsive Design
Automatic sizing based on content with configurable appearance options
Visual Preview
Standard Context Menu
BeepPopupListFormStrip showing common contextual actions with icons and shortcuts
Data Grid Context Menu
Data-centric context menu with record-specific operations and visual grouping
Basic Usage
Simple Context Menu
// Create a BeepPopupListFormStrip
var contextMenu = new BeepPopupListFormStrip();
// Add menu items
contextMenu.ListItems.Add(new SimpleItem
{
Text = "Edit",
ImagePath = "edit-icon.svg",
GuidId = Guid.NewGuid().ToString()
});
contextMenu.ListItems.Add(new SimpleItem
{
Text = "Copy",
ImagePath = "copy-icon.svg",
GuidId = Guid.NewGuid().ToString()
});
contextMenu.ListItems.Add(new SimpleItem
{
Text = "-", // Separator
IsSeparator = true,
GuidId = Guid.NewGuid().ToString()
});
contextMenu.ListItems.Add(new SimpleItem
{
Text = "Delete",
ImagePath = "delete-icon.svg",
GuidId = Guid.NewGuid().ToString()
});
// Handle item clicks
contextMenu.ItemClicked += (sender, item) =>
{
switch (item.Text)
{
case "Edit":
EditSelectedItem();
break;
case "Copy":
CopySelectedItem();
break;
case "Delete":
DeleteSelectedItem();
break;
}
};
// Show context menu on right-click
someControl.MouseDown += (sender, e) =>
{
if (e.Button == MouseButtons.Right)
{
var screenLocation = someControl.PointToScreen(e.Location);
contextMenu.Show(screenLocation);
}
};
Data Grid Context Menu
public void SetupDataGridContextMenu(DataGridView dataGrid)
{
var contextMenu = new BeepPopupListFormStrip();
// Build context menu items
var menuItems = new[]
{
new SimpleItem { Text = "View Details", ImagePath = "view-icon.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Edit Record", ImagePath = "edit-icon.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Send Email", ImagePath = "email-icon.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Call Contact", ImagePath = "phone-icon.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Export Data", ImagePath = "export-icon.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Generate Report", ImagePath = "report-icon.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Delete Record", ImagePath = "delete-icon.svg", GuidId = Guid.NewGuid().ToString() }
};
foreach (var item in menuItems)
{
contextMenu.ListItems.Add(item);
}
// Handle context menu actions
contextMenu.ItemClicked += (sender, item) =>
{
if (dataGrid.SelectedRows.Count == 0) return;
var selectedRow = dataGrid.SelectedRows[0];
switch (item.Text)
{
case "View Details":
ShowRecordDetails(selectedRow);
break;
case "Edit Record":
EditRecord(selectedRow);
break;
case "Send Email":
SendEmailToContact(selectedRow);
break;
case "Call Contact":
InitiatePhoneCall(selectedRow);
break;
case "Export Data":
ExportRowData(selectedRow);
break;
case "Generate Report":
GenerateRecordReport(selectedRow);
break;
case "Delete Record":
DeleteRecord(selectedRow);
break;
}
};
// Attach to data grid
dataGrid.MouseDown += (sender, e) =>
{
if (e.Button == MouseButtons.Right)
{
var hitTest = dataGrid.HitTest(e.X, e.Y);
if (hitTest.RowIndex >= 0)
{
// Select the row that was right-clicked
dataGrid.ClearSelection();
dataGrid.Rows[hitTest.RowIndex].Selected = true;
// Show context menu
var screenLocation = dataGrid.PointToScreen(e.Location);
contextMenu.Show(screenLocation);
}
}
};
}
Control Integration Pattern
public class ContextMenuProvider
{
private readonly Dictionary contextMenus;
public ContextMenuProvider()
{
contextMenus = new Dictionary();
InitializeContextMenus();
}
private void InitializeContextMenus()
{
// Text control context menu
var textContextMenu = CreateTextContextMenu();
contextMenus[typeof(TextBox)] = textContextMenu;
contextMenus[typeof(BeepTextBox)] = textContextMenu;
// Image control context menu
var imageContextMenu = CreateImageContextMenu();
contextMenus[typeof(PictureBox)] = imageContextMenu;
contextMenus[typeof(BeepImage)] = imageContextMenu;
// List control context menu
var listContextMenu = CreateListContextMenu();
contextMenus[typeof(ListBox)] = listContextMenu;
contextMenus[typeof(BeepListBox)] = listContextMenu;
}
private BeepPopupListFormStrip CreateTextContextMenu()
{
var menu = new BeepPopupListFormStrip();
menu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Cut", ImagePath = "cut.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Copy", ImagePath = "copy.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Paste", ImagePath = "paste.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Select All", ImagePath = "select-all.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Find", ImagePath = "search.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Replace", ImagePath = "replace.svg", GuidId = Guid.NewGuid().ToString() }
});
menu.ItemClicked += (sender, item) =>
{
var activeControl = GetActiveTextControl();
if (activeControl == null) return;
switch (item.Text)
{
case "Cut":
if (activeControl is TextBoxBase textBox)
textBox.Cut();
break;
case "Copy":
if (activeControl is TextBoxBase tb)
tb.Copy();
break;
case "Paste":
if (activeControl is TextBoxBase tbPaste)
tbPaste.Paste();
break;
case "Select All":
if (activeControl is TextBoxBase tbSelect)
tbSelect.SelectAll();
break;
case "Find":
ShowFindDialog(activeControl);
break;
case "Replace":
ShowReplaceDialog(activeControl);
break;
}
};
return menu;
}
private BeepPopupListFormStrip CreateImageContextMenu()
{
var menu = new BeepPopupListFormStrip();
menu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Copy Image", ImagePath = "copy.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Save Image As...", ImagePath = "save.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Zoom In", ImagePath = "zoom-in.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Zoom Out", ImagePath = "zoom-out.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Actual Size", ImagePath = "zoom-actual.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Properties", ImagePath = "properties.svg", GuidId = Guid.NewGuid().ToString() }
});
menu.ItemClicked += (sender, item) =>
{
var activeImage = GetActiveImageControl();
if (activeImage == null) return;
switch (item.Text)
{
case "Copy Image":
CopyImageToClipboard(activeImage);
break;
case "Save Image As...":
SaveImageAs(activeImage);
break;
case "Zoom In":
ZoomImage(activeImage, 1.2f);
break;
case "Zoom Out":
ZoomImage(activeImage, 0.8f);
break;
case "Actual Size":
ResetImageZoom(activeImage);
break;
case "Properties":
ShowImageProperties(activeImage);
break;
}
};
return menu;
}
public void AttachContextMenu(Control control)
{
if (contextMenus.TryGetValue(control.GetType(), out var contextMenu))
{
control.MouseDown += (sender, e) =>
{
if (e.Button == MouseButtons.Right)
{
var screenLocation = control.PointToScreen(e.Location);
contextMenu.Show(screenLocation);
}
};
}
}
// Helper methods
private Control GetActiveTextControl() => Form.ActiveForm?.ActiveControl as TextBoxBase;
private Control GetActiveImageControl() => Form.ActiveForm?.ActiveControl as PictureBox;
private void ShowFindDialog(Control control) { /* Implementation */ }
private void ShowReplaceDialog(Control control) { /* Implementation */ }
private void CopyImageToClipboard(Control imageControl) { /* Implementation */ }
private void SaveImageAs(Control imageControl) { /* Implementation */ }
private void ZoomImage(Control imageControl, float factor) { /* Implementation */ }
private void ResetImageZoom(Control imageControl) { /* Implementation */ }
private void ShowImageProperties(Control imageControl) { /* Implementation */ }
}
Properties
Inherited from BeepListBox
| Property | Type | Description |
|---|---|---|
ListItems |
BindingList<SimpleItem> | Collection of context menu items |
ShowCheckBox |
bool | Whether to show checkboxes (default: false for context menus) |
SelectedIndex |
int | Index of currently selected item |
SelectedItem |
SimpleItem | Currently selected menu item |
Context Menu Specific
| Property | Type | Description |
|---|---|---|
PopupForm |
BeepPopupForm | Internal popup form hosting the context menu |
SimpleItem Structure
| Property | Type | Description |
|---|---|---|
Text |
string | Display text for the menu item |
ImagePath |
string | Path to icon image file |
IsSeparator |
bool | Whether the item is a visual separator (Text = "-") |
GuidId |
string | Unique identifier for the menu item |
Tag |
object | Additional data associated with the item |
Enabled |
bool | Whether the menu item is enabled |
Events
Context Menu Events
| Event | Event Args | Description |
|---|---|---|
ItemClicked |
EventHandler<SimpleItem> | Fired when a context menu item is clicked |
Inherited Events from BeepListBox
| Event | Event Args | Description |
|---|---|---|
SelectedItemChanged |
EventHandler | Fired when the selected item changes |
ListItemClicked |
Override method | Handles individual item clicks from BeepButton controls |
Examples
Tree View Context Menu
public void SetupTreeViewContextMenu(TreeView treeView)
{
var contextMenu = new BeepPopupListFormStrip();
// Node-specific menu items that change based on node type
contextMenu.ItemClicked += (sender, item) =>
{
if (treeView.SelectedNode == null) return;
var node = treeView.SelectedNode;
switch (item.Text)
{
case "Add Child":
AddChildNode(node);
break;
case "Edit":
EditNode(node);
break;
case "Delete":
DeleteNode(node);
break;
case "Expand All":
node.ExpandAll();
break;
case "Collapse All":
node.Collapse();
break;
case "Properties":
ShowNodeProperties(node);
break;
}
};
// Build context menu dynamically based on selected node
treeView.MouseDown += (sender, e) =>
{
if (e.Button == MouseButtons.Right)
{
var node = treeView.GetNodeAt(e.X, e.Y);
if (node != null)
{
treeView.SelectedNode = node;
// Clear existing items
contextMenu.ListItems.Clear();
// Add items based on node type/level
if (node.Level == 0) // Root node
{
contextMenu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Add Child", ImagePath = "add.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Expand All", ImagePath = "expand.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Collapse All", ImagePath = "collapse.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Properties", ImagePath = "properties.svg", GuidId = Guid.NewGuid().ToString() }
});
}
else // Child nodes
{
contextMenu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Add Child", ImagePath = "add.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Edit", ImagePath = "edit.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Delete", ImagePath = "delete.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Properties", ImagePath = "properties.svg", GuidId = Guid.NewGuid().ToString() }
});
}
// Show context menu
var screenLocation = treeView.PointToScreen(e.Location);
contextMenu.Show(screenLocation);
}
}
};
}
private void AddChildNode(TreeNode parentNode)
{
var newNode = new TreeNode("New Node");
parentNode.Nodes.Add(newNode);
parentNode.Expand();
// Begin edit mode
newNode.BeginEdit();
}
private void EditNode(TreeNode node)
{
node.BeginEdit();
}
private void DeleteNode(TreeNode node)
{
if (MessageBox.Show($"Delete '{node.Text}'?", "Confirm Delete",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
node.Remove();
}
}
private void ShowNodeProperties(TreeNode node)
{
var properties = new Dictionary
{
["Text"] = node.Text,
["Level"] = node.Level,
["Index"] = node.Index,
["Children"] = node.Nodes.Count,
["Tag"] = node.Tag?.ToString() ?? "None"
};
// Show properties dialog
var propertiesForm = new NodePropertiesForm(properties);
propertiesForm.ShowDialog();
}
File Explorer Context Menu
public class FileExplorerContextMenu
{
private BeepPopupListFormStrip fileContextMenu;
private BeepPopupListFormStrip folderContextMenu;
private BeepPopupListFormStrip emptySpaceContextMenu;
public FileExplorerContextMenu()
{
InitializeContextMenus();
}
private void InitializeContextMenus()
{
// File context menu
fileContextMenu = new BeepPopupListFormStrip();
fileContextMenu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Open", ImagePath = "open.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Open With", ImagePath = "open-with.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Cut", ImagePath = "cut.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Copy", ImagePath = "copy.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Delete", ImagePath = "delete.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Rename", ImagePath = "rename.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Properties", ImagePath = "properties.svg", GuidId = Guid.NewGuid().ToString() }
});
// Folder context menu
folderContextMenu = new BeepPopupListFormStrip();
folderContextMenu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "Open", ImagePath = "open.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Open in New Window", ImagePath = "new-window.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Cut", ImagePath = "cut.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Copy", ImagePath = "copy.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Delete", ImagePath = "delete.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Rename", ImagePath = "rename.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Properties", ImagePath = "properties.svg", GuidId = Guid.NewGuid().ToString() }
});
// Empty space context menu
emptySpaceContextMenu = new BeepPopupListFormStrip();
emptySpaceContextMenu.ListItems.AddRange(new[]
{
new SimpleItem { Text = "New", ImagePath = "new.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Paste", ImagePath = "paste.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Refresh", ImagePath = "refresh.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "-", IsSeparator = true, GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "View", ImagePath = "view.svg", GuidId = Guid.NewGuid().ToString() },
new SimpleItem { Text = "Sort By", ImagePath = "sort.svg", GuidId = Guid.NewGuid().ToString() }
});
// Wire up events
fileContextMenu.ItemClicked += (s, item) => HandleFileAction(item);
folderContextMenu.ItemClicked += (s, item) => HandleFolderAction(item);
emptySpaceContextMenu.ItemClicked += (s, item) => HandleEmptySpaceAction(item);
}
public void AttachToListView(ListView listView)
{
listView.MouseDown += (sender, e) =>
{
if (e.Button == MouseButtons.Right)
{
var item = listView.GetItemAt(e.X, e.Y);
var screenLocation = listView.PointToScreen(e.Location);
if (item != null)
{
// Select the item
listView.SelectedItems.Clear();
item.Selected = true;
// Show appropriate context menu based on item type
var isFolder = (bool)(item.Tag ?? false);
if (isFolder)
{
folderContextMenu.Show(screenLocation);
}
else
{
fileContextMenu.Show(screenLocation);
}
}
else
{
// Right-clicked on empty space
emptySpaceContextMenu.Show(screenLocation);
}
}
};
}
private void HandleFileAction(SimpleItem item)
{
switch (item.Text)
{
case "Open":
OpenSelectedFile();
break;
case "Open With":
ShowOpenWithDialog();
break;
case "Cut":
CutSelectedItems();
break;
case "Copy":
CopySelectedItems();
break;
case "Delete":
DeleteSelectedItems();
break;
case "Rename":
RenameSelectedItem();
break;
case "Properties":
ShowFileProperties();
break;
}
}
private void HandleFolderAction(SimpleItem item)
{
switch (item.Text)
{
case "Open":
OpenSelectedFolder();
break;
case "Open in New Window":
OpenFolderInNewWindow();
break;
// ... other folder actions
}
}
private void HandleEmptySpaceAction(SimpleItem item)
{
switch (item.Text)
{
case "New":
ShowNewItemMenu();
break;
case "Paste":
PasteItems();
break;
case "Refresh":
RefreshView();
break;
// ... other empty space actions
}
}
// Implementation methods
private void OpenSelectedFile() { /* Implementation */ }
private void ShowOpenWithDialog() { /* Implementation */ }
private void CutSelectedItems() { /* Implementation */ }
private void CopySelectedItems() { /* Implementation */ }
private void DeleteSelectedItems() { /* Implementation */ }
private void RenameSelectedItem() { /* Implementation */ }
private void ShowFileProperties() { /* Implementation */ }
private void OpenSelectedFolder() { /* Implementation */ }
private void OpenFolderInNewWindow() { /* Implementation */ }
private void ShowNewItemMenu() { /* Implementation */ }
private void PasteItems() { /* Implementation */ }
private void RefreshView() { /* Implementation */ }
}
Theming
BeepPopupListFormStrip inherits full theming capabilities from BeepListBox with additional popup-specific styling:
Theme Application
var contextMenu = new BeepPopupListFormStrip
{
Theme = "DarkTheme"
};
// Theme automatically applies to:
// - Menu item backgrounds and text
// - Hover and selection states
// - Border and separator colors
// - Popup form background
// - Icon color adjustments
// Manual theme refresh
contextMenu.ApplyTheme();
Context Menu Theming Properties
- PanelBackColor - Context menu background
- ButtonBackColor - Menu item backgrounds
- ButtonForeColor - Menu item text color
- ButtonHoverBackColor - Hover state background
- ButtonSelectedBackColor - Selected item background
- BorderColor - Menu border and separators
- ShadowColor - Drop shadow effects
Custom Popup Styling
// Override ApplyTheme for custom popup styling
public class CustomContextMenuStrip : BeepPopupListFormStrip
{
public override void ApplyTheme()
{
base.ApplyTheme();
if (popupForm != null && _currentTheme != null)
{
// Custom popup form styling
popupForm.BackColor = _currentTheme.PanelBackColor;
popupForm.Opacity = 0.98; // Slight transparency
// Add custom shadow effects
if (_currentTheme.ShowShadow)
{
popupForm.DropShadow = true;
popupForm.ShadowDepth = 8;
}
}
}
}
Best Practices
??? User Experience
- Context awareness: Show only relevant actions for the selected item/context
- Logical grouping: Use separators to group related menu items
- Clear icons: Use intuitive icons that match the action being performed
- Keyboard shortcuts: Display keyboard shortcuts for common actions
?? Positioning & Behavior
- Screen boundaries: Ensure context menus stay within screen bounds
- Quick access: Keep frequently used actions at the top of the menu
- Auto-hide: Close menus when clicking outside or pressing Escape
- Selection feedback: Provide clear visual feedback for menu item selection
?? Implementation
- Dynamic menus: Build menu contents based on current context/selection
- State management: Enable/disable menu items based on current state
- Error handling: Gracefully handle menu actions that might fail
- Memory management: Properly dispose of popup forms and resources
? Performance
- Lazy creation: Create context menus only when needed
- Reuse menus: Reuse context menu instances where appropriate
- Efficient updates: Update menu contents efficiently without recreation
- Resource cleanup: Dispose of unused menu resources properly
Keep context menus focused and concise. More than 10-12 items can overwhelm users - consider using submenus for complex actions.
Always handle the ItemClicked event to provide immediate feedback and action execution when menu items are selected.
Ensure proper disposal of BeepPopupForm instances to prevent memory leaks, especially in applications with frequent context menu usage.