C # - pass information to BackgroundWorker from the user interface at runtime

I have a C # application that uses a desktop thread and quite successfully updates the user interface from a working thread. The application includes the shortest route routing in the network, and I display the network and the shortest path in the user interface when the background worker is running. I want the user to be able to slow down the display using the slider while the application is running.

I found this as a suggestion, but it is in vb.net, I don’t understand how to make it work in C #.

How can BackgroundWorker get values ​​from a user interface thread while it is running?

I can pass the value of the slider into the wallpaper as follows:

// Run the asynchronous operation. delay = this.trackBar1.Value; backgroundWorker1.RunWorkerAsync (delay);

and use it in the backgroundworker thread, but it uses only the original value. I don’t understand how to get the value from inside the background worker when I move the slider in the user interface.

I used to use several threads and delegates, but if you can use a background worker, I would prefer it for its simplicity.

10/10/2012

Thank you all for your answers. I'm still having problems, most likely due to the fact that I have structured things. High load calculations for network routing are performed in the TransportationDelayModel class. BackgroundWorker_DoWork instantiates this class and then turns it off. The delay is handled by TransportDelayModel.

The code skeleton is as follows:

In the user interface:

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; } } 

Constructor 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(); } 

so I have tdmWorker that allows me to pass information back to the user interface.

In internal calculations in TransportationDelayModel, I sleep for a delay period

  if (delay2 > 0) { tdmWorker.ReportProgress(-12, zzz); System.Threading.Thread.Sleep(delay2); } 

therefore, the problem is how to pass the updated value of the slider from the user interface back to the object that is running in the background worker. I tried several combinations, seemingly turning around, to no avail, nothing happens, or I get a message that I am not allowed to access what is happening in another thread. I understand that if I did all the work in the DoWork event handler, I would have to do everything that you offer, but there are too many difficulties for this.

Thanks again for your suggestions and help.

6/2/2012

I solved this problem in two ways, but I have some questions. In my commentary on R. Harvey, I created a simple application. It consists of a form with a start button, a slider and a rich text field. The start button starts a workflow thread that creates an object of the Model class that does all the work (a simplified surrogate for my transport model). The Model class simply writes 100 lines to the text field, increasing the number of points in each line by 1, with a delay between each line based on the slider setting and the value of the slider at the end of the line, something like this:

.................... 58

..................... 58

...................... 58

....................... 51

........................ 44

......................... 44

The goal of this exercise is the ability to move the slider in the form during the operation of the "Model" and obtain a delay for the change (as described above).

My first solution involves creating a Globals class to hold the value of the slider:

 class Globals { public static int globalDelay; } 

then in the form I update this value whenever the scrollbar scrolls:

 private void trackBar1_Scroll(object sender, EventArgs e) { Globals.globalDelay = this.trackBar1.Value; } 

and in the Model, I just raise the value of global:

  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); } } } 

This works great. My question is: are there any flaws in this approach that seem very simple to implement and fairly general.

The second approach, based on the proposals of R. Harvey, uses delegates and calls.

I am creating a class for delegates:

  public class MyDelegates { public delegate int DelegateCheckTrackBarValue(); // create the delegate here } 

in the form, I create:

  public int CheckTrackBarValue() { return this.trackBar1.Value; } 

and the Model class now has a member 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; } 

When the background thread is started by the start button, the calling form is submitted

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; } } 

Finally, in the Model, the Invoke method is called in the passed form to get the value of the trackbar. public void Run (BackgroundWorker employee, 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); } } 

This also works. I kept getting the error until I made the member variable of the variable static, for example. public static MyDelegates.DelegateCheckTrackBarValue m_CheckTrackBarValue = null;

My questions about this solution: are there any advantages to this solution in relation to the previous version? Am I too complicated on how I did this? Why m_CheckTrackBarValue should be static.

I apologize for the length of this edit, but I thought the problem and solutions might be of interest to others.

+4
source share
3 answers

You should pass the TrackBar object to BackgroundWorker , not delay . delay does not change after setting it.

To simplify the necessary Invoke() , you can use a helper method, like this one :

 Async.UI(delegate { textBox1.Text = "This is way easier!"; }, textBox1, true); 
+2
source

I assume that you are already familiar with calling cross-thread to update the user interface. Thus, the solution is very simple: in your workflow, after each iteration, call the user interface to get the position of the thumb of the slider.

+1
source

To use a background artist, you add a method to the DoWork property, for example:

  this.backgroundWorker1.WorkerSupportsCancellation = true; this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork); this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted); 

In the DoWork method, you need to check the variable in which the updated delay is set. This can be an integer field that is available in the containing Form or UI control, or it can be the TrackBar itself.

0
source

Source: https://habr.com/ru/post/1411341/


All Articles