WinForms:仅为DataGridView首个ComboBox列应用MeasureItem与DrawItem事件
解决WinForms DataGridView多ComboBox列自定义绘制冲突问题
问题场景
开发WinForms项目时,DataGridView包含两个ComboBox列,要求第一列仅显示符合特定条件的项。通过监听EditingControlShowing事件给第一列ComboBox绑定MeasureItem和DrawItem事件实现了自定义绘制,但操作第二列时,本该显示全部项的ComboBox出现异常。
原因分析
DataGridView的编辑控件是复用的,切换到第二列时,之前给第一列ComboBox绑定的自定义绘制事件未被移除,导致第二列的ComboBox也执行了自定义逻辑,从而显示异常。
解决方案
核心思路:在EditingControlShowing事件中,先无条件移除之前绑定的自定义事件,再根据当前列判断是否需要重新绑定并开启自定义绘制,非目标列则恢复默认绘制模式。
修改后的完整代码
public partial class Form1 : Form { public Form1() { InitializeComponent(); DataGridViewComboBoxColumn first_column = new DataGridViewComboBoxColumn(); first_column.Name = "First"; first_column.Items.Add("A"); first_column.Items.Add("B"); first_column.Items.Add("C"); first_column.Items.Add("D"); dataGridView1.Columns.Add(first_column); DataGridViewComboBoxColumn second_column = new DataGridViewComboBoxColumn(); second_column.Name = "Second"; second_column.Items.Add("1"); second_column.Items.Add("2"); second_column.Items.Add("3"); second_column.Items.Add("4"); dataGridView1.Columns.Add(second_column); dataGridView1.EditingControlShowing += DataGridView1_EditingControlShowing; } private void DataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { ComboBox comboBox = e.Control as ComboBox; if (comboBox != null) { // 先移除所有自定义事件绑定,避免控件复用残留事件 comboBox.DrawItem -= ComboBox_DrawItem; comboBox.MeasureItem -= ComboBox_MeasureItem; // 仅对第一列应用自定义绘制逻辑 if (dataGridView1.CurrentCell.ColumnIndex == 0) { comboBox.DrawItem += ComboBox_DrawItem; comboBox.MeasureItem += ComboBox_MeasureItem; comboBox.DrawMode = DrawMode.OwnerDrawVariable; } else { // 恢复第二列的默认绘制模式 comboBox.DrawMode = DrawMode.Normal; } } } private void ComboBox_MeasureItem(object sender, MeasureItemEventArgs e) { if (sender is DataGridViewComboBoxEditingControl comboBox) { if (!canDraw(e.Index)) { e.ItemHeight = 0; } } } private void ComboBox_DrawItem(object sender, DrawItemEventArgs e) { if (sender is DataGridViewComboBoxEditingControl comboBox) { if (canDraw(e.Index)) { Brush foregroundBrush = Brushes.Black; e.DrawBackground(); e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), e.Font, foregroundBrush, e.Bounds, StringFormat.GenericDefault); e.DrawFocusRectangle(); } } } private bool canDraw(int index) { return index > -1 && index % 2 == 0; } }
关键修改点
- 事件清理优先:每次进入编辑状态时,先移除自定义事件绑定,彻底避免控件复用导致的事件残留
- 区分列逻辑:仅对第一列绑定自定义绘制事件,第二列强制恢复
Normal绘制模式,使用默认逻辑 - 简化判断逻辑:移除
MeasureItem和DrawItem方法中多余的列索引判断,因为只有第一列会触发这些事件
内容的提问来源于stack exchange,提问作者mega5800
相关产品推荐
相关产品推荐

