私はマルチスレッドUIの構築に取り組んでいます。長いプロセスをBackgroundWorkerクラスで処理し、UIの小さなタイマーでプロセスの経過時間を把握したいと思います。このようなUIを構築するのは初めてのことなので、ウェブ上の関連リソースを読んでいます。私のテストコードは、このようです:WPF&Multi-threading questions
private BackgroundWorker worker;
private Stopwatch swatch = new Stopwatch();
private delegate void simpleDelegate();
System.Timers.Timer timer = new System.Timers.Timer(1000);
string lblHelpPrevText = "";
private void btnStart_Click(object sender, RoutedEventArgs e)
{
try
{
worker = new BackgroundWorker(); //Create new background worker thread
worker.DoWork += new DoWorkEventHandler(BG_test1);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BG_test1end);
worker.RunWorkerAsync();
simpleDelegate del = new simpleDelegate(clockTicker);
AsyncCallback callBack = new AsyncCallback(clockEnd);
IAsyncResult ar = del.BeginInvoke(callBack, null);
lblHelpText.Text = "Processing...";
}
finally
{
worker.Dispose(); //clear resources
}
}
private void clockTicker()
{
//Grab Text
simpleDelegate delLblHelpText = delegate()
{ lblHelpPrevText = this.lblHelpText.Text; };
this.Dispatcher.BeginInvoke(DispatcherPriority.Send, delLblHelpText);
//Start clock
timer.Elapsed += new ElapsedEventHandler(clockTick);
timer.Enabled = true;
swatch.Start();
}
private void clockTick(object sender, ElapsedEventArgs e)
{
simpleDelegate delUpdateHelpTxt = delegate()
{ this.lblHelpText.Text = String.Format("({0:00}:{1:00}) {2}", swatch.Elapsed.Minutes, swatch.Elapsed.Seconds, lblHelpPrevText); };
this.Dispatcher.BeginInvoke(DispatcherPriority.Send, delUpdateHelpTxt);
}
private void BG_test1(object sender, DoWorkEventArgs e)
{
//this.lblHelpText.Text = "Processing for 10 seconds...";
Thread.Sleep(15000);
}
private void BG_test1end(object sender, RunWorkerCompletedEventArgs e)
{
this.lblHelpText.Text = "Process done.";
this.timer.Enabled = false;
this.swatch.Stop();
this.swatch.Reset();
}
static void clockEnd(IAsyncResult ar)
{
simpleDelegate X = (simpleDelegate)((AsyncResult)ar).AsyncDelegate;
X.EndInvoke(ar);
}
アイデアは、ボタンがクリックされたとき、私たちはその後、毎秒その上に時間を追加(「...処理」など)ラベルからステータステキストを取るです。別のスレッド上にあるので、TimerクラスのUI要素にアクセスできませんでした。そのため、代理人を使用してテキストを取得して設定する必要がありました。
これは機能しますが、これを処理するにはより良い方法がありますか?コードは、このような基本的な操作のために非常によく見えます。私はまた、下部のEndInvokeビットを完全に理解していません。このスレッドのコードの一部を入手しましたShould One Always Call EndInvoke a Delegate inside AsyncCallback?
私はBeginInvokeの結果を受け取ることを理解しています。しかし、これをこの状況で使う正しい方法はありますか?リソースのリークが心配ですが、デバッグ時にタイマーが動作する前にコールバックが実行されているように見えます。
おかげで、私は手動での進捗状況を更新する必要はないでしょうか?この場合、操作の実行時間を決定することはできません(SQLデータベースから数千行のデータを取得する予定です)ので、操作の実行時間を記録するタイマーを設定するだけでした。 – MHTri
@MHTri:実行時間と進歩は2つの異なるものです。事前に取得する行数( 'SELECT COUNT')を知っている場合は、進捗状況を更新できます。 「どのくらいの時間操作が実行されているか」については、本当にタイマーが必要です。しかし、*タイマーと 'BackgroundWorker'の間には*カップリングは全くありません。 – Jon
Hmm。私は別のSQLユーティリティクラスの別のメソッドでSQLDataAdapter.Fillを使用しているので、進捗状況をFillブロックとして見積もりしません(つまり、DataTable.Rows.Countプロパティにアクセスできないまた、完了するまで、RowChangedEventの火災は変わらないでしょうか? – MHTri