DataGridView内嵌ComboBox控件时上下方向键行为异常咨询
问题原因
当前浮动ComboBox方案未做按键消息隔离,上下方向键事件会同时被ComboBox和DataGridView两个控件接收:ComboBox收到事件时会切换下拉选项,DGV收到事件时会切换当前行,二者触发顺序不确定,就出现了行为混乱的问题。另外现有代码还存在两个隐性bug:
- 每次进入列索引2的单元格都会重复往DGV的Controls集合里添加CBTest控件,多次进入后会生成大量重复控件实例
- 用
SendKeys.Send("{F4}")模拟按键展开下拉的方式不稳定,容易和其他按键消息冲突
修复思路
- 窗体初始化阶段就把CBTest添加到DGV控件集合,后续进入单元格只调整位置、控制显示隐藏,不要重复执行Add操作
- 拦截CBTest激活状态下DGV的按键响应,CBTest显示时DGV不处理任何方向键、回车、ESC按键
- 给CBTest添加KeyDown事件处理,统一处理上下键、回车、ESC逻辑,处理完后设置
e.Handled = True阻止消息冒泡到DGV - 替换
SendKeys.Send("{F4}")为直接设置CBTest.DroppedDown = True展开下拉列表,消除模拟按键的不稳定性
修复后可直接运行的代码
Public Class frmSIDDGV Dim nCol As Integer = 0 Dim nRow As Integer = 0 Private Sub frmSIDDGV_Load(sender As Object, e As EventArgs) Handles MyBase.Load With DGV2 .AllowUserToAddRows = False .AllowUserToDeleteRows = False .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill .AutoResizeColumns() .RowTemplate.Height = 40 .RowHeadersVisible = False End With DGV2.ColumnCount = 5 With DGV2 .Columns(0).Name = "Item" .Columns(1).Name = "MRP" .Columns(2).Name = "Qty" .Columns(3).Name = "Unit" .Columns(4).Name = "Amount" End With ' 初始化时一次性添加ComboBox,避免重复生成实例 CBTest.Visible = False DGV2.Controls.Add(CBTest) DGV2.Rows.Add() DGV2(0, 0).Value = 1 End Sub Private Sub DGV2_KeyDown(sender As Object, e As KeyEventArgs) Handles DGV2.KeyDown ' CBTest激活时拦截DGV所有按键响应,彻底避免冲突 If CBTest.Visible Then e.Handled = True Return End If If e.KeyCode = Keys.Enter Then If nCol = DGV2.ColumnCount - 1 Then DGV2.Rows.Add() DGV2.CurrentCell = DGV2(0, nRow + 1) Else DGV2.CurrentCell = DGV2(nCol + 1, nRow) End If e.Handled = True End If End Sub Private Sub DGV2_CurrentCellChanged(sender As Object, e As EventArgs) Handles DGV2.CurrentCellChanged Try nCol = DGV2.CurrentCell.ColumnIndex nRow = DGV2.CurrentCell.RowIndex Catch ex As Exception nCol = 0 nRow = 0 End Try End Sub Private Sub DGV2_CellEnter(sender As Object, e As DataGridViewCellEventArgs) Handles DGV2.CellEnter ' 进入非下拉列时先隐藏CBTest CBTest.Visible = False Select Case e.ColumnIndex Case 2 ' 进入Qty列时显示下拉 Dim oRectangle = DGV2.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, True) CBTest.Size = New Size(oRectangle.Width, oRectangle.Height) CBTest.Location = New Point(oRectangle.X, oRectangle.Y) CBTest.Visible = True ' 直接设置下拉展开状态,替换不稳定的SendKeys方案 CBTest.DroppedDown = True CBTest.SelectedIndex = 0 CBTest.Focus() End Select End Sub ' 新增ComboBox按键独立处理逻辑 Private Sub CBTest_KeyDown(sender As Object, e As KeyEventArgs) Handles CBTest.KeyDown If e.KeyCode = Keys.Enter Then ' 回车确认选择,跳转到下一列 DGV2.Item(nCol, nRow).Value = Trim(CBTest.Text) CBTest.Visible = False CBTest.DroppedDown = False DGV2.Focus() If nCol = DGV2.ColumnCount - 1 Then DGV2.Rows.Add() DGV2.CurrentCell = DGV2(0, nRow + 1) Else DGV2.CurrentCell = DGV2(nCol + 1, nRow) End If e.Handled = True ElseIf e.KeyCode = Keys.Escape Then ' ESC取消选择,返回原单元格 CBTest.SelectedIndex = 0 CBTest.Visible = False CBTest.DroppedDown = False DGV2.CurrentCell = DGV2(nCol, nRow) DGV2.Focus() e.Handled = True ElseIf e.KeyCode = Keys.Up Or e.KeyCode = Keys.Down Then ' 上下键仅在ComboBox下拉项中切换,不传递给DGV e.Handled = True End If End Sub Private Sub CBTest_SelectionChangeCommitted(sender As Object, e As EventArgs) Handles CBTest.SelectionChangeCommitted ' 鼠标点击选择项后的跳转逻辑 With DGV2 .Item(nCol, nRow).Value = Trim(CBTest.Text) CBTest.Visible = False CBTest.DroppedDown = False .Focus() If nCol = DGV2.ColumnCount - 1 Then .Rows.Add() .CurrentCell = .Item(0, nRow + 1) Else .CurrentCell = .Rows(.CurrentRow.Index).Cells(nCol + 1) End If End With End Sub End Class
补充说明
- 原代码用KeyUp事件处理回车逻辑存在响应滞后问题,统一改为KeyDown事件处理,按键响应更跟手
- 所有按键逻辑处理完后设置
e.Handled = True,可以彻底阻断消息向父控件传递,从根源解决按键冲突 - 如果后续需要给其他列也加下拉选择,只需要在CellEnter的Select Case块里新增对应列索引的判断即可
内容的提问来源于stack exchange,提问作者Kirti
相关产品推荐
相关产品推荐

