バックグラウンドワーカースレッドを使用するac#アプリケーションがあり、実行中のスレッドからUIを正常に更新しています。このアプリケーションには、ネットワーク上の最短パスルーティングが含まれます。バックグラウンドワーカーが進むにつれて、UIにネットワークと最短パスが表示されます。アプリケーションの実行中に、ユーザーがスライダーを使用して表示を遅くできるようにしたいと思います。
これは提案として見つけましたが、vb.netにあり、c#で機能させる方法がわかりません。
BackgroundWorkerが実行中にUIスレッドから値を取得するにはどうすればよいですか?
次のように、スライダーの値をbackgroundworkerに渡すことができます。
//非同期操作を開始します。delay = this.trackBar1.Value; backgroundWorker1.RunWorkerAsync(delay);
そして、backgroundworkerスレッド内で使用しますが、最初に送信された値のみを使用します。UIのスライダーを動かしたときに、バックグラウンドワーカー内から値を取得する方法がわかりません。
以前は複数のスレッドとデリゲートを使用していましたが、バックグラウンドワーカーを利用できる場合は、その単純さのためにそれを使用したいと思います。
2012年5月10日
ご回答ありがとうございます。私はまだ問題を抱えています。おそらく私が物事をどのように構造化したかが原因です。ネットワークルーティングのヘビーデューティー計算は、TransportationDelayModelクラスで実行されます。BackgroundWorker_DoWorkは、このクラスのインスタンスを作成してから開始します。遅延はTransportationDelayModelで処理されます。
コードのスケルトンは次のとおりです。
UIの場合:
private void runToolStripMenuItem1_Click(object sender, EventArgs e)
{
if (sqliteFileName.Equals("Not Set"))
{
MessageBox.Show("Database Name Not Set");
this.chooseDatabaseToolStripMenuItem_Click(sender, e);
}
if (backgroundWorker1.IsBusy != true)
{
// Start the asynchronous operation.
delay = this.trackBar1.Value;
// pass the initial value of delay
backgroundWorker1.RunWorkerAsync(delay);
// preclude multiple runs
runToolStripMenuItem1.Enabled = false;
toolStripButton2.Enabled = false;
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (!backgroundWorkerLaunched)
{
// instantiate the object that does all the heavy work
TransportationDelayModel TDM = new TransportationDelayModel(worker, e);
// kick it off
TDM.Run(sqliteFileName, worker, e);
backgroundWorkerLaunched = true;
}
}
TransportationDelayModelコンストラクターは次のとおりです。
public TransportationDelayModel(BackgroundWorker worker, DoWorkEventArgs e)
{
listCentroids = new List<RoadNode>();
listCentroidIDs = new List<int>();
listNodes = new List<RoadNode>();
listNodeIDs = new List<int>();
listRoadLink = new List<RoadLink>();
roadGraph = new AdjacencyGraph<int, RoadLink>(true); // note parallel edges allowed
tdmWorker = worker;
tdmEvent = e;
networkForm = new NetworkForm();
}
だから私はtdmWorkerを持っていて、それを使って情報をUIに戻すことができます。
TransportationDelayModelの内部計算では、遅延期間の間スリープします
if (delay2 > 0)
{
tdmWorker.ReportProgress(-12, zzz);
System.Threading.Thread.Sleep(delay2);
}
したがって、問題は、更新されたスライダー値をUIからバックグラウンドワーカーで実行されているオブジェクトに戻す方法にあるようです。私はいくつかの組み合わせを試しましたが、何も起こらないか、他のスレッドで起こっていることにアクセスすることを許可されていないというメッセージが表示されます。DoWorkイベントハンドラーですべての作業を行っていれば、あなたが提案するようにできるはずですが、それを実現するには複雑すぎます。
繰り返しになりますが、ご提案とご協力に感謝いたします。
2012年6月2日
この問題は2つの方法で解決しましたが、いくつか質問があります。R. Harveyへのコメントによると、私は簡単なアプリケーションを作成しました。これは、実行ボタン、スライダー、およびリッチテキストボックスを備えたフォームで構成されています。実行ボタンは、すべての作業を実行するクラス「Model」のオブジェクトをインスタンス化するバックグラウンドワーカースレッドを起動します(私のTransportationModelの簡略化された代理)。Modelクラスは、テキストボックスに100行を書き込むだけで、各行のドット数を1ずつ増やし、スライダーの設定と行末のスライダー値に基づいて、各行の間に遅延を設定します。これ:
.................... 58
..................... 58
...................... 58
....................... 51
........................ 44
......................... 44
この演習の目的は、「モデル」の実行中にフォーム上のスライダーを移動し、変更の遅延を取得できるようにすることです(上記のように)。
私の最初の解決策は、スライダーの値を保持するために、Globalsクラスを作成することです。
class Globals
{
public static int globalDelay;
}
次に、フォームで、トラックバーがスクロールされるたびにこの値を更新します。
private void trackBar1_Scroll(object sender, EventArgs e)
{
Globals.globalDelay = this.trackBar1.Value;
}
モデルでは、グローバルの値を取得します。
public void Run(BackgroundWorker worker, DoWorkEventArgs e)
{
for (int i = 1; i < 100; i++)
{
delay = Globals.globalDelay; // revise delay based on static global set on UI
System.Threading.Thread.Sleep(delay);
worker.ReportProgress(i);
string reportString = ".";
for (int k = 0; k < i; k++)
{
reportString += ".";
}
reportString += delay.ToString();
worker.ReportProgress(-1, reportString);
}
}
}
これは問題なく機能します。私の質問:このアプローチには、実装が非常に簡単で非常に一般的であると思われる欠点がありますか。
R. Harveyの提案に基づく、2番目のアプローチでは、デリゲートを使用して呼び出します。
デリゲート用のクラスを作成します。
public class MyDelegates
{
public delegate int DelegateCheckTrackBarValue(); // create the delegate here
}
フォームで、私は作成します:
public int CheckTrackBarValue()
{
return this.trackBar1.Value;
}
Modelクラスにメンバーm_CheckTrackBarValueが追加されました
public class Model
{
#region Members
Form1 passedForm;
public static MyDelegates.DelegateCheckTrackBarValue m_CheckTrackBarValue=null;
#endregion Members
#region Constructor
public Model(BackgroundWorker worker, DoWorkEventArgs e, Form1 form)
{
passedForm = form;
}
実行ボタンによってバックグラウンドスレッドが起動されると、呼び出しフォームが渡されます
private void button1_Click(object sender、EventArgs e){if(backgroundWorker1.IsBusy!= true){
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (!backgroundWorkerLaunched)
{
// instantiate the object that does all the heavy work
Model myModel= new Model(worker, e, this);
Model.m_CheckTrackBarValue = new MyDelegates.DelegateCheckTrackBarValue(this.CheckTrackBarValue);
// kick it off
myModel.Run(worker, e);
backgroundWorkerLaunched = true;
}
}
最後に、モデルでは、渡されたフォームでInvokeメソッドが呼び出され、トラックバーの値が取得されます。public void Run(BackgroundWorker worker、DoWorkEventArgs e){
for (int i = 1; i < 100; i++)
{
int delay = (int)passedForm.Invoke(m_CheckTrackBarValue,null); // invoke the method, note need the cast here
System.Threading.Thread.Sleep(delay);
worker.ReportProgress(i);
string reportString = ".";
for (int k = 0; k < i; k++)
{
reportString += ".";
}
reportString += delay.ToString();
worker.ReportProgress(-1, reportString);
}
}
これも機能します。メンバー変数を静的にするまでエラーが発生し続けました。たとえば、public static MyDelegates.DelegateCheckTrackBarValue m_CheckTrackBarValue = null;
このソリューションに関する私の質問:以前のバージョンに関して、このソリューションには利点がありますか?これを実装する方法で物事を複雑にしすぎていませんか?m_CheckTrackBarValueを静的にする必要があるのはなぜですか。
この編集の長さについてお詫び申し上げますが、問題と解決策は他の人の興味を引くかもしれないと思いました。