3

私は単純なスレッド アプリケーションをコーディングしています。開始ボタンをクリックすると、アプリケーションはこのボタンを無効にし、For iterations を作成して 5 つの ProgressBars を更新するだけで 5 つのスレッドを実行します。最後のスレッドは、スレッドの終了を待って、開始ボタンを再度有効にします。

問題: プログレスバーが 100% になる前にボタンが有効になっているのが表示されます... プログレスバーはまだ更新中です!

どうしたの?UI スレッドで結合ステートメントを作成する方法はありますか?

コードは次のとおりです。

Imports System.Threading

Public Structure ThreadParameters
    Public ThreadId As Integer
    Public pgBar As ProgressBar
    Public iterations As Integer
End Structure

Public Structure SetPgValueParameters
    Public pgBar As ProgressBar
    Public value As Integer
End Structure

Public Class Form1

Private threads(4) As Thread
Private Shared FormThread As Thread

Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click
    StartButton.Enabled = False

    Dim MainThread As Thread = New Thread(AddressOf WaitThreads)
    MainThread.Start()

    For i = 0 To 4
        threads(i) = New Thread(AddressOf ThreadRun)

        Dim params As ThreadParameters

        params.pgBar = Me.Controls.Find("ProgressBar" & (i + 1), False)(0)
        params.iterations = 100
        params.ThreadId = i

        threads(i).Start(params)
    Next

End Sub

Private Sub ThreadRun(params As ThreadParameters)
    Dim invokeParams As SetPgValueParameters
    Dim lastIntegerVal As Integer = 1
    invokeParams.pgBar = params.pgBar
    For i = 0 To params.iterations
            invokeParams.value = (100 * i / params.iterations)
        setPgValue(invokeParams)
        Console.WriteLine(params.ThreadId & ":" & i & "/" & params.iterations)
    Next
End Sub

Private Delegate Sub setPgValueDelegate(params As SetPgValueParameters)
Private Sub setPgValue(params As SetPgValueParameters)
    If params.pgBar.InvokeRequired Then
        Dim d As New setPgValueDelegate(AddressOf setPgValue)
        Me.Invoke(d, New Object() {params})
    Else
        params.pgBar.Value = params.value
        'Application.DoEvents()
    End If
End Sub


Private Sub WaitThreads()
    For i = 0 To 4
        threads(i).Join()
    Next
    FormThread.Join()
    setButtonEnabled()
End Sub

Private Delegate Sub setButtonEnabledDelegate()
Private Sub setButtonEnabled()
    If StartButton.InvokeRequired Then
        Dim d As New setButtonEnabledDelegate(AddressOf setButtonEnabled)
        Me.Invoke(d, Nothing)
    Else
        Application.DoEvents()
        StartButton.Enabled = True
        Console.WriteLine("Bouton réactivé")
    End If
End Sub
End Class

編集: Bjørn-Roger Kringsjå へのリンクをありがとう。したがって、回避策があります。値を増やして直接減らすと、アニメーションがスキップされます。最大値については、値を最大に設定し、1 ずつ減らし、1 ずつ増やします...まだ少しラグがありますが、ProgressBars が中央にあるときにボタンを有効にするよりも本当に優れています:)

4

1 に答える 1

2

さて、私はいくつかのテストを行いましたが、問題は進行状況バーのアニメーションにあることがわかりました. ボタンが有効になった時点で (これは正しい)、プログレス バーはまだ「アニメーション モード」です。これは、 XP ビジュアル スタイルを無効にすることで確認できます。

プログレス バーにはMarqueeAnimationSpeedBlocksというプロパティ名がありますが、残念ながらスタイルを使用する場合は効果がありません。

私はグーグル検索を行い、このSO投稿を見つけました:

値を変更するときに.NETプログレスバーのアニメーションを無効にしますか?

サンプル アプリケーション

Imports System.Threading
Imports System.Threading.Tasks

Public Class Form1

    Public Sub New()
        Me.InitializeComponent()
        Me.StartButton = New Button() With {.TabIndex = 0, .Dock = DockStyle.Top, .Text = "Start", .Height = 30}
        Me.ProgressBar1 = New ProgressBar With {.TabIndex = 1, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar1"}
        Me.ProgressBar2 = New ProgressBar With {.TabIndex = 2, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar2"}
        Me.ProgressBar3 = New ProgressBar With {.TabIndex = 3, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar3"}
        Me.ProgressBar4 = New ProgressBar With {.TabIndex = 4, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar4"}
        Me.ProgressBar5 = New ProgressBar With {.TabIndex = 5, .Dock = DockStyle.Top, .Height = 30, .Name = "ProgressBar5"}
        Me.Controls.AddRange({Me.ProgressBar5, Me.ProgressBar4, Me.ProgressBar3, Me.ProgressBar2, Me.ProgressBar1, Me.StartButton})
    End Sub

    Private Sub HandleStartButtonClicked(sender As Object, e As EventArgs) Handles StartButton.Click

        Me.StartButton.Enabled = False

        Dim tasks As Task() = New Task(4) {}

        For i As Integer = 0 To 4
            Dim index As Integer = i
            tasks(i) = New Task(
                Sub()
                    Dim n As Integer = rnd.Next(100, 1001)
                    For j As Integer = 0 To n
                        Me.Invoke(
                            Sub()
                                DirectCast(Me.Controls("ProgressBar" & (index + 1).ToString()), ProgressBar).Value = CInt((j / n) * 100.0#)
                            End Sub
                        )
                        Thread.Sleep(10)
                    Next
                End Sub
                )
        Next

        Task.Factory.ContinueWhenAll(tasks,
            Sub() Me.Invoke(
                Sub()
                    MessageBox.Show(String.Join(Environment.NewLine, From pb As ProgressBar In Me.Controls.OfType(Of ProgressBar)() Select (pb.Name & ": " & pb.Value.ToString())))
                    Me.StartButton.Enabled = True
                End Sub
            )
        )

        Array.ForEach(Of Task)(tasks, Sub(t) t.Start())

    End Sub

    Private WithEvents StartButton As Button
    Private WithEvents ProgressBar1 As ProgressBar
    Private WithEvents ProgressBar2 As ProgressBar
    Private WithEvents ProgressBar3 As ProgressBar
    Private WithEvents ProgressBar4 As ProgressBar
    Private WithEvents ProgressBar5 As ProgressBar
    Private Shared rnd As New Random()

End Class
于 2014-05-15T15:42:00.203 に答える