如何在C#中实现从数据库加载带分组标题的ComboBox
Got it, let's tackle this grouped ComboBox requirement. The standard WinForms ComboBox doesn't support native group headers, so we'll use owner-drawn items to achieve the exact layout you want—with section headers followed by paired data rows from your SQL database.
Step 1: Define a Data Model
First, create a class to represent both header and data items. This helps us distinguish between them when drawing and handling selections:
public class BioDataEntry { // For header items, this holds the group name (e.g., "Neuropeptide") public string GroupName { get; set; } // For data items, these are your two SQL columns public string Identifier { get; set; } public string Label { get; set; } // Flag to mark if this is a header or data row public bool IsHeader { get; set; } = false; }
Step 2: Fetch and Group Data from SQL
Next, pull your data from the database, group it by the data type, and populate a list with both headers and data rows:
List<BioDataEntry> comboItems = new List<BioDataEntry>(); // Replace with your actual connection string string connectionString = @"Server=yourServer;Database=yourDB;Trusted_Connection=True;"; using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); // Adjust the query to match your table/column names string query = @"SELECT DataType, Column1, Column2 FROM YourBiologicalData ORDER BY DataType, Column1"; using (SqlCommand cmd = new SqlCommand(query, sqlConnection)) { using (SqlDataReader reader = cmd.ExecuteReader()) { string currentGroup = null; while (reader.Read()) { string groupName = reader["DataType"].ToString(); // Add a header whenever we hit a new data type if (groupName != currentGroup) { comboItems.Add(new BioDataEntry { GroupName = groupName, IsHeader = true }); currentGroup = groupName; } // Add the data row for this entry comboItems.Add(new BioDataEntry { GroupName = groupName, Identifier = reader["Column1"].ToString(), Label = reader["Column2"].ToString(), IsHeader = false }); } } } }
Step 3: Configure the ComboBox for Custom Drawing
Set up your ComboBox to use owner-drawn mode, then bind the populated list:
// In your form's constructor or load event comboBoxBioData.DropDownStyle = ComboBoxStyle.DropDownList; // Prevent user input comboBoxBioData.DrawMode = DrawMode.OwnerDrawFixed; comboBoxBioData.ItemHeight = 22; // Adjust row height for readability comboBoxBioData.DataSource = comboItems;
Step 4: Implement Custom Drawing
Handle the DrawItem event to render headers differently (bold, distinct color) and data rows as paired values:
private void comboBoxBioData_DrawItem(object sender, DrawItemEventArgs e) { if (e.Index < 0) return; ComboBox combo = sender as ComboBox; BioDataEntry entry = combo.Items[e.Index] as BioDataEntry; // Draw the background e.DrawBackground(); if (entry.IsHeader) { // Render header with bold font and dark color using (Font boldFont = new Font(combo.Font, FontStyle.Bold)) { e.Graphics.DrawString(entry.GroupName, boldFont, Brushes.DarkSlateBlue, e.Bounds); } } else { // Render data row with combined columns string displayText = $"{entry.Identifier} {entry.Label}"; e.Graphics.DrawString(displayText, combo.Font, Brushes.Black, e.Bounds); } // Draw the focus rectangle for selected items e.DrawFocusRectangle(); }
Step 5: Prevent Selecting Header Items
Add a SelectedIndexChanged handler to ensure users can't select header rows (auto-select the next data row instead):
private void comboBoxBioData_SelectedIndexChanged(object sender, EventArgs e) { ComboBox combo = sender as ComboBox; if (combo.SelectedItem is BioDataEntry entry && entry.IsHeader) { // Move selection to the next data item if available if (combo.SelectedIndex + 1 < combo.Items.Count) { combo.SelectedIndex++; } else { combo.SelectedIndex = -1; // Clear selection if no data follows } } }
This setup will give you exactly the grouped layout you described: bold section headers followed by your paired SQL data rows. Just adjust the query, column names, and styling (fonts/colors) to match your specific needs.
内容的提问来源于stack exchange,提问作者Christopher Ross

