web-dev-qa-db-ja.com

VB.NETプログレスバーbackgroundworker

アプリケーションが起動し、アップグレードされたばかりのとき、ローカルデータベースの更新(sqlite)を実行しています。

それはそのようなものです:ユーザーが私のアプリを起動し、次に私がアップグレードプロセスを開始します。このアップグレードプロセス中に、継続的なプログレスバーを持つフォームを表示しています。アップグレードプロセスが完了すると、このフォームは閉じ、ユーザーはアプリケーションの使用を開始できます。

ただし、アップグレードプロセスは非常に集中的であるため、プログレスバーはアニメーション化されません。

古いVB6バージョンでは、フォームが1つあり、プログレスバーが表示されるActiveX-Exeを使用していました。これが私の「バックグラウンドワーカー」でした。

VB.NETで同じアプローチを使用できるかどうかはわかりません。

バックグラウンドワーカーで作業を行う例を見ただけですが、プログレスバー自体がバックグラウンドワーカーである例は見たことがありません。

データベースのアップグレードをブロックする必要があります。データベースのアップグレードが完了する前に、ユーザーが私のアプリケーションを使用することはできません。これは、プログレスバーのみが「アウトプロセス」であり、アップグレードではないことを意味します。

どうもありがとうございました!

9
tmighty

最初にこれを読んでください: Application.DoEvents()の使用

したがって、上記の回答を読んだ後は、DoEventsを二度と使用することはなく、DoEventsがないと(および/またはPaintイベントが発生するようにProgressBarを無効化すると)、「アップグレードプロセスが非常に集中するため、プログレスバーはアニメーション化されません」

したがって、Cthulhuのコメント-「プログレスバーを使用してダイアログを作成し、そのダイアログをモーダルにして、バックグラウンドワーカーでdb-stuffを実行できます。」前進するための最良の方法の1つです。

私が使用しているこれのC#実装を翻訳しました。すぐにドロップできるはずです。

これはProgressBarフォームです:

Public Partial Class ThinkingProgressBar
    Inherits Form
    Private startTime As System.DateTime = DateTime.Now
    Public Sub New()
        InitializeComponent()
    End Sub

    Private Sub lblClose_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs)
        Me.Tag = "Cancelled"
        Me.Hide()
    End Sub

    Public Sub SetThinkingBar(ByVal switchedOn As Boolean)
        If switchedOn Then
            lblTime.Text = "0:00:00"
            startTime = DateTime.Now
            Timer1.Enabled = True
            Timer1.Start()
        Else
            Timer1.Enabled = False
            Timer1.Stop()
        End If
    End Sub

    Private Sub timer1_Tick(sender As Object, e As EventArgs)
        Dim diff As New TimeSpan()
        diff = DateTime.Now.Subtract(startTime)
        lblTime.Text = diff.Hours & ":" & diff.Minutes.ToString("00") & ":" & diff.Seconds.ToString("00")
        lblTime.Invalidate()
    End Sub
End Class

BackgroundWorkerコントロールをフォームにドラッグアンドドロップします。バックグラウンドワーカーのイベントは次のとおりです。

Private Sub backgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    e.Result = e.Argument
    'DirectCast(e.Result, ThinkingProgressBar).SetThinkingBar(True) 
    'DO LONG OPERATION HERE

End Sub

Private Sub backgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    Dim dlg As ThinkingProgressBar = TryCast(e.Result, ThinkingProgressBar)
    If IsNothing(dlg) = False Then
        dlg.SetThinkingBar(False)
        dlg.Close()
    End If
End Sub

そして、アプリケーションが起動してアップグレードを行うときの呼び出しコードは次のとおりです。

Dim dlg As New ThinkingProgressBar()
dlg.SetThinkingBar(True)
BackgroundWorker1.RunWorkerAsync(dlg)
dlg.ShowDialog()
If IsNothing(dlg.Tag) = False AndAlso dlg.Tag.ToString() = "Cancelled" Then
    Return
End If

