Fairly frustrating since this seems to be well documented and the fact that I accomplished this before, but can't duplicate the same success. Sorry, I'll try to relate it all clearly.
Visual Studio, C# Form, One Main Form has text fields, among other widgets.
At one point we have the concept that we are "running" and therefore gathering data.
For the moment, I started a one second timer so that I can update simulated data into some fields. Eventually that one second timer will take the more rapid data and update it only once per second to the screen, that's the request for the application right now we update at the rate we receive which is a little over 70 Hz, they don't want it that way. In addition some other statistics will be computed and those should be the field updates. Therefore being simple I'm trying to just generate random data and update those fields at the 1 Hz rate. And then expand from that point.
Definition and management of the timer: (this is all within the same class MainScreen)
System.Timers.Timer oneSecondTimer;
public UInt32 run_time = 0;
public int motion = 5;
private void InitializeTimers()
{
this.oneSecondTimer = new System.Timers.Timer(1000);
this.oneSecondTimer.Elapsed += new System.Timers.ElapsedEventHandler(oneSecondTimer_elapsed);
}
public void start_one_second_timer()
{
run_time = 0;
oneSecondTimer.Enabled = true;
}
public void stop_one_second_timer()
{
oneSecondTimer.Enabled = false;
run_time = 0;
}
Random mot = new Random();
private void oneSecondTimer_elapsed(object source, System.Timers.ElapsedEventArgs e)
{
run_time++;
motion = mot.Next(1, 10);
this.oneSecondThread = new Thread(new ThreadStart(this.UpdateTextFields));
this.oneSecondThread.Start();
}
private void UpdateTextFields()
{
this.motionDisplay.Text = this.motion.ToString();
}
motionDisplay is just a textbox in my main form. I get the Invalid Operation Exception pointing me towards the help on how to make Thread-Safe calls. I also tried backgroundworker and end up with the same result. The details are that motionDisplay is accessed from a thread other than the thread it was created on.
So looking for some suggestions as to where my mistakes are.
Best Regards. I continue to iterate on this and will update if I find a solution.
Use a System.Forms.Timer rather than a System.Timers.Timer. It will fire it's elapsed event in the UI thread.
Don't create a new thread to update the UI; just do the update in the elapsed event handler.
Try this
private void UpdateTextFields()
{
this.BeginInvoke(new EventHandler((s,e)=>{
this.motionDisplay.Text = this.motion.ToString();
}));
}
This will properly marshall a call back to the main thread.
The thing with WinForm development is that all the controls are not thread safe. Even getting a property such as .Text from another thread can cause these type of errors to happen. To make it even more frustrating is that sometimes it will work at runtime and you won't get an exception, other times you will.
This is how I do it:
private delegate void UpdateMotionDisplayCallback(string text);
private void UpdateMotionDisplay(string text) {
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.motionDisplay.InvokeRequired) {
UpdateMotionDisplayCallback d = new UpdateMotionDisplayCallback(UpdateMotionDisplay);
this.Invoke(d, new object[] { text });
} else {
this.motionDisplay.Text = text;
}
}
When you want to update the text in motionDisplay just call:
UpdateMotionDisplay(this.motion.ToString())
Related
I have been searching for over two days for a solution to this issue, and have finally decided to ask this question. I have found MANY relevant topics, but none of them seem to solve my problem. Most recently, I tried all of the solutions listed here.
Background Info: I have a class that handles traversing a massive amount of data. The class is called Traverse. There is a class method called DoFullTraverse (Traverse.DoFullTraverse), that runs a complete traverse than can (depending on user input) take up to 30 seconds. I am working in WPF, MVVM pattern. I would like to update a status bar on the gui for the progress of the DoFullTraverse. I calculate at the beginning of the function the exact number of loops required for calculation, and then increment a loop counter. Each time it reaches another 1/100, I increment the progress bar by 1. My progress bar (in xaml) has its value bound to a property in my VM called PBarV.
Most Recent Attempt: I have tried 100 different solutions but my most recent attempt looks like this:
private void runTraverseAndUpdateBar()
{
var worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_Complete);
worker.RunWorkerAsync();
while (!ThreadCheck)
{
Thread.Sleep(500);
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
for (int i = 0; i < 36; i++)
{
Thread.Sleep(500);
PBarV += 3;
}
e.Result = true;
}
void worker_Complete(object sender, RunWorkerCompletedEventArgs e)
{
ThreadCheck = true;
}
I believe that I am fundamentally misunderstanding how the background worker does work.
The Main Problem: I can get this method to work just fine, if I throw the function into the background worker and continue as usual. The problem is, I need the data from that function before my program to continue. Therefore, I need it to execute linearly but still update the status bar properly.
If anyone can shed some light on what I am missing or even nudge me in the right direction, I would appreciate it greatly.
Edit: This is not duplicate. The post you provided does not cover the issue of linear executing and waiting for the background worker to complete before continuing.
Edit 2: (As Per #Clemens Request)
I need the background worker to complete work before the main program continues. I am running the computationally heavy process in the background worker specifically so that the progress bar can be updated. But, BEFORE the main program can continue, I need the information from Traverse.DoFullTraverse();
To be VERY specific. The main program should halt all execution (other than updating status bar) until the background worker has completed Traverse.DoFullTraverse();
Here's a trivial example you can play around with and apply to your view model. It's important to use prototypes to create code and learn how it works in order to apply it to a larger and more complex application.
Please note that the example doesn't include trivial stuff like how to implement INotifyPropertyChanged and ICommand--those are easy to do.
Also, note the comments within TraverseYo. Specifically, the ones that tell you what thread you're currently on. Understanding the flow of execution across threads is important to get this working correctly. If you don't know what thread you're on, simply get the ApartmentState of the current thread. If it's STA, you're most likely on the UI thread.
public class LongLastingWorkViewModel : INotifyPropertyChanged
{
public bool Busy
{
// INotifyPropertyChanged property implementation omitted
}
public double PercentComplete
{
// INotifyPropertyChanged property implementation omitted
}
public ICommand PerformWork { get; set; }
public LongLastingWorkViewModel()
{
// delegated ICommand implementation omitted--there's TONS of it out there
PerformWork = new DelegatedCommand(TraverseYo);
}
private void TraverseYo()
{
// we are on the UI thread here
Busy = true;
PercentComplete = 0;
Task.Run(() => {
// we are on a background thread here
// this is an example of long lasting work
for(int i = 0; i < 10; i++)
{
Thread.Sleep(10 * 1000); // each step takes 10 seconds
// even though we are on a background thread, bindings
// automatically marshal property updates to the UI thread
// this is NOT TRUE for INotifyCollectionChanged updates!
PercentDone += .1;
}
Busy = false;
});
}
You can bind Busy to an overlay that blocks all UI while execution runs, bind PercentComplete to a progress bar, and PerformWork to a button.
I am very new to all this so please bear with me! I am writing a small app to control my telescope, at the moment I can connect to it and tell it where to point. I want to have a couple of text boxes, or labels that constantly update with the telescopes position - T is the telescope object and I am calling T.Altitude, T.Azimuth, T.RightAscention and T.Declination and I want these values to update the four labels every half second or so. I assume I need to use a background worker but am I correct? Will I be able to access the Telescope object since it was created on the main thread? And how exactly do I do it all! This is what I have so far (and it aint much!)...
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
string Az = T.Azimuth.ToString();
string Alt = T.Altitude.ToString();
string Dec = T.Declination.ToString();
string Ra = T.RightAscension.ToString();
System.Threading.Thread.Sleep(500);
}
In your case you should consider using one of the Timer classes. Those classes call a given delegate in specified intervals.
The Timer class from Windows.Forms namespace calls a delegate in UI thread, so you will not have to bother with dispatching or anything, but it might make UI less responsive if you call it too often.
Other Timers use separate threads, so you will need to use either Dispatcher object or SynchronizationContext object to modify UI values. You can read more about those on msdn.
The easiest way is probably as suggested to use a Windows.Forms.Timer to periodically update the Gui with current values from your Telescope (object).
As a side note, the Background Worker is kind of obsolete in C# 5.0 since it is much easier to use async/await (see this thread about async/await vs BackgroundWorker).
Here is an example implementation in WinForms which refreshes a set of labels every 500 milliseconds.
public partial class MyForm : Form
{
private readonly Timer _timer = new Timer();
public MyForm()
{
InitializeComponent();
_timer.Interval = 500;
_timer.Tick += TimerTick;
_timer.Enabled = true;
}
void TimerTick(object sender, EventArgs e)
{
_labelAzimuth.Text = T.Azimuth.ToString();
_labelAltitude.Text = T.Altitude.ToString();
_labelDeclination.Text = T.Declination.ToString();
_labelRightAscension.Text = T.RightAscension.ToString();
}
}
I made a program that loads a bunch of computer information. In the Form_Load event I have it initialize 3 (that number will grow) panels of information. One that has a bunch of unit information seems to make the program load rather slowly. I've tried to speed it up a bunch by switching from WMI to using Native calls, which helped a bunch. Soon though I'm going to have network information posted as well. I used to load that panel but i disabled it for a little bit till I work out the bugs in my other panels. So while learning how I can use a seperate thread to update my battery information I figured that I might be able to create seperate threads in my unit information panel so that it might could load faster. I dont know that any of my information would cause concurrent issues, but i can work on that.
I want to start small so what if i change this
private void Form1_Load(object sender, EventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
to this
private void Form1_Load(object sender, EventArgs e)
{
Thread infoThread = new Thread(new ThreadStart(unitInformationPanel1.PopulateUnitInformation));
infoThread.Start();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
would the info thread be terminated when populate unit info is done? or would it be better to move that thread creation into PopulateUnitInformation? here is what it looks like.
public void PopulateUnitInformation()
{
unitModelLabel.Text = Properties.Settings.Default.UnitModelString;
serialNumberLabel.Text = Properties.Settings.Default.UnitSerialString;
biosVersionLabel.Text = UnitBios.GetBiosNumber();
osLabel.Text = OS.getOSString();
cpuLabel.Text = UnitCpu.GetCpuInfo();
var hdd = HddInfo.GetHddInfo();
diskNameLabel.Text = hdd.Name;
diskCapacityLabel.Text = hdd.Capacity;
diskFirmwareLabel.Text = hdd.Firmware;
memoryLabel.Text = MemoryInformation.GetTotalMemory();
NetworkPresenceInformation.GetAdapatersPresent();
biometricLabel.Text = BiometricInformation.IsPresent ? "Present" : "Not Present";
var networkAdaptersPresense = NetworkPresenceInformation.GetAdapatersPresent();
bluetoothLabel.Text = networkAdaptersPresense[0] ? "Present" : "Not Present";
wifiLabel.Text = networkAdaptersPresense[1] ? "Present" : "Not Present";
cellularLabel.Text = networkAdaptersPresense[2] ? "Present" : "Not Present";
}
--
wow i just ran it with the infothread and it still took some time to load (might be the 12 panels i created in the main thread. but it loaded the 12 frames and the unit information panel populated its information after everything loaded. That was cool, but is it safe? is it somewhat easy to make 12 threads for my panels? or is that dumb?
EDIT
this is what i did for stopwatch.
Stopwatch programTimer;
public Form1()
{
programTimer = Stopwatch.StartNew();
InitializeComponent();
SetupDebugWindow();
TerminateKeymon();
UnitModel.SetModel();
UnitSerialNumber.SetSerialNumber();
}
private void Form1_Shown(object sender, EventArgs e)
{
audioBrightnessPanel1.UpdateBrightnessTrackbar();
applicationLauncherPanel1.LoadApplications();
programTimer.Stop();
Console.WriteLine("Load Time: {0}",programTimer.ElapsedMilliseconds);
timer1.Start();
}
Will this be accurate?
EDIT 2 6/18/2012
Well I took the advice of using backgroundworker. Please let me know if i did this right.
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
You've asked a very broad question, but I'm going to give some general advice. If you want more specific information, you should consider deleting this question and posting more specific individual questions.
First and foremost, you should very strongly consider using something like the System.Threading.Task class for your multithreaded operations. There is a ton of information online about how to get started with it and how you can use Tasks to manage asynchronous operations. The short story is that if you're spinning up your own thread (as you're doing above), you almost certainly should be using something else to do that for you.
Adding multithreading to your code will not, in the strictest sense of the word, make it any "faster"; they will always take the same amount of total processor time. What it can and will do is two things: free up the UI thread to be responsive and allow you to split that "total processor time" across multiple cores or processors, should those be available to the system. So, if you have operation X that takes 10 seconds to complete, then just shifting operation X to another thread will not make it complete any faster than 10 seconds.
No, what you are doing above is not safe. I'm assuming that somewhere you've turned off checking for cross-thread communication errors in your app? Otherwise, that code should throw an exception, assuming this is a WinForms or WPF application. This is one reason to use Tasks, as you can easily separate the part of your process that actually takes a long time (or isn't UI related), then add a task continuation that uses the results and populates the UI elements within a properly synchronized context.
So my final approach this was as follows. I felt that my Main Form was doing more than it should. Sticking with the single responsibility principle I decided that MainForm should only be responsible for one thing, showing and displaying all 12 panels (now down to 11, i turned one into a menu item). So moved all the multithreading out of mainform and into program.cs. I found that this was even a little more difficult. What I did find though was a simple solution that allows me to not even worry about multithreading at all. It was the Idle event. Here is what i chose to do.
[STAThread]
static void Main()
{
DateTime current = DateTime.Now;
DateTime today = new DateTime(2012,7,19);
TimeSpan span = current.Subtract(today);
if (span.Days<0)
{
MessageBox.Show("Please adjust Time then restart Aspects","Adjust Time");
Process.Start("timedate.cpl").WaitForExit();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Idle += new EventHandler(Application_Idle);
mainForm = new MainForm();
mainForm.Closing += new CancelEventHandler(mainForm_Closing);
#if !DEBUG
TerminateKeymon();
StartSerial();
SetupDefaultValues();
EmbeddedMessageBox(0);
#endif
Application.Run(mainForm);
}
}
static void Application_Idle(object sender, EventArgs e)
{
Application.Idle -= Application_Idle;
mainForm.toolStripProgressBar1.Increment(1);
UnitInformation.SetupUnitInformation();
mainForm.toolStripProgressBar1.Increment(1);
Aspects.Unit.HddInfo.GetHddInfo();
mainForm.toolStripProgressBar1.Increment(1);
for (int i = 0; i < mainForm.Controls.Count; i++)
{
if (mainForm.Controls[i] is AbstractSuperPanel)
{
try
{
var startMe = mainForm.Controls[i] as AbstractSuperPanel;
startMe.StartWorking();
mainForm.toolStripProgressBar1.Increment(1);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + mainForm.Controls[i].ToString());
}
}
}
mainForm.toolStripProgressBar1.Value = 0;
}
to sum up what that does is is I add a idle listener event. Once the thead goes idle (basically meaning that Mainform is finished drawing and making all 12 panels and is showing on my desktop) I then kill the idle event listener and tell all my panels and classes to start working one at a time, updating my progress bar as I go. It works great. The load time is still the same as it was before, but there is window visibile after only a few seconds. Maybe not the best use of resources, but i think the solution is simple and straight forward.
I had a question somewhat related to this for Mobile app development a few months back (see How to write a Trigger?), and Marc "the man" Gravell posted back with a simple class that I modified to return data to my main application whenever the thread was complete.
The actual class I put into use has loads of pointless data (for you), so I'm going to paste in a revised version of Mr. Gravell's code using techniques which I used to make them work:
First, I had to create my own EventArgs class:
public class SuperEventArgs : EventArgs {
private object data;
public SuperEventArgs(object data) : base() {
this.data = data;
}
public object Data { get { return data; } }
}
Using that, here is a class I created to pass my data back to the main thread:
public delegate event DataChangedHandler(object sender, SuperEventArgs e);
public class Simple1 {
private object parameter1, parameter2;
private Control parent;
#if PocketPC
public delegate void MethodInvoker(); // include this if it is not defined
#endif
public Simple1(Control frmControl, object param1, object param2) {
parent = frmControl;
parameter1 = param1;
parameter2 = param2;
}
public event DataChangedHandler DataChanged;
public void Start() {
object myData = new object(); // whatever this is. DataTable?
try {
// long routine code goes here
} finally {
if (DataChanged != null) {
SuperEventArgs e = new SuperEventArgs(myData);
MethodInvoker methInvoker = delegate {
DataChanged(this, e);
};
try {
parent.BeginInvoke(methInvoker);
} catch (Exception err) {
Log(err); // something you'd write
}
}
}
}
}
Back in the actual main thread of execution, you'd do something like this:
public partial class Form1 : Form {
private Simple1 simple;
public Form1() {
object query = new object(); // something you want to pass in
simple = new Simple1(this, query, DateTime.Now);
simple.DataChanged += new DataChangedHandler(simple1_DataChanged);
Thread thread = new Thread(simpleStart);
thread.Start();
}
private void simpleStart() {
if (simple != null) {
simple.Start();
}
}
private void simple1_DataChanged(object sender, SuperEventArgs e) {
MyFancyData fancy = e.Data as MyFancyData;
if (fancy != null) {
// populate your form with the data you received.
}
}
}
I know it looks long, but it works really well!
This is not anything I have actually tested, of course, because there isn't any data. If you get to working with it and you experience any issues, let me know and I'll happily help you work through them.
~JoeP
I have a c# application that uses a background worker thread, and quite successfully updates the UI from the running thread. The application involves shortest path routing on a network, and I display the network and the shortest path, on the UI, as the background worker proceeds. I would like to allow the user to slow down the display through use of a slider, while the application is running.
I found this as a suggestion, but it is in vb.net, I am not clear on how to get it to work in c#.
How can the BackgroundWorker get values from the UI thread while it is running?
I can pass the value of the slider to the backgroundworker as follows:
// Start the asynchronous operation.
delay = this.trackBar1.Value;
backgroundWorker1.RunWorkerAsync(delay);
and use it within the backgroundworker thread, but it only uses the initially-sent value. I am not clear on how to pick up the value from inside the backgroundworker when I move the slider on the UI.
I have previously used multiple threads and delegates, but if it is possible to utilize the background worker, I would prefer it for its simplicity.
5/10/2012
Thanks to all for your responses. I am still having problems, most likely because of how I have structured things. The heavy duty calculations for network routing are done in the TransportationDelayModel class. BackgroundWorker_DoWork creates an instance of this class, and then kicks it off. The delay is handled in TransportationDelayModel.
The skeleton of code is as follows:
In 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;
}
}
The TransportationDelayModel constructor is:
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 the tdmWorker, which allows me to pass information back to the UI.
In the internal calculations in TransportationDelayModel, I sleep for the delay period
if (delay2 > 0)
{
tdmWorker.ReportProgress(-12, zzz);
System.Threading.Thread.Sleep(delay2);
}
so the problem seems to be how to pass an updated slider value from the UI back to the object that is executing in the background worker. I have tried a number of combinations, sort of thrashing around, to no avail, either nothing happens or I get a message about not being allowed to access what is happening on the other thread. I realize that if I were doing all the work in the DoWork event handler, then I should be able to do things as you suggest, but there is too much complexity for that to happen.
Again, thank you for your suggestions and help.
6/2/2012
I have resolved this problem by two methods, but I have some questions. Per my comment to R. Harvey, I have built a simple application. It consists of a form with a run button, a slider, and a rich text box. The run button launches a background worker thread that instantiates an object of class "Model" that does all the work (a simplified surrogate for my TransportationModel). The Model class simply writes 100 lines to the text box, incrementing the number of dots in each line by 1, with a delay between each line based on the setting of the slider, and the slider value at the end of the line, something like this:
....................58
.....................58
......................58
.......................51
........................44
.........................44
The objective of this exercise is to be able to move the slider on the form while the "Model" is running, and get the delay to change (as in above).
My first solution involves the creation of 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 trackbar is scrolled:
private void trackBar1_Scroll(object sender, EventArgs e)
{
Globals.globalDelay = this.trackBar1.Value;
}
and in the Model, I just pick up the value of the 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 just fine.
My question: are there any drawbacks to this approach, which seems very simple to implement and quite general.
The second approach, based on suggestions by R. Harvey, makes use of delegates and invoke.
I create 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 launched by the run button, the calling form is passed
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 on the passed form to get the value of the trackbar.
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);
}
}
This works as well. I kept getting an error until I made the member variable static, e.g.
public static MyDelegates.DelegateCheckTrackBarValue m_CheckTrackBarValue=null;
My questions on this solution: Are there advantages to this solution as regards to the previous version? Am I making things too complicated in the way I have implemented this? Why does m_CheckTrackBarValue need to be static.
I apologize for the length of this edit, but I thought that the problem and solutions might be of interest to others.
You have to pass the TrackBar object to the BackgroundWorker, not delay. delay doesn't change once you set it.
To simplify the needed Invoke(), you can use a helper method, such as this one:
Async.UI(delegate { textBox1.Text = "This is way easier!"; }, textBox1, true);
I will assume that you are already familiarized with cross-thread invocation to update the UI. So, the solution is very simple: in your worker thread, after each iteration, invoke the UI to get the slider thumb position.
To use a backgroundworker, you add a method to the DoWork property, like this:
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 where the updated delay is set.
This could be an integer field that is available on the containing Form or UI control, or it could be the TrackBar itself.
I am currently working on a home project for myself.
The program is written in C# using winforms.
The problem I'm currently experiencing is as followed:
I have a listview in my mainform called lvwGames
When I run the program without debugging, it runs fine.
However when I start with a debug, I get an error. This has something to do with the background worker thread.
Allow me to post some code to assist me.
private void MainViewLoad(object sender, EventArgs e)
{
RefreshGamesListView();
}
Nothing special here.
The reason I am calling RefreshGamesListView() is because I have to refresh on several occasions.
The method being called looks like this.
public void RefreshGamesListView()
{
pbRefreshGamesList.Value = 0;
bgwRefreshList.RunWorkerAsync();
}
So when the method is called, the background worker is called and runs the dowork method.
This one is quite big.
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
lvwGames.Items.Add(li);
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
When i run the code in debug, when the code is on lvwGames.Items.Add(li);
It gives me the following error:
Cross-thread operation not valid: Control 'lvwGames' accessed from a thread other than the thread it was created on.
I have absolutely no clue why.
I think it is code specific. But it can also mean I don't get the background worker completely, and specifically when to use it properly.
The reason I'm using it is because I'm loading a large large list from the database, I want to keep responsiveness in the UI when the list is loaded, and inform the users how far it is, using a progress bar.
If any code is missing, or you actually understand why this is happening PLEASE explain me why in this case its causing the error. You don't need to fix it for me. I just want to know WHY it's caused.
Thanks for taking the time to read this post. I hope to be able to continue using the debugger soon. :)
You need to call Conrol.Invoke when accessing visual controls from background threads.
if (_lvwGames.IsHandleCreated) {
Action addGameToList = () => {
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
....
_lvwGames.Items.Add(li);
};
if (_lvwGames.InvokeRequired) {
_lvwGames.Invoke(addGameToList);
} else {
addGameToList();
}
}
From Manipulating Controls from Threads
...For example, you might call a method that disables a button or
updates a display on a form in response to action taken by a thread.
The .NET Framework provides methods that are safe to call from any
thread for invoking methods that interact with controls owned by other
threads. The Control.Invoke method allows for the synchronous
execution of methods on controls...
This is because you're attempting to access a UI control (lvwGames) from a background thread. The way to make it work requires you to marshal the information back to the main UI thread and update the control from there:
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
// This is the new line you need:
lvwGames.Invoke(new MethodInvoker(delegate { lvwGames.Items.Add(item) }));
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
Normally you would check the InvokeRequired property first as mentioned in other answers, but there is really no need if you are always calling it from the background thread. Your DoWork method will always require an invoke call, so you might as well just go ahead and write it like that.
This happening cause, just like compiler cliams, you are going to update UI control content from another thread. You can not do that, as UI control can be updated only within main thread.
Please have look on this SO answer with example code provided:
Invoke from another thread
The background worker is not working properly if you run in debug mode in studio. If you have calls that use the windows handle to retrieve messages, then they will fail. If you for instance have a progressChanged event handler and this changes a text in a textbox that might fail.
I had this scenario: A Form that has a background worker. If I just start the worker without getting a dialog box up first then it works ok. If I show a dialog and then start the background worker then it fails. When I run the program normally it does not fail. It is somehow the debug environment that destroys the link between the events and the foreground window. I have changed my code to use invoke, and now all works both in when running in release and when I debug.
Here is a link explaining what can be done to make a program thread safe.
http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
I did not do the same as the sample to microsoft. I made delegates, assigned to the functions I needed to run. and called invoke on them.
sample pseudo code:
class MyClassWithDelegates
{
public delegate void ProgressDelegate( int progress );
public ProgressDelegate myProgress;
public void MyProgress(int progress)
{
myTextbox.Text = ..... ; // this is code that must be run in the GUI thread.
}
public MyClassWithDelegates()
{
myProgress = new ProgressDelegate(MyProgress);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Invoke( myProgress, e.ProgressPercentage );
}
}
All code that potentially have to be run in the GUI thread of the application must be Invoked to be safe.