You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VB.NET中同一表单兼作查看/插入时DataGridView CellValidated事件报错

问题:VB.NET同一表单实现发票查看/录入时CellValidated事件报错

我尝试用同一个Frm_Invoice表单同时实现发票明细查看和发票录入功能,但在查看模式下点击DataGridView的ProductName列时,CellValidated事件触发报错。请问能否在查看模式下禁用该事件?我的代码是否存在问题?请给予指导。

invoicepage代码

Imports System.ComponentModel
Imports System.Data.OleDb
Imports System.Reflection
Imports Dapper

Public Class invoicepage
    Dim InvoicesService As New InvoicesService()
    Private bindingSource As BindingSource = Nothing
    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        ' To reduce the flickering...
        DataGridView1.GetType().
    GetProperty("DoubleBuffered",
                BindingFlags.Instance Or BindingFlags.NonPublic).
    SetValue(DataGridView1, True)
        DataGridView1.ReadOnly = True
        LoadData()
        DataGridView1.Columns.Add(New DataGridViewLinkColumn With {
.Name = "Colview",
.Text = "👁‍🗨",
.HeaderText = "",
.DisplayIndex = 1,
.UseColumnTextForLinkValue = True,
.ActiveLinkColor = System.Drawing.Color.MediumAquamarine,
.LinkColor = System.Drawing.Color.MediumAquamarine,
.VisitedLinkColor = System.Drawing.Color.MediumAquamarine,
.LinkBehavior = LinkBehavior.NeverUnderline,
.ReadOnly = True,
.DataPropertyName = "Colview"})
    End Sub
    Private Sub LoadData()
        Dim list = InvoicesService.GetInvoices()
        bindingSource = New BindingSource With {.DataSource = New BindingList(Of Invoices)(CType(InvoicesService.GetInvoices(), IList(Of Invoices)))} 
        DataGridView1.DataSource = bindingSource
        DataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells)
    End Sub
    Private Function GetSELECT() As Invoices
        Return If(DataGridView1.SelectedCells.Count = 0, Nothing, TryCast(DataGridView1.SelectedCells(0).OwningRow.DataBoundItem, Invoices))
    End Function
    Private Sub DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
        Dim dgv = DirectCast(sender, DataGridView)
        If e.RowIndex >= 0 AndAlso
        dgv.Columns(e.ColumnIndex) Is DataGridView1.Columns("colview") Then
            Dim SelectDgv = GetSELECT()
            Dim frm = New Frm_Invoice(SelectDgv)
            If (TryCast(Application.OpenForms("Frm_Invoice"), Frm_Invoice)) IsNot Nothing Then
                'Form is already open
                MessageBox.Show("Form is already open")
            Else
                ' Form is not open
                'MessageBox.Show("Form is not open")
                frm.Show()
            End If
        End If
    End Sub
End Class

Frm_Invoice(交易表单)代码

Imports System.ComponentModel
Imports System.Data.OleDb
Imports System.Reflection
Imports Dapper

