在 VB.NET 中,有没有一种方法可以将未更改单元格的 DataGridView ComboBox 下拉列表更改为新的列表数组,同时保留已更改的单元格?

In VB.NET, is there a way to change DataGridView ComboBox drop downs of unchanged cells to a new list array while preserving changed cells?

我创建 DataGridView1 并填充它,以便每个单元格都是一个 ComboBox,其中包含来自 ComboNames() 的数据,这是文本文件中的名称列表。在主窗体代码中,我声明了一个名为 DropDownArray() 的全局数组,它是 ComboNames() 的副本,因此我可以在其他 Sub 中使用它。当我 test/debug 加载表单时,我可以在表单中执行任何操作而不会出错(有一堆按钮和一个单独的列表框)。但是,一旦我更改网格中的单元格值,它就会中断。我的代码后底部的错误消息。希望我不会尝试做一些无法完成的事情,因为我已经在这个项目上投入了很多时间

DataGridView 设置片段:

        DataGridView1.ColumnCount = 12
        DataGridView1.RowCount = 12
        DataGridView1.RowHeadersWidth = 50
        For row As Integer = 0 To 11
            DataGridView1.Rows(row).HeaderCell.Value = (row + 1).ToString()
            For col As Integer = 0 To 11
                DataGridView1(col, row) = New DataGridViewComboBoxCell
            Next
        Next

组合框设置片段:

        Dim combo As DataGridViewComboBoxCell
        For row = 0 To DataGridView1.Rows.Count - 1
            For col = 0 To DataGridView1.Columns.Count - 1
                combo = DataGridView1(col, row)
                combo.DataSource = ComboNames
            Next
        Next
        Started = 1 

我的失败尝试更新未更改的单元格以具有 UpdatedNamesList() 的新 ComboBox 数据源:

    Private Sub DataGridView1_SelectionChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
        Dim UpdatedNameList As New List(Of String)()
        UpdatedNameList = DropDownArray '!!! DropDownArray() = ComboNames() !!!

        If started = 1 Then
            If DataGridView1.CurrentCell.Value.ToString.Length > 4 Then 'Shortest name is Brian
                GridNames.Add(DataGridView1.CurrentCell.Value.ToString)
            End If
            Dim Difference As IEnumerable(Of String) = DropDownArray.Except(GridNames)
            For x As Integer = 0 To DropDownArray.ToString.Length - 1
                UpdatedNameList(x) = Difference(x)
            Next
            Dim combo As DataGridViewComboBoxCell
            For row = 0 To DataGridView1.Rows.Count - 1
                For col = 0 To DataGridView1.Columns.Count - 1
                    combo = DataGridView1(col, row)
                    If combo.Value.ToString.Length < 4 Then
                        combo.DataSource = UpdatedNameList
                    End If
                Next
            Next
        End If
    End Sub

我收到一个没有引用我的代码的中断错误,所以我不知道我搞砸了什么。错误文本如下:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

NameGrid.Form1.DataGridView1_SelectionChanged(Object, System.Windows.Forms.DataGridViewCellEventArgs) in Form1.vb

很难给出一个好的答案,因为我觉得可能没有一个好的答案。我不想阻止您尝试做的事情,但是我希望我可以让您更好地了解实现您的要求所需的条件。基本上这是我对你之前问题的评论(你忽略了)的延续......

对于初学者来说,代码将组合框添加到网格中的单元格的方式是......。只是错了。请让我澄清一下……

DataGridView 有一列“TYPE”专门用于“组合框”。它被称为 DataGridViewComboBoxColumn 并且是您应该使用的。相反,您的代码是将 DataGridViewTextBoxColumn 添加到网格,然后将 DataGridViewComboBoxCell 放入每个 Text Box 单元格中……? ……这可能很有效,但是它只会使事情复杂化并为您“创造”更多的工作。有更好的方法。

示例,查看 DataGridView 设置的第一段代码…

