VB.Net 异步 UI 线程问题
VB.Net Asynchronous UI Thread Issue
我有一个 Windows 表单,它有一个 Telerik RadGridView,其中有一堆记录要导入到我们的 SQL 数据库中。 RadGridView 仅向用户显示将要导入的记录,以防他们在导入记录之前看到可以解决的易于修复的错误。
我在Winform窗体上有如下控件:
rgImport - Telerik RadGridView 控件
btnSubmit - 按钮控件
ProgressBar1 - 导入记录时的进度条
Label1 - 一些文本告诉用户查看 rgImport
中显示的记录
我正在寻找其中的一些功能 Async/Await,因此 UI 不会被锁定,并且进度条会显示已导入记录的百分比:
对这些函数的两次调用:
DataAPI.HR_Payroll_TimeCards_Insert(博士)
DataAPI.HR_Payroll_TimeCards_Insert_双(博士)
除了将记录注入我们的数据库服务器表和 return 一个关于成功还是失败的布尔值之外什么都不做
Private Sub Import_Payroll_Review_Load(sender As Object, e As EventArgs) Handles Me.Load
rgImport.DataSource = DataAPI.HR_Payroll_Import_GetData()
ProgressBar1.Minimum = 0
ProgressBar1.Maximum = 100
ProgressBar1.Visible = False
End Sub
DataAPI.HR_Payroll_Import_GetData() - 只是 returns 一个数据表,用于加载带有记录的 RadGridView
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Select Case MsgBox("Are you sure you are ready to import?", MsgBoxStyle.YesNo, "Import Records")
Case MsgBoxResult.Yes
'Import the records from the import table
Dim dt As DataTable = DataAPI.HR_Payroll_Import_GetRecordsToImport()
Dim iRows As Long = 1
Dim iTotalRows As Long = dt.Rows.Count
'CallTheInsertMethodHere
For Each dr As DataRow In dt.Rows
If dr("AfterHours") = 0 Then
'This is standard time, so import it into the [TimeCards.Current] table
If DataAPI.HR_Payroll_TimeCards_Insert(dr) Then
'Success
Debug.Print("Inserted Row into TimeCard.Current")
Else
'Failed
Debug.Print("Failed to insert row into TimeCard.Current")
End If
Else
'This is double time, so import it into the [TimeCards.Current.Double] table
If DataAPI.HR_Payroll_TimeCards_Insert_Double(dr) Then
'Success
Debug.Print("Inserted Row into TimeCard.Current.Double")
Else
'Failed
Debug.Print("Failed to insert row into TimeCard.Current.Double")
End If
End If
iRows += 1
Next
MsgBox("Inserted " & iRows - 1 & " rows", vbInformation, "Success")
Case Else
'User clicked on no, so exit
Exit Sub
End Select
End Sub
我只是在寻找 help/direction 如何分解它以及在何处添加 Async/Await 以允许 UI 自由显示百分比的记录已使用 ProgressBar 插入(总记录在变量 iTotalRows 中)
感谢您抽出宝贵时间审阅此内容,以及您可能向我提供的任何意见以使此工作正常进行。
这是 Async/Await
的基本实现,让您了解 UI 在等待 DoWorkAsync 时如何仍然响应,但在 运行 DoWork 时则不然。我包括了 this Stephen Cleary answer
的进展
Private Function DoWorkAsync(progress As IProgress(Of Integer)) As Task(Of Boolean)
Return Task.Run(Function() DoWork(progress))
End Function
Private Function DoWork(progress As IProgress(Of Integer)) As Boolean
Dim i As Integer
While i < 100
i += (New Random).Next(5)
Threading.Thread.Sleep(50)
progress.Report(i)
End While
Return True
End Function
Private Async Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim progress = New Progress(Of Integer)(
Sub(value)
ProgressBar1.Value = Math.Max(Math.Min(value, ProgressBar1.Maximum), ProgressBar1.Minimum)
Console.WriteLine(value)
End Sub)
If MessageBox.Show("Run Async?", "Confirm", MessageBoxButtons.YesNo) = DialogResult.Yes Then
Await DoWorkAsync(progress)
Else
DoWork(progress)
End If
MessageBox.Show("Done")
End Sub
您基本上可以将所有工作放入 DoWork 中,使用成功逻辑(这就是我返回布尔值的原因)并且它不会阻塞 UI。我不会担心为 DataAPI.HR_Payroll_Import_GetData()
、DataAPI.HR_Payroll_Import_GetRecordsToImport()
、DataAPI.HR_Payroll_TimeCards_Insert()
或 DataAPI.HR_Payroll_TimeCards_Insert_Double(dr)
制作单独的函数,除非您的代码的其他部分需要这种粒度。
您可以添加一个 CancellationToken,并使用您自己的代码完成工作,它可能看起来像这样
Private Function DoWorkAsync(progress As IProgress(Of Integer), cancellationToken As Threading.CancellationToken) As Task(Of Boolean)
Return Task.Run(Function() DoWork(progress, cancellationToken))
End Function
Private Function DoWork(progress As IProgress(Of Integer), cancellationToken As Threading.CancellationToken) As Boolean
Try
'Import the records from the import table
Dim dt As DataTable = DataAPI.HR_Payroll_Import_GetRecordsToImport()
Dim iRows As Long = 1
Dim iTotalRows As Long = dt.Rows.Count
'CallTheInsertMethodHere
For Each dr As DataRow In dt.Rows
If dr("AfterHours") = 0 Then
'This is standard time, so import it into the [TimeCards.Current] table
If DataAPI.HR_Payroll_TimeCards_Insert(dr) Then
'Success
Debug.Print("Inserted Row into TimeCard.Current")
Else
'Failed
Debug.Print("Failed to insert row into TimeCard.Current")
End If
Else
'This is double time, so import it into the [TimeCards.Current.Double] table
If DataAPI.HR_Payroll_TimeCards_Insert_Double(dr) Then
'Success
Debug.Print("Inserted Row into TimeCard.Current.Double")
Else
'Failed
Debug.Print("Failed to insert row into TimeCard.Current.Double")
End If
End If
iRows += 1
progress.Report(CInt(100 * iRows / iTotalRows))
cancellationToken.ThrowIfCancellationRequested()
Next
Return True
Catch ex As Exception
Debug.Print(ex.Message)
Return False
End Try
End Function
Private Async Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim progress As IProgress(Of Integer) = New Progress(Of Integer)(
Sub(value)
ProgressBar1.Value = Math.Max(Math.Min(value, ProgressBar1.Maximum), ProgressBar1.Minimum)
Console.WriteLine(value)
End Sub)
cancellationSource = New Threading.CancellationTokenSource()
Dim cancellationToken = cancellationSource.Token
cancellationToken.Register(Sub() progress.Report(0))
If MessageBox.Show("Run Async?", "Confirm", MessageBoxButtons.YesNo) = DialogResult.Yes Then
Await DoWorkAsync(progress, cancellationToken)
Else
DoWork(progress, cancellationToken)
End If
progress.Report(0)
If cancellationToken.IsCancellationRequested Then
MessageBox.Show("Cancelled")
Else
MessageBox.Show("Done")
End If
End Sub
Private cancellationSource As New Threading.CancellationTokenSource()
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
cancellationSource.Cancel()
End Sub
感谢Stephen Cleary for the CancellationToken,再次
我有一个 Windows 表单,它有一个 Telerik RadGridView,其中有一堆记录要导入到我们的 SQL 数据库中。 RadGridView 仅向用户显示将要导入的记录,以防他们在导入记录之前看到可以解决的易于修复的错误。
我在Winform窗体上有如下控件:
rgImport - Telerik RadGridView 控件
btnSubmit - 按钮控件
ProgressBar1 - 导入记录时的进度条
Label1 - 一些文本告诉用户查看 rgImport
我正在寻找其中的一些功能 Async/Await,因此 UI 不会被锁定,并且进度条会显示已导入记录的百分比:
对这些函数的两次调用:
DataAPI.HR_Payroll_TimeCards_Insert(博士)
DataAPI.HR_Payroll_TimeCards_Insert_双(博士)
除了将记录注入我们的数据库服务器表和 return 一个关于成功还是失败的布尔值之外什么都不做
Private Sub Import_Payroll_Review_Load(sender As Object, e As EventArgs) Handles Me.Load
rgImport.DataSource = DataAPI.HR_Payroll_Import_GetData()
ProgressBar1.Minimum = 0
ProgressBar1.Maximum = 100
ProgressBar1.Visible = False
End Sub
DataAPI.HR_Payroll_Import_GetData() - 只是 returns 一个数据表,用于加载带有记录的 RadGridView
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Select Case MsgBox("Are you sure you are ready to import?", MsgBoxStyle.YesNo, "Import Records")
Case MsgBoxResult.Yes
'Import the records from the import table
Dim dt As DataTable = DataAPI.HR_Payroll_Import_GetRecordsToImport()
Dim iRows As Long = 1
Dim iTotalRows As Long = dt.Rows.Count
'CallTheInsertMethodHere
For Each dr As DataRow In dt.Rows
If dr("AfterHours") = 0 Then
'This is standard time, so import it into the [TimeCards.Current] table
If DataAPI.HR_Payroll_TimeCards_Insert(dr) Then
'Success
Debug.Print("Inserted Row into TimeCard.Current")
Else
'Failed
Debug.Print("Failed to insert row into TimeCard.Current")
End If
Else
'This is double time, so import it into the [TimeCards.Current.Double] table
If DataAPI.HR_Payroll_TimeCards_Insert_Double(dr) Then
'Success
Debug.Print("Inserted Row into TimeCard.Current.Double")
Else
'Failed
Debug.Print("Failed to insert row into TimeCard.Current.Double")
End If
End If
iRows += 1
Next
MsgBox("Inserted " & iRows - 1 & " rows", vbInformation, "Success")
Case Else
'User clicked on no, so exit
Exit Sub
End Select
End Sub
我只是在寻找 help/direction 如何分解它以及在何处添加 Async/Await 以允许 UI 自由显示百分比的记录已使用 ProgressBar 插入(总记录在变量 iTotalRows 中)
感谢您抽出宝贵时间审阅此内容,以及您可能向我提供的任何意见以使此工作正常进行。
这是 Async/Await
的基本实现,让您了解 UI 在等待 DoWorkAsync 时如何仍然响应,但在 运行 DoWork 时则不然。我包括了 this Stephen Cleary answer
Private Function DoWorkAsync(progress As IProgress(Of Integer)) As Task(Of Boolean)
Return Task.Run(Function() DoWork(progress))
End Function
Private Function DoWork(progress As IProgress(Of Integer)) As Boolean
Dim i As Integer
While i < 100
i += (New Random).Next(5)
Threading.Thread.Sleep(50)
progress.Report(i)
End While
Return True
End Function
Private Async Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim progress = New Progress(Of Integer)(
Sub(value)
ProgressBar1.Value = Math.Max(Math.Min(value, ProgressBar1.Maximum), ProgressBar1.Minimum)
Console.WriteLine(value)
End Sub)
If MessageBox.Show("Run Async?", "Confirm", MessageBoxButtons.YesNo) = DialogResult.Yes Then
Await DoWorkAsync(progress)
Else
DoWork(progress)
End If
MessageBox.Show("Done")
End Sub
您基本上可以将所有工作放入 DoWork 中,使用成功逻辑(这就是我返回布尔值的原因)并且它不会阻塞 UI。我不会担心为 DataAPI.HR_Payroll_Import_GetData()
、DataAPI.HR_Payroll_Import_GetRecordsToImport()
、DataAPI.HR_Payroll_TimeCards_Insert()
或 DataAPI.HR_Payroll_TimeCards_Insert_Double(dr)
制作单独的函数,除非您的代码的其他部分需要这种粒度。
您可以添加一个 CancellationToken,并使用您自己的代码完成工作,它可能看起来像这样
Private Function DoWorkAsync(progress As IProgress(Of Integer), cancellationToken As Threading.CancellationToken) As Task(Of Boolean)
Return Task.Run(Function() DoWork(progress, cancellationToken))
End Function
Private Function DoWork(progress As IProgress(Of Integer), cancellationToken As Threading.CancellationToken) As Boolean
Try
'Import the records from the import table
Dim dt As DataTable = DataAPI.HR_Payroll_Import_GetRecordsToImport()
Dim iRows As Long = 1
Dim iTotalRows As Long = dt.Rows.Count
'CallTheInsertMethodHere
For Each dr As DataRow In dt.Rows
If dr("AfterHours") = 0 Then
'This is standard time, so import it into the [TimeCards.Current] table
If DataAPI.HR_Payroll_TimeCards_Insert(dr) Then
'Success
Debug.Print("Inserted Row into TimeCard.Current")
Else
'Failed
Debug.Print("Failed to insert row into TimeCard.Current")
End If
Else
'This is double time, so import it into the [TimeCards.Current.Double] table
If DataAPI.HR_Payroll_TimeCards_Insert_Double(dr) Then
'Success
Debug.Print("Inserted Row into TimeCard.Current.Double")
Else
'Failed
Debug.Print("Failed to insert row into TimeCard.Current.Double")
End If
End If
iRows += 1
progress.Report(CInt(100 * iRows / iTotalRows))
cancellationToken.ThrowIfCancellationRequested()
Next
Return True
Catch ex As Exception
Debug.Print(ex.Message)
Return False
End Try
End Function
Private Async Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim progress As IProgress(Of Integer) = New Progress(Of Integer)(
Sub(value)
ProgressBar1.Value = Math.Max(Math.Min(value, ProgressBar1.Maximum), ProgressBar1.Minimum)
Console.WriteLine(value)
End Sub)
cancellationSource = New Threading.CancellationTokenSource()
Dim cancellationToken = cancellationSource.Token
cancellationToken.Register(Sub() progress.Report(0))
If MessageBox.Show("Run Async?", "Confirm", MessageBoxButtons.YesNo) = DialogResult.Yes Then
Await DoWorkAsync(progress, cancellationToken)
Else
DoWork(progress, cancellationToken)
End If
progress.Report(0)
If cancellationToken.IsCancellationRequested Then
MessageBox.Show("Cancelled")
Else
MessageBox.Show("Done")
End If
End Sub
Private cancellationSource As New Threading.CancellationTokenSource()
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
cancellationSource.Cancel()
End Sub
感谢Stephen Cleary for the CancellationToken,再次