Public Class Frm_Invoice
    Inherits Form
    Private dictProducts As Dictionary(Of String, ProductFinaltesting)
    Private BindingSource1 As BindingSource = Nothing
    Private BindingSource2 As BindingSource = Nothing
    Dim InvoicesService As New InvoicesService()
    Public Sub New(Invno As Invoices)
        Me.New
        ' To reduce the flickering...
        DataGridView1.GetType().
    GetProperty("DoubleBuffered",
                BindingFlags.Instance Or BindingFlags.NonPublic).
    SetValue(DataGridView1, True)

        DataGridView1.AllowUserToAddRows = False
        BindingSource1 = New BindingSource With {.DataSource = New BindingList(Of InvoicesDetail)(CType(InvoicesService.GetInvoicesdetail(Invno.INVNO), IList(Of InvoicesDetail)))} 
        DataGridView1.DataSource = BindingSource1
        DataGridView1.ReadOnly = True
        TextBox1.Text = Invno.INVNO
    End Sub
    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()
        ' To reduce the flickering...
        DataGridView1.GetType().
    GetProperty("DoubleBuffered",
                BindingFlags.Instance Or BindingFlags.NonPublic).
    SetValue(DataGridView1, True)
        updateDataSource()
    End Sub

    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)

    End Sub
    Protected Sub updateDataSource()
        dictProducts = ProductFinaltestingService.GetDistinctProducts().
    ToDictionary(Function(p) p.ToString(), Function(p) p)
        BindingSource1 = New BindingSource With {.DataSource = New BindingList(Of ProductFinaltesting)}
        DataGridView1.AutoGenerateColumns = False
        DataGridView1.AllowUserToAddRows = False
        DataGridView1.DataSource = BindingSource1
        DataGridView1.Columns("Unit").ReadOnly = True
        DataGridView1.Columns("No").ReadOnly = True
        DataGridView1.Columns("Prp").ReadOnly = True
        DataGridView1.Columns("Prs").ReadOnly = True
        BindingSource1.AddNew()
        DataGridView1.Select()
    End Sub
    Private Sub DataGridView1_CellValidated(
       sender As Object,
       e As DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
        Dim dgv = DirectCast(sender, DataGridView)

        If e.ColumnIndex = dgv.Columns("ProductName").Index Then
            Dim p As ProductFinaltesting = Nothing
            Dim key = dgv(e.ColumnIndex, e.RowIndex).Value?.ToString().Trim()

            If key Is Nothing Then Return
            If dictProducts.TryGetValue(key, p) Then
                Dim boundItem = TryCast(dgv.Rows(e.RowIndex).DataBoundItem, ProductFinaltesting)
'Error below line code
  If p.Unit <> boundItem.Unit Or p.Prp <> boundItem.Prp Or p.Prs <> boundItem.Prs Then
                    boundItem.Unit = p.Unit
                    dgv.UpdateCellValue(dgv.Columns("Unit").Index, e.RowIndex)
                    boundItem.Prp = p.Prp
                    dgv.UpdateCellValue(dgv.Columns("Prp").Index, e.RowIndex)
                    boundItem.Prs = p.Prs
                    dgv.UpdateCellValue(dgv.Columns("Prs").Index, e.RowIndex)

                End If

                dgv.Rows(e.RowIndex).ErrorText = Nothing
                Btnsave.Visible = True
            End If
        End If
    End Sub

End Class

运行效果截图


问题原因与解决方法

核心问题

报错的本质是:

  1. 查看模式下DataGridView绑定的是InvoicesDetail类型数据,但CellValidated事件里强行将其转换为ProductFinaltesting类型,类型不匹配导致空引用异常。
  2. 即使设置了DataGridView为ReadOnly,单元格获得/失去焦点时仍会触发CellValidated事件。

解决方案

1. 添加模式标识字段

在Frm_Invoice类中新增私有字段标记当前模式:

Private isViewMode As Boolean = False

2. 初始化查看模式标识

修改带参数的构造函数,设置查看模式标识:

Public Sub New(Invno As Invoices)
    Me.New()
    ' 原有代码...
    DataGridView1.ReadOnly = True
    TextBox1.Text = Invno.INVNO
    ' 标记为查看模式
    isViewMode = True
End Sub

3. 在CellValidated事件中判断模式

在事件处理逻辑最开头加入判断,查看模式下直接退出:

Private Sub DataGridView1_CellValidated(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
    ' 查看模式下跳过事件逻辑
    If isViewMode Then Return

    ' 原有事件代码...
End Sub

4. 额外优化:避免类型转换异常

即使在录入模式,也应该先判断转换是否成功,防止意外报错:

Dim boundItem = TryCast(dgv.Rows(e.RowIndex).DataBoundItem, ProductFinaltesting)
' 先判断转换是否成功,再执行后续逻辑
If boundItem Is Nothing Then Return
If p.Unit <> boundItem.Unit Or p.Prp <> boundItem.Prp Or p.Prs <> boundItem.Prs Then
    ' 原有赋值代码...
End If

内容的提问来源于stack exchange,提问作者dlaksmi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:44:58