1. 程式人生 > >VB.net WinForm如何寫一個分執行緒進度條

VB.net WinForm如何寫一個分執行緒進度條

首先, 我們先來看一個長耗時過程

    Private Sub ValueTest()       
        For i As Integer = 0 To 100
            ProgressBar1.Value = i 
            Threading.Thread.Sleep(1000)
        Next
    End Sub

我們會發現,在100時間內,窗體無法被移動,進度條也不會移動(win10的UI可以移動,但任然看起來很卡),等迴圈完畢後進度條突然就滿了。 懂UI窗體執行原理的都知道,程式的主執行緒不僅僅要處理我們寫的程式碼,還要重新整理UI的介面。當在處理長耗時過程時,UI介面也會被“鎖死”,直到過程被執行完畢。 有些懂執行緒委託的小朋友就要說了,用分執行緒委託嘛,例如這樣:

  Private Sub ValueTest_Thread()
        For i As Integer = 0 To 100
            ChangePBValue(i, ProgressBar1)
            Threading.Thread.Sleep(1000)
        Next
    End Sub
    Private Sub ChangePBValue(_Value As Integer, ByRef _PB As ProgressBar)
        If _PB.InvokeRequired = True Then
            _PB.BeginInvoke(
New D_ChangePBValue(AddressOf ChangePBValue), _Value, _PB) Else _PB.Value = _Value End If End Sub Private Delegate Sub D_ChangePBValue(_Value As Integer, ByRef _PB As ProgressBar) Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Dim
NT As New Threading.Thread(AddressOf ValueTest_Thread) NT.Start() End Sub

萌新們表示看起來很困難,我這裡不過多解釋怎麼建立分執行緒了,自己百度一下跨執行緒更新控制元件方法與原理。 我給大家說一下分執行緒窗體的呼叫方法

    Private Sub ValueTest_ThreadForm()
        ThreadPBManger.Start("我想表達點什麼呢???")
        For i As Integer = 0 To 100
            ThreadPBManger.SetValue(i)
            Threading.Thread.Sleep(100)
        Next
        ThreadPBManger.Stop()
    End Sub

好像很簡單啊,這樣就可以在主執行緒完全卡死的情況下也能在進度條中顯示進度了,還能報告在做什麼內容,好神奇,但是ThreadPBManger是什麼鬼?

Public Module ThreadPBManger
    Dim NF_PB As F_PB
    Public Sub Start(ShowStr As String)
        Dim NT As New Threading.Thread(AddressOf Start_thread)
        NT.Start(ShowStr)
    End Sub
    Private Sub Start_thread(ShowStr As String)
        NF_PB = New F_PB
        NF_PB.Text = ShowStr
        Application.Run(NF_PB)
    End Sub
    Public Sub SetValue(Value As Integer)
        If IsNothing(NF_PB) Then Return
        If NF_PB.IsDisposed Then Return
        ChangePBValue(Value, NF_PB.ProgressBar1)
    End Sub
    Private Delegate Sub D_Stop()
    Public Sub [Stop]()
        If IsNothing(NF_PB) Then Return
        If NF_PB.IsDisposed Then Return
        If NF_PB.InvokeRequired Then
            NF_PB.Invoke(New D_Stop(AddressOf [Stop]))
        Else
            NF_PB.Dispose()
        End If
    End Sub

#Region "委託更新PB"
    Public Sub ChangePBValue(_Value As Integer, ByRef _PB As ProgressBar)
        If _PB.InvokeRequired = True Then
            _PB.BeginInvoke(New D_ChangePBValue(AddressOf ChangePBValue), _Value, _PB)
        Else
            _PB.Value = _Value
        End If
    End Sub
    Private Delegate Sub D_ChangePBValue(_Value As Integer, ByRef _PB As ProgressBar)
#End Region
End Module

知識點 Delegate,執行緒委託,BeginInvoke,InvokeRequired

重點解釋

  • Application.Run:在當前執行緒上開始執行標準應用程式訊息迴圈,並使指定窗體可見。以該模式開啟窗體後,該執行緒窗體將持續響應訊息迴圈,該執行緒只會隨著窗體的關閉而關閉。一個執行緒只有存在一個訊息迴圈。當前執行位置將會停止在此,當制定窗體關閉後,繼續執行後面的程式碼。

PS

  • 實際使用過程中,需要小夥伴們美化窗體,並新增更多的Try Catch來處理具體的異常。
  • 有趣的是Devexpress提供了SplashScreenManager提供了完整的執行緒進度窗體解決方案,我們叫它飛濺窗體。基礎版使用相對簡單,但要使用自定義窗體其難度非常高,後面我開文章教大家如何使用Devexpress的控制元件(想想腦殼疼,好大的專案啊!)