DataGridView1.ColumnCount = 12
DataGridView1.RowCount = 12
DataGridView1.RowHeadersWidth = 50
For row As Integer = 0 To 11
  DataGridView1.Rows(row).HeaderCell.Value = (row + 1).ToString()
  For col As Integer = 0 To 11
    DataGridView1(col, row) = New DataGridViewComboBoxCell
  Next
Next

你应该注意这段代码的第一行…

DataGridView1.ColumnCount = 12

这是添加 12 个“TEXT BOX”列,您应该添加 12 个“COMBO BOX”列。如果您使用 DataGridViewComboBoxColumn 而不是 DataGridViewTextBoxColumn,那么您可以删除循环中添加 DataGridViewComboBoxCell

的代码行
DataGridView1(col, row) = New DataGridViewComboBoxCell

网格将“自动”为组合框列中的每个单元格创建一个 DataGridViewComboBoxCell。如果添加新行,它会自动添加 DataGridViewComboBoxCells。因此,使用 DataGridViewComboBoxColumn 可能看起来像……

For index = 1 To 12
  DataGridView1.Columns.Add(New DataGridViewComboBoxColumn())
Next
DataGridView1.RowCount = 12
DataGridView1.RowHeadersWidth = 50
For row As Integer = 0 To 11
  DataGridView1.Rows(row).HeaderCell.Value = (row + 1).ToString()
Next

如果你 运行 上面的代码,你会注意到每个单元格“已经”是一个 DataGridViewComboBoxCell 并且你的代码不需要添加一个。此外,由于您的代码使用的是 TextBoxColumn 这可能是代码失败的原因之一...... combo = DataGridView1(col, row) ......

接下来……关于具有不同值的组合框……您需要记住,您要管理 12x12=144 个组合框!如果所有 144 个组合框项目列表都包含相同的值,那么它是相当简单的,我们可以轻松地将每个组合框数据源设置为相同的项目列表。但是,将每个 DataGridViewComboBoxColumnDataSource 设置为同一个列表会更容易,并且每列将对该列中的所有组合框单元格使用此列表。因此,您可以更改上面的代码,将组合框列添加到…

For index = 1 To 12
  Dim col = New DataGridViewComboBoxColumn()
  col.DataSource = ComboNames
  DataGridView1.Columns.Add(col)
Next

这将消除对第二个代码片段的需要 ComboBox Setup

但是,我的理解是您想要...如果用户“selects”一个组合框中的特定项目,那么,该 selected 项目将不会在任何组合框中可用其他组合框项目列表。

这当然可行;但是,应该清楚的是……如果每个组合框都有不同的值 selected……那么从技术上讲……必须有 144 个不同的数据源!每个单独的组合框数据源都必须是唯一的。

如果我们继续……让我们仔细看看当用户更改组合框的 selected 值时涉及的内容。最初,当加载表单时,所有组合框都是空的,并且每个组合框都使用“相同”的数据源。

现在,用户更改了其中一个“空”组合框中的 selected 值……一旦用户 select 设置了一个值,那么网格的一个事件就会触发,让我们知道哪个单元格值已更改。假设用户 select 在组合框中输入了“选择 1”。由于所有组合框都使用相同的数据源并包含相同的值,因此我们可以简单地从该项目列表中“删除选项 1”。

但是,如果我们从列表中删除“选择 1”项……那么当前具有值“选择 1”的组合框 selected 将失败,您将获得网格 DataError 因为“选择 1”不会出现在项目的组合框列表中。因此,我们只需要将“选择 1”添加到项目的组合框列表中。这使得组合框数据源成为唯一的,我们不能再使用其他(非selected)组合框使用的相同数据源。因此,让我们假设我们专门为该组合框创建了一个唯一的项目列表并继续。

现在用户 select 有一个不同的组合框。我们知道“选择 1”不会成为组合框项目列表中的项目,因为我们已将其从列表中删除。继续,让我们假设用户 select 编辑了“选择 3”,并且网格更改了事件触发,我们像之前一样进行……我们从使用相同数据源的所有组合框的列表中删除“选择 3”并将“Choice 3”添加到当前的组合框项目列表中。然而……

