VB.NET中DataGridView新增数据时通知弹窗不触发问题求助
问题描述
正在开发一款以MS Access为数据库的独立应用,当从应用中更新数据时,希望所有运行该应用的其他电脑上的DataGridView数据也能同步更新。我使用了LoadData()方法,但新增行时无法弹出通知弹窗。本人并非专业开发者,恳请各位提供帮助。
附上现有代码:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick LoadData() End Sub Private Sub dgvNotify_RowsAdded(sender As Object, e As DataGridViewRowsAddedEventArgs) Handles dgvNotify.RowsAdded Dim targetRow = Me.dgvNotify.Rows.Cast(Of DataGridViewRow)().OrderByDescending(Function(r) r.Cells("ID").Value).FirstOrDefault If targetRow IsNot Nothing Then targetRow.Selected = True New_Notification() End If End Sub Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing Me.WindowState = FormWindowState.Minimized Me.Visible = False e.Cancel = True End Sub Private Sub New_Notification() NotifyIcon1.BalloonTipTitle = "New Important Notification" NotifyIcon1.BalloonTipText = "Open the notification application to check the notifications" & vbCrLf & "If you have any question reachout to ops leads:)" NotifyIcon1.ShowBalloonTip(3000) End Sub
问题原因
你的代码里用Timer定时调用LoadData(),如果LoadData()是直接把新的DataTable赋值给DataGridView的DataSource,这种方式会一次性替换所有行,不会触发RowsAdded事件——这个事件只有在逐行添加行的时候才会被触发,批量替换数据源不会触发它,所以你的通知弹窗不会弹出。
解决方案
下面提供两种适合非专业开发者的实现方式,核心思路是对比新旧数据,找出新增行后触发通知:
方法一:对比新旧数据集,手动触发通知
这种方式适合数据量较大的场景,依然用批量替换数据源,但提前找出新增行:
- 先声明一个全局变量,保存上一次加载的数据集:
' 全局变量,保存上一次加载的数据 Private lastLoadedData As DataTable = New DataTable()
- 修改
LoadData()方法(替换你原来的LoadData逻辑):
Private Sub LoadData() ' 第一步:从Access获取最新数据,这里替换成你实际取数据的代码 ' 示例:假设你用OleDbConnection连接Access,填充DataTable Dim newData As DataTable = New DataTable() Using conn As New OleDb.OleDbConnection("你的Access连接字符串") conn.Open() Dim cmd As New OleDb.OleDbCommand("SELECT * FROM 你的表名", conn) Dim adapter As New OleDb.OleDbDataAdapter(cmd) adapter.Fill(newData) End Using ' 第一次加载时初始化数据 If lastLoadedData.Rows.Count = 0 Then lastLoadedData = newData.Copy() dgvNotify.DataSource = newData Return End If ' 找出新增的行(假设ID是自增主键,用ID判断) Dim maxLastId As Integer = If(lastLoadedData.Rows.Count > 0, Convert.ToInt32(lastLoadedData.Compute("MAX(ID)", "")), 0) Dim hasNewRows = newData.AsEnumerable().Any(Function(row) Convert.ToInt32(row("ID")) > maxLastId) ' 更新DataGridView的数据源 dgvNotify.DataSource = newData ' 如果有新增行,触发通知并选中最新行 If hasNewRows Then ' 找到ID最大的行并选中 Dim maxNewId = Convert.ToInt32(newData.Compute("MAX(ID)", "")) For Each row As DataGridViewRow In dgvNotify.Rows If Convert.ToInt32(row.Cells("ID").Value) = maxNewId Then row.Selected = True Exit For End If Next New_Notification() End If ' 更新上一次加载的数据 lastLoadedData = newData.Copy() End Sub
方法二:逐行添加新增数据(适合小数据量)
如果你的数据量不大,可以只添加新增的行,这样会触发RowsAdded事件,不用修改原有的事件逻辑:
- 同样先声明全局变量
lastLoadedData,然后修改LoadData():
Private Sub LoadData() Dim newData As DataTable = New DataTable() ' 从Access获取最新数据,代码同方法一 Using conn As New OleDb.OleDbConnection("你的Access连接字符串") conn.Open() Dim cmd As New OleDb.OleDbCommand("SELECT * FROM 你的表名", conn) Dim adapter As New OleDb.OleDbDataAdapter(cmd) adapter.Fill(newData) End Using ' 当前DataGridView绑定的数据 Dim currentData As DataTable = DirectCast(dgvNotify.DataSource, DataTable) ' 第一次加载 If currentData Is Nothing Then dgvNotify.DataSource = newData lastLoadedData = newData.Copy() Return End If ' 找出新增行 Dim maxLastId As Integer = Convert.ToInt32(currentData.Compute("MAX(ID)", "")) Dim newRows = newData.AsEnumerable().Where(Function(row) Convert.ToInt32(row("ID")) > maxLastId) ' 逐行添加到当前数据集,这样会触发RowsAdded事件 For Each row In newRows currentData.ImportRow(row) Next ' 更新上一次加载的数据 lastLoadedData = newData.Copy() End Sub
注意事项
- 确保你的Access表中
ID字段是自增主键,这样才能通过ID判断哪些是新增行 - Timer的间隔不要设置太频繁(比如不要小于5秒),避免频繁访问数据库导致程序卡顿或数据库压力过大
- 可以在取数据的代码里加
Try-Catch捕获异常,防止数据库连接出错导致Timer停止工作
内容的提问来源于stack exchange,提问作者Eragon006
相关产品推荐
相关产品推荐

