C#でデータベース呼び出しの進捗バーとバックグラウンドワーカーを実装するにはどうすればよいですか?
大量のデータを処理する方法がいくつかあります。それらは比較的長時間実行される操作なので、プログレスバーを実装して、実際に何かが起こっていることをユーザーに知らせたいと思います。
プログレスバーまたはステータスストリップラベルを使用することを考えましたが、単一の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);
}
もっと良い例を探しています。
一部の人々はそれを好まないかもしれませんが、これは私がしていることです:
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を有効にする必要がありますが、更新する必要なく、独自に継続的にスクロールします。進行状況を報告しないデータベース操作に使用します。
進行状況がわからない場合は、進行状況バーを悪用して偽装しないでください。代わりに、en.wikipedia.org/wiki/Throbber#Spinning_wheelのようなビジーなアイコンを表示してください。終わった。これにより、より「正直な」GUIが作成されます。
バックグラウンドスレッドで操作を実行し、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);
});
}
WinForms:
// 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デリゲートの場合、キャストが必要な場合があります。または、そこに少しの助けが必要な場合があります。正確な構文を覚えてはいけません。
バックグラウンドワーカーの進捗状況を報告する背後にある考え方は、「完了率」イベントを送信することです。あなたは、自分が何らかの形で「どれだけ」の作業が完了したかを判断する責任があります。残念ながら、これは最も難しい部分です。
あなたの場合、作業の大部分はデータベースに関連しています。私の知る限り、DBから進捗情報を直接取得する方法はありません。ただし、できることは、作業を動的に分割することです。たとえば、大量のデータを読み取る必要がある場合、これを実装する素朴な方法があります。
実際の読み取り値を小さなチャンクに分割し、1つのチャンクが完了するたびに進行状況を報告します。
for (int i = 0; i < count; i++)
{
bgWorker.ReportProgress((100 * i) / count);
// ... (read data for step i)
}
これは概念実証のためのものであるため、コンパイルしていません。これが、過去のデータベースアクセスの進行状況バーを実装した方法です。この例は、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);
}
これにより、Helpfull.Easyが実装され、100%テストされます。
for(int i=1;i<linecount;i++)
{
progressBar1.Value = i * progressBar1.Maximum / linecount; //show process bar counts
LabelTotal.Text = i.ToString() + " of " + linecount; //show number of count in lable
int presentage = (i * 100) / linecount;
LabelPresentage.Text = presentage.ToString() + " %"; //show precentage in lable
Application.DoEvents(); keep form active in every loop
}