我们还需要从任何已经 select 编辑了项目的组合框中删除“选择 3”。我们需要为每个先前更改的组合框执行此操作,因为这些组合框有自己的唯一项目列表。因此,随着用户更改越来越多的组合框,代码必须做越来越多的工作。

另请注意,前面的示例“假设”当用户 select 组合框中的项目时,组合框中的 selected 项目最初是“空的”......意味着之前没有任何内容 selected。如果用户 select 为组合框编辑了一个值,稍后又将该 select 编辑的值“更改”为不同的值……那么……我们需要 PUT BACK 之前的 selected值,因此其他组合框可以 select 该值。这将包括任何先前更改的组合框!

我希望您可以从前面的示例中看出,以您描述的方式管理 144 个组合框并非易事。最初,当所有组合框都相同时……这是微不足道的……但是,随着用户更改越来越多的组合框,当组合框更改时可能会看到一个缓慢的 UI 也就不足为奇了。

请原谅我的哈运行,我只是想帮忙。应该很清楚,根据需要管理组合框绝非易事,尤其是使用 DataGridViewComboBoxCell。我希望我清理了一些东西。

最后,我不得不问你是否认真坐下来实际“使用”了你的 12x12 组合框网格......我 运行 进行了一些测试,只能代表我自己......但是......作为一个用户……看着 144 个组合框并不愉快。我不确定总体目标是什么,但是从用户的角度来看,我认为 12x12 组合框网格是一个有问题的 UI 选择。我们已经知道它的实现绝非易事,如果您竭尽全力使其发挥作用却发现用户讨厌它,那将会很糟糕。

要点是……听起来您可能需要回到“数据设计”阶段。您想做的事情并没有那么不寻常,但是组合框太多了,每个组合框中的项目也太多了……这是“真正的”问题,这是一个“数据设计”问题……不是 UI 问题。祝你好运。

我解决了我的问题。它很慢而且效率很低,但这是另一个问题页面。我最初遇到的问题是更改 DataGridView 中“未更改”单元格的数据源,同时保留已更改的单元格。很容易做的事。但是我的代码中的问题是对以下内容的调用:

If DataGridView1.CurrentCell.Value.ToString.Length > 4 Then

在未被触及的单元格上没有长度。如果您尝试在“未更改”的单元格上调用它,它会中断但不会引用任何代码。因此,我在 CellValueChanged 上的每个单元格上循环并执行 Try Catch 因此,如果失败,我将 DataSource 更改为新数组,如果没有,我只需将值添加到 运行 列表网格中的条目。它很慢(单元格更改和允许我更改下一个单元格之间大约需要 4 秒)但有效。我对优化持开放态度,但同样,答案是“不要尝试获取未更改单元格的长度”。

正在工作 用于更新未更改单元格以具有 UpdatedNamesList() 的新组合框数据源的代码:

Private Sub DataGridView1_SelectionChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
    Dim UpdatedNameList As List(Of String) = DropDownArray
    Dim combo2 As DataGridViewComboBoxCell
    If started = 1 Then
        For row = 0 To DataGridView1.Rows.Count - 1
            For col = 0 To DataGridView1.Columns.Count - 1
                Try
                    If DataGridView1(col, row).Value.ToString.Length >= 4 Then
                        GridNames.Add(DataGridView1(col, row).Value.ToString)
                    End If
                Catch
                    Dim Difference As IEnumerable(Of String) = DropDownArray.Except(GridNames)
                    For x As Integer = 0 To DropDownArray.ToString.Length - 1
                        UpdatedNameList(x) = Difference(x)
                    Next
                    combo2 = DataGridView1(col, row)
                    combo2.DataSource = UpdatedNameList
                End Try
            Next
        Next
    End If
End Sub