20

C# でデータベース呼び出しのプログレス バーとバックグラウンド ワーカーを実装するにはどうすればよいですか?

大量のデータを処理する方法がいくつかあります。これらは比較的長時間実行される操作であるため、進行状況バーを実装して、実際に何かが起こっていることをユーザーに知らせたいと考えています。

プログレス バーまたはステータス ストリップ ラベルを使用することを考えましたが、データベース処理メソッドが実行されるスレッドが 1 つの UI スレッドであるため、UI コントロールが更新されず、プログレス バーまたはステータス ストリップ ラベルが役に立たなくなります。

すでにいくつかの例を見てきましたが、それらは for ループを扱っています。

for(int i = 0; i < count; i++)
{ 
    System.Threading.Thread.Sleep(70);
    // ... do analysis ...
    bgWorker.ReportProgress((100 * i) / count);
}

private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar.Value = Math.Min(e.ProgressPercentage, 100);
}

より良い例を探しています。

4

6 に答える 6

18

嫌いな人もいるかもしれませんが、私は以下のようにしています。

private void StartBackgroundWork() {
    if (Application.RenderWithVisualStyles)
        progressBar.Style = ProgressBarStyle.Marquee;
    else {
        progressBar.Style = ProgressBarStyle.Continuous;
        progressBar.Maximum = 100;
        progressBar.Value = 0;
        timer.Enabled = true;
    }
    backgroundWorker.RunWorkerAsync();
}

private void timer_Tick(object sender, EventArgs e) {
    if (progressBar.Value < progressBar.Maximum)
        progressBar.Increment(5);
    else
        progressBar.Value = progressBar.Minimum;
}

Marquee スタイルでは VisualStyles を有効にする必要がありますが、更新しなくても継続的にスクロールします。進行状況を報告しないデータベース操作に使用します。

于 2009-08-11T12:56:39.560 に答える
11

進行状況がわからない場合は、進行状況バーを悪用して偽造するのではなく、en.wikipedia.org/wiki/Throbber#Spinning_wheel のような忙しいアイコンを表示するだけで、タスクの開始時に表示し、タスクの開始時に非表示にします終了した。これにより、より「正直な」GUI が作成されます。

于 2010-04-07T14:49:40.277 に答える
6

バックグラウンド スレッドで操作を実行し、UI を更新する場合、バックグラウンド スレッドからは何も呼び出すことも設定することもできません。WPF の場合は Dispatcher.BeginInvoke が必要で、WinForms の場合は Invoke メソッドが必要です。

WPF:

// assuming "this" is the window containing your progress bar..
// following code runs in background worker thread...
for(int i=0;i<count;i++)
{
    DoSomething();
    this.Dispatcher.BeginInvoke((Action)delegate(){
         this.progressBar.Value = (int)((100*i)/count);
    });
}

Winフォーム:

// assuming "this" is the window containing your progress bar..
// following code runs in background worker thread...
for(int i=0;i<count;i++)
{
    DoSomething();
    this.Invoke(delegate(){
         this.progressBar.Value = (int)((100*i)/count);
    });
}

WinFormsデリゲートの場合、キャストが必要になるか、ほとんど助けが必要ない場合があります。正確な構文を今覚えていないでください。

于 2009-08-11T12:15:09.967 に答える
5

バックグラウンド ワーカーで進行状況を報告する背後にある考え方は、「完了率」イベントを送信することです。「どのくらい」の作業が完了したかを何らかの形で判断するのは、あなた自身の責任です。残念ながら、これは多くの場合、最も難しい部分です。

あなたの場合、作業の大部分はデータベース関連です。私の知る限り、DB から直接進捗情報を取得する方法はありません。ただし、できることは、作業を動的に分割することです。たとえば、大量のデータを読み取る必要がある場合、これを実装する単純な方法は次のようになります。

  • 取得する行数を決定します (SELECT COUNT(*) FROM ...)
  • 実際の読み取り値を小さなチャンクに分割し、1 つのチャンクが完了するたびに進行状況を報告します。

    for (int i = 0; i < count; i++)
    {
        bgWorker.ReportProgress((100 * i) / count);
        // ... (read data for step i)
    }
    
于 2009-08-11T12:27:22.420 に答える
2

これは概念実証用であるため、コンパイルしていません。これは、過去にデータベース アクセス用のプログレス バーを実装した方法です。この例は、System.Data.SQLite モジュールを使用した SQLite データベースへのアクセスを示しています。

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{   
    // Get the BackgroundWorker that raised this event.
    BackgroundWorker worker = sender as BackgroundWorker;
    using(SQLiteConnection cnn = new SQLiteConnection("Data Source=MyDatabase.db"))
    {
        cnn.Open();
        int TotalQuerySize = GetQueryCount("Query", cnn); // This needs to be implemented and is not shown in example
        using (SQLiteCommand cmd = cnn.CreateCommand())
        {
            cmd.CommandText = "Query is here";
            using(SQLiteDataReader reader = cmd.ExecuteReader())
            {
                int i = 0;
                while(reader.Read())
                {
                    // Access the database data using the reader[].  Each .Read() provides the next Row
                    if(worker.WorkerReportsProgress) worker.ReportProgress(++i * 100/ TotalQuerySize);
                }
            }
        }
    }
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.progressBar1.Value = e.ProgressPercentage;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Notify someone that the database access is finished.  Do stuff to clean up if needed
    // This could be a good time to hide, clear or do somthign to the progress bar
}

public void AcessMySQLiteDatabase()
{
    BackgroundWorker backgroundWorker1 = new BackgroundWorker();
    backgroundWorker1.DoWork += 
        new DoWorkEventHandler(backgroundWorker1_DoWork);
    backgroundWorker1.RunWorkerCompleted += 
        new RunWorkerCompletedEventHandler(
    backgroundWorker1_RunWorkerCompleted);
    backgroundWorker1.ProgressChanged += 
        new ProgressChangedEventHandler(
    backgroundWorker1_ProgressChanged);
}
于 2010-04-07T15:38:57.700 に答える