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

扫描条码后SQL返回单条记录,如何向DataGridView追加新行而非替换?

问题描述

开发了一款条码扫描应用,流程如下:

  • 用户通过条码扫描器将条码输入文本框(扫描器自动发送CR/LF,无需手动提交)
  • 扫描后通过SQL查询验证条码,查询每次仅返回数据库中一行数据
  • 需要将该行数据添加到DataGridView,最终DataGridView中每行对应一个扫描的条码

当前实现的VB.NET代码片段如下:

Public Function GetResults() As DataTable

        Dim dtResults As New DataTable
        Dim dtScanned As New DataTable
        Dim connString As String = "Data Source=SQL03\SQL2019;Initial Catalog=aa"

        Using conn As New SqlConnection(connString)
            Using cmd As New SqlCommand("
                SELECT requests.request_id AS 'Request ID', patients.first_name AS GivenName, patients.surname AS Surname,
                concat(patients.day_birth, '/', patients.month_birth, '/', patients.year_birth) AS DOB, patients.sex AS 'Sex',
                services.service_abbrev AS 'Ref.Service', specimens.specimen AS 'Specimen', referral_details.referral_dt AS 'Referral Date'
                
                FROM
                { insert a bunch of joins here }

                WHERE
                    referral_details.referral_dt >= @StartDate AND
                    referral_details.referral_dt <= @EndDate AND
                    labs.lab_id = '46' AND
                    requests.request_id = @LabID
                
                ORDER BY
                    labs.lab_name ASC,
                    requests.request_id ASC,
                    services.service_abbrev ASC

                ", conn)
                cmd.Parameters.Add("@StartDate", SqlDbType.DateTime).Value = dateFrom.Value
                cmd.Parameters.Add("@EndDate", SqlDbType.DateTime).Value = DateTo.Value
                cmd.Parameters.Add("@LabID", SqlDbType.Int).Value = txtBarcode.Text
                cmd.CommandTimeout = 0

                Try
                    conn.Open()
                    Dim reader As SqlDataReader = cmd.ExecuteReader()
                    dtResults.Load(reader)

                    If dtResults.Rows.Count = 0 Then
                        MessageBox.Show("No referral found for this Lab ID.", "No Referral", MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Else
                        Return dtResults
                    End If

                Catch Ex As Exception
                    MessageBox.Show("Unable to connect to SQL Server. Please check your network connection and if required, report the issue to IT." & vbCrLf & vbCrLf & Ex.Message, "No Connection!", MessageBoxButtons.OK, MessageBoxIcon.Error)
                End Try
            End Using

        End Using

        For Each dr As DataRow In dtResults.Rows
            dtScanned.ImportRow(dr)

        Next

        Return dtScanned

    End Function

目前遇到的问题:每次扫描条码时,DataGridView中的第一行都会被新数据替换。已知原因是每次扫描都重新创建数据表,但不清楚如何持久存储每次扫描的数据,让新数据作为新增行显示在DataGridView中。

解决方案

核心问题分析

当前代码每次调用GetResults()时,都会在函数内部新建dtScanned和dtResults,返回的始终是单条数据的新表,绑定到DataGridView自然会覆盖之前的内容。要实现累加行的效果,需要把存储扫描历史的DataTable提升为类级成员变量,而非每次在函数内重新创建。

具体实现步骤

  1. 定义类级DataTable
    在窗体类的顶部(所有方法之外)声明用于存储所有扫描数据的DataTable,确保它在窗体生命周期内持续存在:
Private dtScanned As DataTable ' 类级变量,保存所有扫描到的数据
  1. 初始化DataTable结构并绑定数据源
    在窗体的Load事件中初始化结构,并绑定到DataGridView:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    dtScanned = New DataTable()
    ' 可以手动匹配SQL查询的列结构,也可以在第一次查询后自动复制结构
    DataGridView1.DataSource = dtScanned
End Sub
  1. 修改GetResults()函数,仅返回单条查询结果
    调整函数逻辑,不再返回整个DataTable,而是返回查询到的单条DataRow:
Public Function GetResults() As DataRow
    Dim dtResults As New DataTable
    Dim connString As String = "Data Source=SQL03\SQL2019;Initial Catalog=aa"

    Using conn As New SqlConnection(connString)
        Using cmd As New SqlCommand("
            SELECT requests.request_id AS 'Request ID', patients.first_name AS GivenName, patients.surname AS Surname,
            concat(patients.day_birth, '/', patients.month_birth, '/', patients.year_birth) AS DOB, patients.sex AS 'Sex',
            services.service_abbrev AS 'Ref.Service', specimens.specimen AS 'Specimen', referral_details.referral_dt AS 'Referral Date'
            
            FROM
            { insert a bunch of joins here }

            WHERE
                referral_details.referral_dt >= @StartDate AND
                referral_details.referral_dt <= @EndDate AND
                labs.lab_id = '46' AND
                requests.request_id = @LabID
            
            ORDER BY
                labs.lab_name ASC,
                requests.request_id ASC,
                services.service_abbrev ASC

            ", conn)
            cmd.Parameters.Add("@StartDate", SqlDbType.DateTime).Value = dateFrom.Value
            cmd.Parameters.Add("@EndDate", SqlDbType.DateTime).Value = DateTo.Value
            cmd.Parameters.Add("@LabID", SqlDbType.Int).Value = txtBarcode.Text
            cmd.CommandTimeout = 0

            Try
                conn.Open()
                Dim reader As SqlDataReader = cmd.ExecuteReader()
                dtResults.Load(reader)

                If dtResults.Rows.Count = 0 Then
                    MessageBox.Show("No referral found for this Lab ID.", "No Referral", MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Return Nothing
                Else
                    Return dtResults.Rows(0) ' 返回单条查询结果
                End If

            Catch Ex As Exception
                MessageBox.Show("Unable to connect to SQL Server. Please check your network connection and if required, report the issue to IT." & vbCrLf & vbCrLf & Ex.Message, "No Connection!", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return Nothing
            End Try
        End Using
    End Using
End Function
  1. 处理扫描事件,添加行到类级DataTable
    监听文本框的Enter键(扫描器自动发送的CR/LF),调用查询并将结果添加到dtScanned:
Private Sub txtBarcode_KeyDown(sender As Object, e As KeyEventArgs) Handles txtBarcode.KeyDown
    If e.KeyCode = Keys.Enter Then
        Dim newRow As DataRow = GetResults()
        If newRow IsNot Nothing Then
            ' 首次扫描时自动复制查询结果的表结构
            If dtScanned.Columns.Count = 0 Then
                dtScanned = newRow.Table.Clone()
                DataGridView1.DataSource = dtScanned
            End If
            dtScanned.ImportRow(newRow) ' 将新行添加到历史表
            txtBarcode.Clear() ' 清空文本框准备下一次扫描
        End If
        e.Handled = True ' 阻止Enter键的默认行为
    End If
End Sub

更优实现建议

  • 防重复扫描:添加行前检查dtScanned中是否已存在相同的Request ID,避免重复数据。
  • 使用BindingSource:用BindingSource作为中间层绑定数据源,能更灵活地处理数据更新,无需手动刷新DataGridView:
Private bsScanned As New BindingSource()

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    bsScanned.DataSource = dtScanned
    DataGridView1.DataSource = bsScanned
End Sub
  • 异步查询:使用ExecuteReaderAsync实现异步查询,避免UI卡顿,提升扫描体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 22:05:40