いくつかのこととして、ユーザーがキャンセルできないようにすることができます(つまり、lblClose_LinkClicked)アップグレード中にユーザーがプロセスを強制終了したり、PCの電源を切ったりした場合に対処するために、保護/防御プログラミングを導入します。

また、ProgressBarは実際にはアニメーションGIFです。データベースの更新にかかる時間を見積もるのは非常に難しいため、これは使用法に適しています。

enter image description here

12
Jeremy Thompson

あなたの要件から、あなたが探しているように私には思えます:

Application.DoAction()

私はアプリケーションで、xmlから2000から3000のアイテムに関連する詳細をロードする必要があり、実装されたプロセスがアイテムを1つずつ読み取り、プロセスをハングアップさせていることを経験しました。ループで上記の行を使用すると、問題が解決しました。 UIはハングアップせず、ユーザーは他のフォームで作業できました。これが登場しましたprogressbar is now as background process(ある程度まで)私には。

それが役に立てば幸い!

3
NeverHopeless

プログレスバーをバックグラウンドスレッドにしたいというあなたの願望は理解していますが、これはできないと確信しています。アプリケーションのビジュアルコンポーネントを更新する必要があるため、UIをプライマリスレッドにする必要があります。メニュー項目を含む、フォーム上のすべてのコントロールを無効にするのは簡単です。

このプロセスを提案させてください:

  1. 記載されているプログレスバーを使用してフォームを表示します。

  2. 他のフォームを表示する場合は、フォームに直接あるすべての子コントロールを無効にします。コントロールによっては、子コントロールをウォークスルーして無効にする必要がある場合があります。

  3. データベースの更新を実行するコードで、プログレスバーの値を必要な値に更新します。値を更新したら、プログレスバーのrefreshメソッドを呼び出します。これにより、プログレスバーが強制的に正しくペイントされます。

  4. 更新が完了したら、プログレスバーを非表示にし、無効にしたすべてのフォームのすべてのコントロールを再度有効にします。

これにより、バックグラウンドスレッドで実行されているプログレスバーの外観が得られますが、アプリケーションは必要に応じてメインスレッドをブロックできます。

3
Adam Zuckerman

これはテストされていませんが、BGWに情報を供給するためにこのようなことをしたことを思い出します。これにより、バックグラウンドワーカーのコンテンツが更新されます。

まず、次の変数を使用してクラス(つまり、ProgExample)を作成し、それをグローバルオブジェクトとして配置します。

Dim ProgValue as Integer;
Dim ProgMax as Integer;

次に、クラスをバックグラウンドワーカーにフィードします。

BackgroundWorker1.RunWorkerAsync(_ProgExample)

それを読んで、中に入れてください:

Dim BGWProgExample as ProgExample = DirectCast(e.Argument, ProgExample), 

バックグラウンドワーカーで次のことを行います。

BGWProgressBar.Maximum = BGWProgExample.ProgMax
while BGWProgExample.ProgValue <> BGWProgExample.ProgMax
BGWProgressBar.Value = BGWProgExample.ProgValue 

したがって、何かを行ったら、_ProgExample.ProgValueを更新するだけです。 ProgValueを引数として渡すだけで、クラスなしでこれを実行しようとしないでください。つまり、ProgValueのコピーを現在の状態で送信し、ProgValueを更新しても変更されないことを意味します。そして、これで何も起こらない場合は、Application.DoEventsやApplication.DoActionをこれと一緒に試してください。

1
WozzeC

質問がわからないかもしれませんが、本当に簡単ではありませんか?

For i = ProgressBar1.Minimum To ProgressBar1.Maximum
        ProgressBar1.Value = i
        ProgressBar1.Update()
        System.Threading.Thread.Sleep(25)
    Next

プログレスバーの「更新」がそのトリックを行います。あなたのコードは「ブロック」しますが(私にはわかりませんが、「アプリケーションが応答しない」ことが望ましい理由はわかりません)、プログレスバーはupdatedを取得します。

ところで:Windows Vista/7(プログレスバーアニメーション)の場合、これをチェックするかもしれません: プログレスバーはWindowsフォームでは遅い

1
igrimpe