Basically, I have a class and inside it a function which counts all lines within a text file.
I need it to update the progress bar from Form1 with each line it counts. I have tried:
public static void rfile(string f)
{
string[] lines = File.ReadAllLines(f);
Form1 form = new Form1();
foreach (string l in lines)
{
form.increaseProg();
}
}
Form.cs
public void increaseProg()
{
progressBar.Value++;
System.Threading.Thread.Sleep(1000);
progressBar.Refresh();
}
But that doesn't seem to increase the progress bar at all.
You can leverage the Progress class to make updating the UI during a long running operation easy on everyone involved. Create the Progress class within your form, and indicate how it should update the UI when it is given progress. Then give that object to the other class that is going to be doing the long running work:
private void button1_Click(object sender, EventArgs args)
{
Progress<int> progress = new Progress<int>();
progress.ProgressChanged += (p, value) => progressbar1.Value = value;
Task.Run(() => SomeOtherClass.DoWork("c:/temp.txt", progress));
}
The long running work is of course done in another thread to avoid blocking the UI. The Progress class will take care of marshaling the ProgressChanged event to the UI thread for us, so we don't need to think about it.
Now for the worker we just need to report progress when needed:
public class SomeOtherClass
{
public static void DoWork(string filepath, IProgress<int> progress)
{
int currentProgress = 0;
foreach (var line in File.ReadLines(filepath))
{
DoSomethingWithLine();
currentProgress++;
progress.Report(currentProgress);
}
}
}
Note that another advantage of this approach is that SomeOtherClass doesn't need to know anything about the form. It can be called by anyone that can provide an IProgress object. If you have some other form needing to call that method you don't need to change it at all. It also means that if one developer is writing the form and another is coding the long running process they only need to agree on the signature of the DoWork method; and from then on the UI guy and do all of the UI work and the non-UI guy can do all of the non-UI work, and they don't need to worry about what the other person is doing.
As for why your code isn't working, the problem is that your worker method isn't accessing the instance of the form that is being displayed, you're creating a brand new form, modifying it's progress bar, never showing it to anyone, and then throwing it away.
This is because you are reading the lines and when updating the progressbar, the UI won't entirely refresh as you are imagining. What you need to do, as it sometimes can work, is to do a progressBar.Refresh() (I cannot remember if the progressbar has a refresh function) so it can update the form visuals (specifically the progressbar) whilst it is working the way through the lines. basically, its "too fast" which can cause the delay in updating.
The progress bar is updating, you just don't see the refresh happening. One way is to make it multithreaded perhaps but this would be overkill for reading lines and updating the UI.
not only this, your code basically does NOT show the UI... it is updating, and not showing the UI, you need to Show() the form and perform the updates on it. you instantiate the form, but you don't actually show it.
using System.Windows.Threading;
Dispatcher.CurrentDispatcher.Invoke(new System.Action(() => ProgressBar1.Value = value));
You can sometimes get away with using the line above to update the UI while doing other work. you'll need some way to keep track of the progress numerically though.
as stated, since you are tying up your main thread with processing, you can't update the UI on the same thread. The above carves out a tiny chunk of time to do this. The only other method would be a background worker, though that is probably overkill for your situation.
edit (Display on separate form)
Did you set the modifier on your progressbar to public? This solution works just fine for showing progress in a separate window, and it updates correctly.
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
f2.progressBar1.Maximum = 1000;
f2.Show();
for(int i = 0; i < 1000; i++)
{
Thread.Sleep(10);
f2.progressBar1.Value++;
}
}
Note that the UI of the progress window will be locked until the underlying process completes. I just tested this and it works fine.
Update progress on same form, but from different class
You need to pass a reference of the progressbar so your method knows what it is updating.
}
private void button1_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
progressBar1.Maximum = 1000;
for(int i = 0; i < 1000; i++)
{
Thread.Sleep(10);
c.IncreaseProgress(progressBar1);
}
}
}
class Class1
{
public void IncreaseProgress(ProgressBar p)
{
p.Value++;
}
}
Both of the above methods will lock the UI while updating though. Use a BackgroundWorker to avoid this. This solution works well in quick and dirty utilities though.
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 trying to upgrade my winforms application with a form that would display the progress of a time consuming task, but regardless of what I do the form does not work properly. Here is what I have:
Setup:
There is a form (Form1) with one button and the following code:
public partial class Form1 : Form
{
CancellationTokenSource cToken = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
cToken = new CancellationTokenSource();
using (Form2 form = new Form2())
{
form.Show(); //Shows the 'progress' form
int i = 0;
Task.Factory.StartNew(() =>
{
while (cToken.Token.IsCancellationRequested == false && i <= 5) //Runs the task until it is cancelled or has reached its end
{
form.UpdateProgress(i); //Updates the 'progress' form
Thread.Sleep(500); //Waits simulating time consuming activity
i++;
}
}, cToken.Token, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).Wait();
}
}
}
Then there is another form (Form2) that is displayed by Form1 when a time consuming operation is running:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
progressBar1.Step = 1;
progressBar1.Maximum = 6;
}
public void UpdateProgress(int i)
{
progressBar1.PerformStep();
label1.Text = string.Format("Step {0} of 6", i);
}
}
This form has a progress bar, a label (shows the percentage) and a cancel button.
Expected behavior:
When a time consuming operation is running, Form2 (the one with the progress bar) is to be shown and is expected to update the progress bar and the label text with each call of UpdateProgress(). At the same moment neither of the forms should be or appear to be blocked so that the user can cancel the time consuming task at any time.
Current behavior:
Form2 is shown, the progress bar updates properly, but other controls are not displayed and both forms are blocked.
Additional information:
I am using C# 4.0 so I cannot use Progress and I would like to avoid using BackgroundWorker or events (not that I don't like them, I just want to use TPL this time).
The question:
Is there I way I can achieve my goal using TPL? I've been googling and reading MSDN docs for days already but so far have not found a working solution. I've come across many topics here on stackoverflow, but they mostly show how to asynchronously update a progress bar within the same form and I can make it work, but it isn't what I really need. I am pretty sure I am missing something but I can't figure out what.
The code starts a new tash on the UI thread with TaskScheduler.FromCurrentSynchronizationContext() and then calls .Wait() on it.
You need to remove both .Wait() and TaskScheduler.FromCurrentSynchronizationContext(). Inside the child form's UpdateProgress you should make the appropriate Invoke calls to update the progress bar, or use Task.StartNew on the UI thread there to update the UI.
You should also consider adding the Microsoft.Bcl.Async package to your project, which adds async/await and Progress<T> support to .NET 4.0 projects. This will allow you to clean up the code considerably.
The .Wait() on the end of your thread is what is causing the Synchronous behavior.
I am currently writing my first program on C# and I am extremely new to the language (used to only work with C so far). I have done a lot of research, but all answers were too general and I simply couldn't get it t work.
So here my (very common) problem:
I have a WPF application which takes inputs from a few textboxes filled by the user and then uses that to do a lot of calculations with them. They should take around 2-3 minutes, so I would like to update a progress bar and a textblock telling me what the current status is.
Also I need to store the UI inputs from the user and give them to the thread, so I have a third class, which I use to create an object and would like to pass this object to the background thread.
Obviously I would run the calculations in another thread, so the UI doesn't freeze, but I don't know how to update the UI, since all the calculation methods are part of another class.
After a lot of reasearch I think the best method to go with would be using dispatchers and TPL and not a backgroundworker, but honestly I am not sure how they work and after around 20 hours of trial and error with other answers, I decided to ask a question myself.
Here a very simple structure of my program:
public partial class MainWindow : Window
{
public MainWindow()
{
Initialize Component();
}
private void startCalc(object sender, RoutedEventArgs e)
{
inputValues input = new inputValues();
calcClass calculations = new calcClass();
try
{
input.pota = Convert.ToDouble(aVar.Text);
input.potb = Convert.ToDouble(bVar.Text);
input.potc = Convert.ToDouble(cVar.Text);
input.potd = Convert.ToDouble(dVar.Text);
input.potf = Convert.ToDouble(fVar.Text);
input.potA = Convert.ToDouble(AVar.Text);
input.potB = Convert.ToDouble(BVar.Text);
input.initStart = Convert.ToDouble(initStart.Text);
input.initEnd = Convert.ToDouble(initEnd.Text);
input.inita = Convert.ToDouble(inita.Text);
input.initb = Convert.ToDouble(initb.Text);
input.initc = Convert.ToDouble(initb.Text);
}
catch
{
MessageBox.Show("Some input values are not of the expected Type.", "Wrong Input", MessageBoxButton.OK, MessageBoxImage.Error);
}
Thread calcthread = new Thread(new ParameterizedThreadStart(calculations.testMethod);
calcthread.Start(input);
}
public class inputValues
{
public double pota, potb, potc, potd, potf, potA, potB;
public double initStart, initEnd, inita, initb, initc;
}
public class calcClass
{
public void testmethod(inputValues input)
{
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
int i;
//the input object will be used somehow, but that doesn't matter for my problem
for (i = 0; i < 1000; i++)
{
Thread.Sleep(10);
}
}
}
I would be very grateful if someone had a simple explanation how to update the UI from inside the testmethod. Since I am new to C# and object oriented programming, too complicated answers I will very likely not understand, I'll do my best though.
Also if someone has a better idea in general (maybe using backgroundworker or anything else) I am open to see it.
First you need to use Dispatcher.Invoke to change the UI from another thread and to do that from another class, you can use events.
Then you can register to that event(s) in the main class and Dispatch the changes to the UI and in the calculation class you throw the event when you want to notify the UI:
class MainWindow : Window
{
private void startCalc()
{
//your code
CalcClass calc = new CalcClass();
calc.ProgressUpdate += (s, e) => {
Dispatcher.Invoke((Action)delegate() { /* update UI */ });
};
Thread calcthread = new Thread(new ParameterizedThreadStart(calc.testMethod));
calcthread.Start(input);
}
}
class CalcClass
{
public event EventHandler ProgressUpdate;
public void testMethod(object input)
{
//part 1
if(ProgressUpdate != null)
ProgressUpdate(this, new YourEventArgs(status));
//part 2
}
}
UPDATE:
As it seems this is still an often visited question and answer I want to update this answer with how I would do it now (with .NET 4.5) - this is a little longer as I will show some different possibilities:
class MainWindow : Window
{
Task calcTask = null;
void buttonStartCalc_Clicked(object sender, EventArgs e) { StartCalc(); } // #1
async void buttonDoCalc_Clicked(object sender, EventArgs e) // #2
{
await CalcAsync(); // #2
}
void StartCalc()
{
var calc = PrepareCalc();
calcTask = Task.Run(() => calc.TestMethod(input)); // #3
}
Task CalcAsync()
{
var calc = PrepareCalc();
return Task.Run(() => calc.TestMethod(input)); // #4
}
CalcClass PrepareCalc()
{
//your code
var calc = new CalcClass();
calc.ProgressUpdate += (s, e) => Dispatcher.Invoke((Action)delegate()
{
// update UI
});
return calc;
}
}
class CalcClass
{
public event EventHandler<EventArgs<YourStatus>> ProgressUpdate; // #5
public TestMethod(InputValues input)
{
//part 1
ProgressUpdate.Raise(this, status); // #6 - status is of type YourStatus
// alternative version to the extension for C# 6+:
ProgressUpdate?.Invoke(this, new EventArgs<YourStatus>(status));
//part 2
}
}
static class EventExtensions
{
public static void Raise<T>(this EventHandler<EventArgs<T>> theEvent,
object sender, T args)
{
if (theEvent != null)
theEvent(sender, new EventArgs<T>(args));
}
}
#1) How to start the "synchronous" calculations and run them in the background
#2) How to start it "asynchronous" and "await it": Here the calculation is executed and completed before the method returns, but because of the async/await the UI is not blocked (BTW: such event handlers are the only valid usages of async void as the event handler must return void - use async Task in all other cases)
#3) Instead of a new Thread we now use a Task. To later be able to check its (successfull) completion we save it in the global calcTask member. In the background this also starts a new thread and runs the action there, but it is much easier to handle and has some other benefits.
#4) Here we also start the action, but this time we return the task, so the "async event handler" can "await it". We could also create async Task CalcAsync() and then await Task.Run(() => calc.TestMethod(input)).ConfigureAwait(false); (FYI: the ConfigureAwait(false) is to avoid deadlocks, you should read up on this if you use async/await as it would be to much to explain here) which would result in the same workflow, but as the Task.Run is the only "awaitable operation" and is the last one we can simply return the task and save one context switch, which saves some execution time.
#5) Here I now use a "strongly typed generic event" so we can pass and receive our "status object" easily
#6) Here I use the extension defined below, which (aside from ease of use) solve the possible race condition in the old example. There it could have happened that the event got null after the if-check, but before the call if the event handler was removed in another thread at just that moment. This can't happen here, as the extensions gets a "copy" of the event delegate and in the same situation the handler is still registered inside the Raise method.
I am going to throw you a curve ball here. If I have said it once I have said it a hundred times. Marshaling operations like Invoke or BeginInvoke are not always the best methods for updating the UI with worker thread progress.
In this case it usually works better to have the worker thread publish its progress information to a shared data structure that the UI thread then polls at regular intervals. This has several advantages.
It breaks the tight coupling between the UI and worker thread that Invoke imposes.
The UI thread gets to dictate when the UI controls get updated...the way it should be anyway when you really think about it.
There is no risk of overrunning the UI message queue as would be the case if BeginInvoke were used from the worker thread.
The worker thread does not have to wait for a response from the UI thread as would be the case with Invoke.
You get more throughput on both the UI and worker threads.
Invoke and BeginInvoke are expensive operations.
So in your calcClass create a data structure that will hold the progress information.
public class calcClass
{
private double percentComplete = 0;
public double PercentComplete
{
get
{
// Do a thread-safe read here.
return Interlocked.CompareExchange(ref percentComplete, 0, 0);
}
}
public testMethod(object input)
{
int count = 1000;
for (int i = 0; i < count; i++)
{
Thread.Sleep(10);
double newvalue = ((double)i + 1) / (double)count;
Interlocked.Exchange(ref percentComplete, newvalue);
}
}
}
Then in your MainWindow class use a DispatcherTimer to periodically poll the progress information. Configure the DispatcherTimer to raise the Tick event on whatever interval is most appropriate for your situation.
public partial class MainWindow : Window
{
public void YourDispatcherTimer_Tick(object sender, EventArgs args)
{
YourProgressBar.Value = calculation.PercentComplete;
}
}
You're right that you should use the Dispatcher to update controls on the UI thread, and also right that long-running processes should not run on the UI thread. Even if you run the long-running process asynchronously on the UI thread, it can still cause performance issues.
It should be noted that Dispatcher.CurrentDispatcher will return the dispatcher for the current thread, not necessarily the UI thread. I think you can use Application.Current.Dispatcher to get a reference to the UI thread's dispatcher if that's available to you, but if not you'll have to pass the UI dispatcher in to your background thread.
Typically I use the Task Parallel Library for threading operations instead of a BackgroundWorker. I just find it easier to use.
For example,
Task.Factory.StartNew(() =>
SomeObject.RunLongProcess(someDataObject));
where
void RunLongProcess(SomeViewModel someDataObject)
{
for (int i = 0; i <= 1000; i++)
{
Thread.Sleep(10);
// Update every 10 executions
if (i % 10 == 0)
{
// Send message to UI thread
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action)(() => someDataObject.ProgressValue = (i / 1000)));
}
}
}
Everything that interacts with the UI must be called in the UI thread (unless it is a frozen object). To do that, you can use the dispatcher.
var disp = /* Get the UI dispatcher, each WPF object has a dispatcher which you can query*/
disp.BeginInvoke(DispatcherPriority.Normal,
(Action)(() => /*Do your UI Stuff here*/));
I use BeginInvoke here, usually a backgroundworker doesn't need to wait that the UI updates. If you want to wait, you can use Invoke. But you should be careful not to call BeginInvoke to fast to often, this can get really nasty.
By the way, The BackgroundWorker class helps with this kind of taks. It allows Reporting changes, like a percentage and dispatches this automatically from the Background thread into the ui thread. For the most thread <> update ui tasks the BackgroundWorker is a great tool.
If this is a long calculation then I would go background worker. It has progress support. It also has support for cancel.
http://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx
Here I have a TextBox bound to contents.
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.Write("backgroundWorker_RunWorkerCompleted");
if (e.Cancelled)
{
contents = "Cancelled get contents.";
NotifyPropertyChanged("Contents");
}
else if (e.Error != null)
{
contents = "An Error Occured in get contents";
NotifyPropertyChanged("Contents");
}
else
{
contents = (string)e.Result;
if (contentTabSelectd) NotifyPropertyChanged("Contents");
}
}
You are going to have to come back to your main thread (also called UI thread) in order to update the UI.
Any other thread trying to update your UI will just cause exceptions to be thrown all over the place.
So because you are in WPF, you can use the Dispatcher and more specifically a beginInvoke on this dispatcher. This will allow you to execute what needs done (typically Update the UI) in the UI thread.
You migh also want to "register" the UI in your business, by maintaining a reference to a control/form, so you can use its dispatcher.
Thank God, Microsoft got that figured out in WPF :)
Every Control, like a progress bar, button, form, etc. has a Dispatcher on it. You can give the Dispatcher an Action that needs to be performed, and it will automatically call it on the correct thread (an Action is like a function delegate).
You can find an example here.
Of course, you'll have to have the control accessible from other classes, e.g. by making it public and handing a reference to the Window to your other class, or maybe by passing a reference only to the progress bar.
Felt the need to add this better answer, as nothing except BackgroundWorker seemed to help me, and the answer dealing with that thus far was woefully incomplete. This is how you would update a XAML page called MainWindow that has an Image tag like this:
<Image Name="imgNtwkInd" Source="Images/network_on.jpg" Width="50" />
with a BackgroundWorker process to show if you are connected to the network or not:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
public partial class MainWindow : Window
{
private BackgroundWorker bw = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
// Set up background worker to allow progress reporting and cancellation
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
// This is your main work process that records progress
bw.DoWork += new DoWorkEventHandler(SomeClass.DoWork);
// This will update your page based on that progress
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
// This starts your background worker and "DoWork()"
bw.RunWorkerAsync();
// When this page closes, this will run and cancel your background worker
this.Closing += new CancelEventHandler(Page_Unload);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BitmapImage bImg = new BitmapImage();
bool connected = false;
string response = e.ProgressPercentage.ToString(); // will either be 1 or 0 for true/false -- this is the result recorded in DoWork()
if (response == "1")
connected = true;
// Do something with the result we got
if (!connected)
{
bImg.BeginInit();
bImg.UriSource = new Uri("Images/network_off.jpg", UriKind.Relative);
bImg.EndInit();
imgNtwkInd.Source = bImg;
}
else
{
bImg.BeginInit();
bImg.UriSource = new Uri("Images/network_on.jpg", UriKind.Relative);
bImg.EndInit();
imgNtwkInd.Source = bImg;
}
}
private void Page_Unload(object sender, CancelEventArgs e)
{
bw.CancelAsync(); // stops the background worker when unloading the page
}
}
public class SomeClass
{
public static bool connected = false;
public void DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
int i = 0;
do
{
connected = CheckConn(); // do some task and get the result
if (bw.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
Thread.Sleep(1000);
// Record your result here
if (connected)
bw.ReportProgress(1);
else
bw.ReportProgress(0);
}
}
while (i == 0);
}
private static bool CheckConn()
{
bool conn = false;
Ping png = new Ping();
string host = "SomeComputerNameHere";
try
{
PingReply pngReply = png.Send(host);
if (pngReply.Status == IPStatus.Success)
conn = true;
}
catch (PingException ex)
{
// write exception to log
}
return conn;
}
}
For more information: https://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx
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.
Creating a .net application in C#, windows forms. How do I update the progress bar 1 step every cycle of a 100 cycle loop?
(I’m processing an excel sheet in the loop.)
The progress bar controls are in the UI class which connects to the controller class which connects to a custom class
(MVC pattern). The loop is in the custom class.
Do I need to send the UI class instance all the way down in each method or is there a better way?
Right now the progress bar updates after the loop finishes. Application.doevents and .update or .refresh don’t work.
Say the below is your class responsible for doing the work with the loop in it. Add an event to indicate your progress. Then from your UI simply handle that event and update the progress bar accordingly.
sealed class Looper
{
public event EventHandler ProgressUpdated;
private int _progress;
public int Progress
{
get { return _progress; }
private set
{
_progress = value;
OnProgressUpdated();
}
}
public void DoLoop()
{
_progress = 0;
for (int i = 0; i < 100; ++i)
{
Thread.Sleep(100);
Progress = i;
}
}
private void OnProgressUpdated()
{
EventHandler handler = ProgressUpdated;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
You might implement this by having a BackgroundWorker as part of your UI, where in the backgroundWorker.DoWork event you call looper.DoLoop(). Then in your handler for the looper.ProgressUpdated event you can call backgroundWorker.ReportProgress to increment your progress bar from the UI thread.
Note that it would probably make more sense to include the progress itself in the information carried by your ProgressUpdated event (I just didn't feel like writing out a new class deriving from EventArgs to illustrate this; you probably get the picture anyway).
Also note that the above really doesn't make sense unless you're executing your code with the loop on a separate thread from the UI thread. Otherwise, all of your work is getting done before the next UI refresh anyway, so your progress bar would not be providing any value (it would just go from 0 to 100 when the loop completed).
Just an example of how this sort of thing can be achieved.
I usually have one class that does the invocation checks on the UI. UI -> "UpdaterClass" -> other class.
The Updater class has predefined methods and references to the UI controls. So Updater.StepProgressBar() is what I call to step the UI progress bar. I pass the Updater class reference to any class that is going to need to update the UI directly.
This way, all UI updates from different threads are handled by one class. Not the most generic way to impliment it, but it never fails.
Example psuedocode:
class Updater()
{
public ProgressBar pb;
delegate void UpdateProgressBar();
public StepProgressBar()
{
if(pb.InvokeRequired)
{
BeginInvoke(new UpdateProgressBar(this.StepProgressBar);
}
else
{
pb.Step();
}
}
}
Something like that.
You could use a delegate. When your background process creates the custom class, tie on a delegate that gets called from the custom class to report update. Then you can react to that call in the UI layer and update the progress bar from there.
e.g. (warning, psuedocode):
MyCustomClass class = new MyCustomClass();
class.ProgressUpdated += (s,e)=>{ /* update UI */};
class.StartLoop();