设置 DataSource 属性 后,无法修改 DatagridviewCombobox Items 集合
DatagridviewCombobox Items collection cannot be modified when the DataSource property is set
我有一个使用 VB.NET 的 windows 表单应用程序,旨在升级 IIS Web 应用程序。
该应用程序有一个显示要升级的网络应用程序列表的数据网格视图。其中一列是 DataGridViewComboBoxColumn。此组合框将其数据源 属性 设置为数据表。我没有为组合框使用绑定源。
想法是,用户 select 更新了 datagridview 行中组合框的值之一和同一 datagridview 行中其他几个单元格的值。
EditingControlShowing 事件处理程序设置为设置组合框的 SelectedIndexChanged 事件。
Private Sub dgvWebApps_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgvWebApps.EditingControlShowing
If dgvWebApps.CurrentCell.ColumnIndex = 5 Then
Dim comboBox As ComboBox = CType(e.Control, ComboBox)
If comboBox IsNot Nothing Then
RemoveHandler comboBox.SelectedIndexChanged, AddressOf ComboBox_Value_Changed
AddHandler comboBox.SelectedIndexChanged, AddressOf ComboBox_Value_Changed
e.CellStyle.BackColor = clrLightYellow
End If
End If
End Sub
ComboBox_Value_Changed 是更新其他单元格值的子例程。
Private Sub ComboBox_Value_Changed(sender As Object, e As EventArgs)
dgvWebApps.CurrentRow.Cells(frmMain.cnstNewVersion).Value = GetNewVersion(sender.Text)
dgvWebApps.CurrentRow.Cells(frmMain.cnstSourcePath).Value = strSourcePath
dgvWebApps.CurrentRow.Cells(frmMain.cnstSourceMediaFileUpdated).Value = True
dgvWebApps.CurrentCell = dgvWebApps.CurrentRow.Cells(frmMain.cnstSourceMediaFile)
End Sub
还处理了 datagridview 的 CurrentCellDirtyStateChanged 事件,以便在 datagridview 的 EditMode 属性 设置为 EditOnEnter 时立即提交当前编辑,并更新当前 datagridview 行中单元格的值。
Private Sub dgvWebApps_CurrentCellDirtyStateChanged(sender As Object, e As EventArgs) Handles dgvWebApps.CurrentCellDirtyStateChanged
If dgvWebApps.CurrentCell.ColumnIndex = 1 Then
dgvWebApps.CurrentRow.Cells(frmMain.cnstLocalPath).Value = Path.Combine(strWebsitePhysicalPath, dgvWebApps.CurrentCell.Value)
End If
If dgvWebApps.IsCurrentCellDirty Then
dgvWebApps.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
这通常工作正常。我可以 select 来自 datagridview 和其他单元格中组合框的不同值,同时移动到不同的单元格。
我遇到的问题是在用户更改组合框的值,然后将表单上的焦点更改为 datagridview 以外的其他内容之后,然后单击返回组合框之一。这是当 datagridview 的 DataError 事件被触发时,我得到了一些相同的错误。
这是错误:
错误消息: 设置数据源 属性 后无法修改项目集合。
错误堆栈跟踪: 在 System.Windows.Forms.ComboBox.CheckNoDataSource()
在 System.Windows.Forms.DataGridViewComboBoxCell.InitializeEditingControl(Int32 rowIndex, Object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
在 System.Windows.Forms.DataGridView.InitializeEditingControlValue(DataGridViewCellStyle& dataGridViewCellStyle, DataGridViewCell dataGridViewCell)
该错误显然意味着组合框的项目集合在设置数据源 属性 时被修改。但是,代码中的任何地方都没有添加、删除或清除组合框的项目集合。
如果我继续处理所有错误,或者只是在数据网格的 DataError 事件期间没有弹出任何消息,一切似乎都在按预期进行。我可以继续 select 从组合框等输入值。但是,如果我再次改变焦点并返回到组合框,错误再次发生。
据我所知,是 ComboBox_Value_Changed 子例程中其他单元格的值变化导致了错误。删除这些会使错误消失。
现在,如果我使用 SelectionChangeCommitted 事件而不是组合框的 SelectedIndexChanged 事件来调用 ComboBox_Value_Changed 子例程,则不会发生此错误。这是我最初使用的事件,但它并没有随着表单上的某些用户交互而持续出现。这就是我在对这些行为进行一些研究后切换到 SelectedIndexChanged 事件的原因。
我不确定为什么在这些情况下会修改组合框的项目集合。如前所述,我可以只处理 datagridview 的 DataError 事件而不生成任何消息,一切似乎都按预期工作。如果可能的话,我只想知道这里发生了什么。如果不必要的话,我不喜欢在错误发生时抑制它们。
首先,我强烈建议您在“编译”选项中打开“Option Strict”。您当前的代码不会在当前状态下编译。似乎有一些幕后演员在进行,这可能会带来问题。在任何情况下,最好打开“Option Strict”选项。
我相信您的问题在于 EditingControlShowing
事件中创建的 ComboBox
订阅的 SelectedIndexChanged
事件永远不会取消订阅。这将导致 ComboBox_SelectedIndexChanged
事件在不应该触发的时候触发。在不取消订阅的情况下,我相信该事件会被触发更多次我们需要或在这种情况下想要的次数。因此,当用户“离开”组合框单元格时,您需要取消订阅组合框事件。
幸运的是,有一个简单的解决方案可用。首先,我们需要“全局”访问在 EditingControlShowing
事件 comboBox
中创建的 ComboBox
。因此,将其设为“全局”ComboBox
变量……
Dim comboBox As ComboBox
然后将 EditingControlShowing
事件中的 comboBox
代码分配更改为类似...
comboBox = CType(e.Control, ComboBox)
现在我们可以“全局”访问 comboBox
,我们可以取消订阅网格 CellLeave
事件中的 ComboBox_SelecedIndecChanged
事件。这个事件可能看起来像……
Private Sub dgvWebApps_CellLeave(sender As Object, e As DataGridViewCellEventArgs) Handles dgvWebApps.CellLeave
If e.ColumnIndex = 5 Then
RemoveHandler comboBox.SelectedIndexChanged, AddressOf ComboBox_Value_Changed
End If
End Sub
我相信这会解决您的问题。如果您需要更多详细信息,请告诉我。祝你好运。
根据 OP 评论编辑
打开 Strict
选项对您来说确实是一个好处。它会查找可能导致问题的事情,这些问题可能并不明显。
例如,在您当前的代码中,如果您打开“严格”,它将标记该行...
sender.Text
在 ComboBox_SelectedIndexChanged
事件中作为一个问题。它将显示一条错误消息,内容类似于……“不允许后期绑定的严格选项。”
是什么意思,就是一个对象是automatically/behind场景获取CAST到不同的对象。具体来说 sender
到 ComboBox
。在强类型语言中,这种转换必须显式完成。原因是这对您有帮助。编译器只是在说...... “嘿......你确定对象 (sender
) 会像你期望的那样正确地转换为 ComboBox
吗?”
在这种特殊情况下,sender
是一个 Object
… 而 Object
没有 Text
属性。因此,编译器会将“automatically/behind 场景”cast/narrow sender
对象转换为 ComboBox
,因为那显然是 sender
。
诚然,这当然很方便,但是,这种情况并不少见 automatic/behind 对象的场景缩小到您可能意想不到的程度。因此来自编译器的危险信号。强类型语言不会自动为您执行此转换。从编码的角度来看……这是有道理的,我更愿意这样来确保我的类型正是我所期望的。
在所有情况下,当您遇到使用“严格”选项抛出的错误时,就像我们在这里遇到的那样,几乎总是可以通过显式执行此转换来轻松修复它们。在这种情况下,要摆脱延迟绑定问题,请将代码更改为...
Dim cb As ComboBox = CType(sender, ComboBox)
DataGridView1.CurrentRow.Cells(1).Value = GetNewVersion(cb.Text)
…此更改将消除错误。在我们开始时,我的观点是,这可以帮助您避免可能的错误。因此,最好遵守 ON 编译器接受的“严格”选项。此外,打开“严格”并修复错误将允许代码编译,即使“严格”已关闭。只是一个想法。
我有一个使用 VB.NET 的 windows 表单应用程序,旨在升级 IIS Web 应用程序。
该应用程序有一个显示要升级的网络应用程序列表的数据网格视图。其中一列是 DataGridViewComboBoxColumn。此组合框将其数据源 属性 设置为数据表。我没有为组合框使用绑定源。
想法是,用户 select 更新了 datagridview 行中组合框的值之一和同一 datagridview 行中其他几个单元格的值。
EditingControlShowing 事件处理程序设置为设置组合框的 SelectedIndexChanged 事件。
Private Sub dgvWebApps_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgvWebApps.EditingControlShowing
If dgvWebApps.CurrentCell.ColumnIndex = 5 Then
Dim comboBox As ComboBox = CType(e.Control, ComboBox)
If comboBox IsNot Nothing Then
RemoveHandler comboBox.SelectedIndexChanged, AddressOf ComboBox_Value_Changed
AddHandler comboBox.SelectedIndexChanged, AddressOf ComboBox_Value_Changed
e.CellStyle.BackColor = clrLightYellow
End If
End If
End Sub
ComboBox_Value_Changed 是更新其他单元格值的子例程。
Private Sub ComboBox_Value_Changed(sender As Object, e As EventArgs)
dgvWebApps.CurrentRow.Cells(frmMain.cnstNewVersion).Value = GetNewVersion(sender.Text)
dgvWebApps.CurrentRow.Cells(frmMain.cnstSourcePath).Value = strSourcePath
dgvWebApps.CurrentRow.Cells(frmMain.cnstSourceMediaFileUpdated).Value = True
dgvWebApps.CurrentCell = dgvWebApps.CurrentRow.Cells(frmMain.cnstSourceMediaFile)
End Sub
还处理了 datagridview 的 CurrentCellDirtyStateChanged 事件,以便在 datagridview 的 EditMode 属性 设置为 EditOnEnter 时立即提交当前编辑,并更新当前 datagridview 行中单元格的值。
Private Sub dgvWebApps_CurrentCellDirtyStateChanged(sender As Object, e As EventArgs) Handles dgvWebApps.CurrentCellDirtyStateChanged
If dgvWebApps.CurrentCell.ColumnIndex = 1 Then
dgvWebApps.CurrentRow.Cells(frmMain.cnstLocalPath).Value = Path.Combine(strWebsitePhysicalPath, dgvWebApps.CurrentCell.Value)
End If
If dgvWebApps.IsCurrentCellDirty Then
dgvWebApps.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
这通常工作正常。我可以 select 来自 datagridview 和其他单元格中组合框的不同值,同时移动到不同的单元格。
我遇到的问题是在用户更改组合框的值,然后将表单上的焦点更改为 datagridview 以外的其他内容之后,然后单击返回组合框之一。这是当 datagridview 的 DataError 事件被触发时,我得到了一些相同的错误。
这是错误:
错误消息: 设置数据源 属性 后无法修改项目集合。
错误堆栈跟踪: 在 System.Windows.Forms.ComboBox.CheckNoDataSource() 在 System.Windows.Forms.DataGridViewComboBoxCell.InitializeEditingControl(Int32 rowIndex, Object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) 在 System.Windows.Forms.DataGridView.InitializeEditingControlValue(DataGridViewCellStyle& dataGridViewCellStyle, DataGridViewCell dataGridViewCell)
该错误显然意味着组合框的项目集合在设置数据源 属性 时被修改。但是,代码中的任何地方都没有添加、删除或清除组合框的项目集合。
如果我继续处理所有错误,或者只是在数据网格的 DataError 事件期间没有弹出任何消息,一切似乎都在按预期进行。我可以继续 select 从组合框等输入值。但是,如果我再次改变焦点并返回到组合框,错误再次发生。
据我所知,是 ComboBox_Value_Changed 子例程中其他单元格的值变化导致了错误。删除这些会使错误消失。
现在,如果我使用 SelectionChangeCommitted 事件而不是组合框的 SelectedIndexChanged 事件来调用 ComboBox_Value_Changed 子例程,则不会发生此错误。这是我最初使用的事件,但它并没有随着表单上的某些用户交互而持续出现。这就是我在对这些行为进行一些研究后切换到 SelectedIndexChanged 事件的原因。
我不确定为什么在这些情况下会修改组合框的项目集合。如前所述,我可以只处理 datagridview 的 DataError 事件而不生成任何消息,一切似乎都按预期工作。如果可能的话,我只想知道这里发生了什么。如果不必要的话,我不喜欢在错误发生时抑制它们。
首先,我强烈建议您在“编译”选项中打开“Option Strict”。您当前的代码不会在当前状态下编译。似乎有一些幕后演员在进行,这可能会带来问题。在任何情况下,最好打开“Option Strict”选项。
我相信您的问题在于 EditingControlShowing
事件中创建的 ComboBox
订阅的 SelectedIndexChanged
事件永远不会取消订阅。这将导致 ComboBox_SelectedIndexChanged
事件在不应该触发的时候触发。在不取消订阅的情况下,我相信该事件会被触发更多次我们需要或在这种情况下想要的次数。因此,当用户“离开”组合框单元格时,您需要取消订阅组合框事件。
幸运的是,有一个简单的解决方案可用。首先,我们需要“全局”访问在 EditingControlShowing
事件 comboBox
中创建的 ComboBox
。因此,将其设为“全局”ComboBox
变量……
Dim comboBox As ComboBox
然后将 EditingControlShowing
事件中的 comboBox
代码分配更改为类似...
comboBox = CType(e.Control, ComboBox)
现在我们可以“全局”访问 comboBox
,我们可以取消订阅网格 CellLeave
事件中的 ComboBox_SelecedIndecChanged
事件。这个事件可能看起来像……
Private Sub dgvWebApps_CellLeave(sender As Object, e As DataGridViewCellEventArgs) Handles dgvWebApps.CellLeave
If e.ColumnIndex = 5 Then
RemoveHandler comboBox.SelectedIndexChanged, AddressOf ComboBox_Value_Changed
End If
End Sub
我相信这会解决您的问题。如果您需要更多详细信息,请告诉我。祝你好运。
根据 OP 评论编辑
打开 Strict
选项对您来说确实是一个好处。它会查找可能导致问题的事情,这些问题可能并不明显。
例如,在您当前的代码中,如果您打开“严格”,它将标记该行...
sender.Text
在 ComboBox_SelectedIndexChanged
事件中作为一个问题。它将显示一条错误消息,内容类似于……“不允许后期绑定的严格选项。”
是什么意思,就是一个对象是automatically/behind场景获取CAST到不同的对象。具体来说 sender
到 ComboBox
。在强类型语言中,这种转换必须显式完成。原因是这对您有帮助。编译器只是在说...... “嘿......你确定对象 (sender
) 会像你期望的那样正确地转换为 ComboBox
吗?”
在这种特殊情况下,sender
是一个 Object
… 而 Object
没有 Text
属性。因此,编译器会将“automatically/behind 场景”cast/narrow sender
对象转换为 ComboBox
,因为那显然是 sender
。
诚然,这当然很方便,但是,这种情况并不少见 automatic/behind 对象的场景缩小到您可能意想不到的程度。因此来自编译器的危险信号。强类型语言不会自动为您执行此转换。从编码的角度来看……这是有道理的,我更愿意这样来确保我的类型正是我所期望的。
在所有情况下,当您遇到使用“严格”选项抛出的错误时,就像我们在这里遇到的那样,几乎总是可以通过显式执行此转换来轻松修复它们。在这种情况下,要摆脱延迟绑定问题,请将代码更改为...
Dim cb As ComboBox = CType(sender, ComboBox)
DataGridView1.CurrentRow.Cells(1).Value = GetNewVersion(cb.Text)
…此更改将消除错误。在我们开始时,我的观点是,这可以帮助您避免可能的错误。因此,最好遵守 ON 编译器接受的“严格”选项。此外,打开“严格”并修复错误将允许代码编译,即使“严格”已关闭。只是一个想法。