Inverted background worker - c#

I have a number of classes that do stuff, typically step through a recordset and call a webservice or two for each record.
At the moment this all runs in the GUI thread and hangs painting. First thought was to use a BackgroundWorker and implement a nice progress bar, handle errors, completion etc. All the nice things a Background worker enables.
As soon as the code hit the screen it started to smell. I was writing a lot of the background worker into each class, repeating most of the ProcessRows method in a bw_DoWork method and thinking there should be a better way, and it's probably already been done.
Before I go ahead and reinvent the wheel is there a pattern or implementation for a class that seperates out the background worker? It would take classes that implement an interface such as ibackgroundable, but the classes could still be run standalone, and would require minimal change to implement the interface.
Edit: A simplified example requested by #Henk:
I have:
private void buttonUnlockCalls_Click(object sender, EventArgs e)
{
UnlockCalls unlockCalls = new UnlockCalls();
unlockCalls.MaxRowsToProcess = 1000;
int processedRows = unlockCalls.ProcessRows();
this.textProcessedRows.text = processedRows.ToString();
}
I think I want:
private void buttonUnlockCalls_Click(object sender, EventArgs e)
{
UnlockCalls unlockCalls = new UnlockCalls();
unlockCalls.MaxRowsToProcess = 1000;
PushToBackground pushToBackground = new PushToBackground(unlockCalls)
pushToBackground.GetReturnValue = pushToBackground_GetReturnValue;
pushToBackground.DoWork();
}
private void pushToBackground_GetReturnValue(object sender, EventArgs e)
{
int processedRows = e.processedRows;
this.textProcessedRows.text = processedRows.ToString();
}
I could go ahead and do this, but don't want to reinvent.
The answer I'm looking for would along the lines of "Yes, Joe did a good implementation of that (here)" or "That's a Proxy Widget pattern, go read about it (here)"

Each operation needs to implement the following interface:
/// <summary>
/// Allows progress to be monitored on a multi step operation
/// </summary>
interface ISteppedOperation
{
/// <summary>
/// Move to the next item to be processed.
/// </summary>
/// <returns>False if no more items</returns>
bool MoveNext();
/// <summary>
/// Processes the current item
/// </summary>
void ProcessCurrent();
int StepCount { get; }
int CurrentStep { get; }
}
This seperates the enumeration of the steps from the processing.
Here is a sample operation:
class SampleOperation : ISteppedOperation
{
private int maxSteps = 100;
//// The basic way of doing work that I want to monitor
//public void DoSteppedWork()
//{
// for (int currentStep = 0; currentStep < maxSteps; currentStep++)
// {
// System.Threading.Thread.Sleep(100);
// }
//}
// The same thing broken down to implement ISteppedOperation
private int currentStep = 0; // before the first step
public bool MoveNext()
{
if (currentStep == maxSteps)
return false;
else
{
currentStep++;
return true;
}
}
public void ProcessCurrent()
{
System.Threading.Thread.Sleep(100);
}
public int StepCount
{
get { return maxSteps; }
}
public int CurrentStep
{
get { return currentStep; }
}
// Re-implement the original method so it can still be run synchronously
public void DoSteppedWork()
{
while (MoveNext())
ProcessCurrent();
}
}
This can be called from the form like this:
private void BackgroundWorkerButton_Click(object sender, EventArgs eventArgs)
{
var operation = new SampleOperation();
BackgroundWorkerButton.Enabled = false;
BackgroundOperation(operation, (s, e) =>
{
BackgroundWorkerButton.Enabled = true;
});
}
private void BackgroundOperation(ISteppedOperation operation, RunWorkerCompletedEventHandler runWorkerCompleted)
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.RunWorkerCompleted += runWorkerCompleted;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork += new DoWorkEventHandler((s, e) =>
{
while (operation.MoveNext())
{
operation.ProcessCurrent();
int percentProgress = (100 * operation.CurrentStep) / operation.StepCount;
backgroundWorker.ReportProgress(percentProgress);
if (backgroundWorker.CancellationPending) break;
}
});
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler((s, e) =>
{
var progressChangedEventArgs = e as ProgressChangedEventArgs;
this.progressBar1.Value = progressChangedEventArgs.ProgressPercentage;
});
backgroundWorker.RunWorkerAsync();
}
I haven't done it yet but I'll be moving BackgroundOperation() into a class of its own and implementing the method to cancel the operation.

I would put my non-UI code into a new class and use a Thread (not background worker). To show progress, have the new class fire events back to the UI and use Dispatcher.Invoke to update the UI.
There is a bit of coding in this, but it is cleaner and works. And is more maintainable than using background worker (which is only really intended for small tasks).

Related

C# Form Controls Won't Update with Multithreading

I've been playing around with multithreading and reading up on some of the questions here, but I haven't found an answer that directly addresses my concerns here.
I have an application that runs on a single thread, except for a progress bar in a separate window. Based on my research, I need to create a new thread for that form which will redraw the form's controls as it's properties change. I've reduced the problem to a simple example below:
Here's the 'main' program:
class Program
{
static MyForm form;
static void Main(string[] args)
{
form = new MyForm();
form.Show();
doWork();
form.Close();
}
//arbitrary example of processing that takes some period of time
static void doWork()
{
while (form.Value < 100000)
{
form.ChangeVal();
Thread.Sleep(1);
}
return;
}
}
...And here's the Form. I'm not including the auto-generated stuff from VS.
public partial class MyForm : Form
{
private int val;
public int Value
{
get { return val; }
set { val = value; }
}
public Thread GUIupdater;
public MyForm()
{
InitializeComponent();
this.Refresh();
}
private void MyForm_Load(object sender, EventArgs e)
{
GUIupdater = new Thread(new ThreadStart(GUIupdaterThread));
GUIupdater.Start();
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(killThreadOnClose);
}
public void ChangeVal()
{
val++;
}
private void changeLabel(string s)
{
label.Text = s;
label.Refresh();
}
private delegate void labelChanger(string s);
private void GUIupdaterThread()
{
while (true)
{
Invoke(new labelChanger(changeLabel), new object[]{val.ToString()} );
Thread.Sleep(100); //100 ms
}
}
private void killThreadOnClose(object sender, FormClosingEventArgs e)
{
GUIupdater.Abort();
}
}
So, my intention here is to have the calculations running constantly, with the window's graphics updating reasonably quickly. When I run the program, however, the invoke function is only called once, and the label never actually updates!
Any and all feedback is appreciated. If you want to view the code in an IDE you can download my project from Here
Edits:
When I add Console.WriteLine Statements, I discovered that the GUIupdaterThread (the thing that's meant to update the GUI) loop always 'breaks' on the Invoke statement, never reaching 'Thread.Sleep'. I changed it to 'BeginInvoke', which causes the loop to function properly, but this hasn't changed the fact that the GUI doesn't update.
CLARIFICATIONS:
About my 'actual' project:
The main thread here in 'Program' simulates my software, which is a plugin implementing an interface. My decision to alter val / value in that thread, not in the thread created by the window, was deliberate.
I'm constrained to using .NET 4.0 . any more recent features can't help me
Since in your application you have GUI thread (main thread) - all UI controls will be accessible from this thread only.
There are several approaches how to update controls from other threads.
I would like to recommend you to use one of modern and native approaches based on Progress < T > class (it's native for .Net platform).
I would suggest overriding the form's OnPaint method. Then inside ChangeVal, after you have updated whatever variables/data you need to update, call this.Invalidate which should trigger the form to repaint itself.
Or if you're just updating a single label, call label.Refresh in your ChangeVal method. The form should update correctly. Here's an example that worked for me:
This form has a single label on it.
public partial class ProgressForm : Form
{
private int currentValue = 0;
public ProgressForm()
{
InitializeComponent();
}
public void ChangeValue(int newValue)
{
currentValue = newValue;
lblValue.Text = string.Format("Current value: {0}", currentValue);
lblValue.Refresh(); //Call Refresh to make the label update itself
}
}
static class Program
{
private static ProgressForm progressForm = null;
[STAThread]
static void Main()
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
progressForm = new ProgressForm();
progressForm.Show();
doWork();
progressForm.Close();
}
//arbitrary example of processing that takes some period of time
static void doWork()
{
int i = 0;
while (i < 100000)
{
progressForm.ChangeValue(i);
Thread.Sleep(1);
i++;
}
return;
}
}
You may use the following instead as you are trying to access UI control other than main thread (from which it is created).
while ( true )
{
Invoke ( ( Action ) (() =>
{
label.Text = val.ToString();
label.Refresh()
Application.DoEvents();
}));
Thread.Sleep( 100 );
}
I recommend you to use "backgroundworker".
First add CheckForIllegalCrossThreadCalls = false; to initialization part otherwise InvalidOperationException occurs.
private void btnDoIt_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Foo();
}
int total = 0;
private void Foo()
{
for (int i = 0; i <= 100000; i++)
{
total += i;
this.Text = i.ToString();
}
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Run next process
}

Can't control Threads from different Methods WPF C#

I'm writing a small Keylogger for some statistics about my typing.
The Keylogger works fine, but now i want to implement it to a wpf to have a better control.
public MainWindow()
{
InitializeComponent();
Thread ThreadLog = new Thread(Log);
Thread ThreadRefreshForm = new Thread(refreshForm);
Thread ThreadAutoSave = new Thread(AutoSave);
ThreadLog.Start();
ThreadRefreshForm.Start();
ThreadAutoSave.Start();
}
private void btn_ThreadLogStop_Click(object sender, RoutedEventArgs e)
{
if (ThreadLog.IsAlive == true)
{
ThreadLog.Abort();
}
This gives me an Error # ThreadLog.IsAlive. How can i solve the Problem?
Thanks for your help!!!!
You should declare your ThreadLog somewhere else and initialize it in the constructor, such that the method can access the ThreadLog:
private Thread ThreadLog;
public MainWindow()
{
InitializeComponent();
ThreadLog = new Thread(Log);
...
}
private void btn_ThreadLogStop_Click(object sender, RoutedEventArgs e)
{
if (ThreadLog.IsAlive == true)
{
ThreadLog.Abort();
}
Generally speaking the correct way how to end threads is like this
private volatile bool m_Stop;
public void ThreadLoop()
{
while(!m_Stop) {
// do some work
}
}
// starting
new Thread(ThreadLoop).Start();
// "force" end
m_Stop = true;
Or if you prefer tasks over threads (which I do):
public void ThreadLoop(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// do some work
}
}
var cancelation = new CancellationTokenSource();
// starting
new Task(() => ThreadLoop(cancelation.Token), cancelation.Token).Start();
// "force" end
cancelation.Cancel();

What is the alternative for BackgroundWorker in Windows 8.1 Universal Apps?

I am migrating my Windows Phone App to Windows Universal Apps. In Phone App, I used BackgroundWorker for database retrieval and then show in UI. Below is the class which I prepared in Windows Phone 8 and how it was called.
public class TestBackgroundWorker
{
private BackgroundWorker backgroundWorker;
ProgressIndicator progressIndicator;
public delegate void functionToRunInBackground();
public functionToRunInBackground currentFunctionToExecute;
public delegate void callbackFunction();
public callbackFunction functionToSendResult;
private bool isCancellationSupported;
private string message;
/// <summary>
///
/// </summary>
/// <param name="functionNameToExecute">specifies function name to be executed in background</param>
/// <param name="isCancellable">Flag which specifies whether the operation is cancellable or not</param>
/// <param name="functionNameWhichGetsResult">Specifies call back function to be executed after the completion of operation</param>
public MCSBackgroundWorker(functionToRunInBackground functionNameToExecute, bool isCancellable, string messageToDisplay, callbackFunction functionNameWhichGetsResult)
{
currentFunctionToExecute = functionNameToExecute;
functionToSendResult = functionNameWhichGetsResult;
isCancellationSupported = isCancellable;
message = messageToDisplay;
backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerSupportsCancellation = isCancellable;
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
deactivateProgressIndicator();
functionToSendResult();
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (currentFunctionToExecute != null)
{
currentFunctionToExecute();
}
}
public void cancelBackgroundOperation()
{
if (isCancellationSupported == true)
{
backgroundWorker.CancelAsync();
}
}
public void Start()
{
backgroundWorker.RunWorkerAsync();
activateProgressIndicator();
}
void activateProgressIndicator()
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var currentPage = App.RootFrame.Content as PhoneApplicationPage;
SystemTray.SetIsVisible(currentPage, true);
SystemTray.SetOpacity(currentPage, 0.5);
SystemTray.SetBackgroundColor(currentPage, Colors.White);
SystemTray.SetForegroundColor(currentPage, Colors.Black);
progressIndicator = new ProgressIndicator();
progressIndicator.IsVisible = true;
progressIndicator.IsIndeterminate = true;
progressIndicator.Text = message;
SystemTray.SetProgressIndicator(currentPage, progressIndicator);
});
}
void deactivateProgressIndicator()
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (progressIndicator != null)
{
var currentPage = App.RootFrame.Content as PhoneApplicationPage;
progressIndicator.IsVisible = false;
SystemTray.SetIsVisible(currentPage, false);
}
});
}
public bool isBackgroundWorkerBusy()
{
return backgroundWorker != null ? backgroundWorker.IsBusy : false;
}
}
}
And calling that as below to run the process in background.
private void loadReports()
{
bgWorker = new TestBackgroundWorker(loadReportsFromDB, true, "Loading...", showReports);
bgWorker.Start();
}
Here, loadReprtsFromDB and showReports are two functions.
Questions:
Can anyone suggest how to achieve same thing in Windows 8.1?
Is there any alternative for PhoneApplicationService.Current.State?
IMHO, even for the desktop, the Task<T> and Progress<T> classes offer a nice alternative to BackgroundWorker, and they are both supported on Windows Phone 8.1. The Task<T> class provides the mechanism to start and then cleanly wait for background operations, while the Progress<T> class provides the mechanism for reporting progress (not part of your example or question, but I mention it because that's the one thing Task along with async/await doesn't provide from BackgroundWorker).
Your example could be changed to something like this:
public class TestBackgroundWorker
{
private Task _task;
private CancellationTokenSource _cancelSource;
public CancellationToken CancellationToken
{
get { return _cancelSource != null ? _cancelSource.Token : null; }
}
ProgressIndicator progressIndicator;
public readonly Action<TestBackgroundWorker> currentFunctionToExecute;
private string message;
/// <summary>
///
/// </summary>
/// <param name="functionNameToExecute">specifies function name to be executed in background</param>
/// <param name="isCancellable">Flag which specifies whether the operation is cancellable or not</param>
/// <param name="functionNameWhichGetsResult">Specifies call back function to be executed after the completion of operation</param>
public MCSBackgroundWorker(Action<TestBackgroundWorker> functionNameToExecute, bool isCancellable, string messageToDisplay)
{
currentFunctionToExecute = functionNameToExecute;
_cancelSource = isCancellable ? new CancellationTokenSource() : null;
message = messageToDisplay;
}
public void cancelBackgroundOperation()
{
if (_cancelSource != null)
{
_cancelSource.Cancel();
}
}
public async Task Start()
{
activateProgressIndicator();
_task = Task.Run(() => currentFunctionToExecute(this));
await _task;
_task = null;
deactivateProgressIndicator();
}
void activateProgressIndicator()
{
// In theory, you should not need to use Dispatcher here with async/await.
// But without a complete code example, it's impossible for me to
// say for sure, so I've left it as-is.
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var currentPage = App.RootFrame.Content as PhoneApplicationPage;
SystemTray.SetIsVisible(currentPage, true);
SystemTray.SetOpacity(currentPage, 0.5);
SystemTray.SetBackgroundColor(currentPage, Colors.White);
SystemTray.SetForegroundColor(currentPage, Colors.Black);
progressIndicator = new ProgressIndicator();
progressIndicator.IsVisible = true;
progressIndicator.IsIndeterminate = true;
progressIndicator.Text = message;
SystemTray.SetProgressIndicator(currentPage, progressIndicator);
});
}
void deactivateProgressIndicator()
{
// Likewise.
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (progressIndicator != null)
{
var currentPage = App.RootFrame.Content as PhoneApplicationPage;
progressIndicator.IsVisible = false;
SystemTray.SetIsVisible(currentPage, false);
}
});
}
public bool isBackgroundWorkerBusy()
{
return _task != null;
}
}
Then you could use it something like this:
private async Task loadReports()
{
bgWorker = new TestBackgroundWorker(loadReportsFromDB, true, "Loading...");
await bgWorker.Start();
showReports();
}
void loadReportsFromDB(TaskBackgroundWorker worker)
{
while (...)
{
if (worker.CancellationToken.IsCancellationRequested)
{
return; // or whatever
}
}
}
To deal with cancellation, the functionNameToExecute delegate would need to be for a method that accepts an instance of TaskBackgroundWorker as a parameter, so that it can retrieve the CancellationToken property value to check for cancellation (similar to the DoWork() event handler…though your code example didn't actually suggest any mechanism by which the actual background operation code would even detect cancellation).
Note that with async/await, your task can also return a value if you like, via the Task<T> type instead of Task. The above example could easily be modified to accommodate that, and that feature of async/await is one of the biggest reasons I prefer it over BackgroundWorker (which has no clean, compiler-supported mechanism for returning results from the background operation).
Caveat: Lacking a complete code example to start with, there is no point for me to try to actually compile and test any of the code. So the above is strictly "browser-authored". It should suffice for the purposes of illustration, but I apologize in advance for any typos that might exist.

How to Wake up a sleeping thread?

I made a thread at load event like below:
Thread checkAlert = null;
bool isStop = false;
private void frmMain_Load(object sender, EventArgs e)
{
checkAlert = new Thread(CheckAlert);
checkAlert.Start();
}
void CheckAlert()
{
while (!isStop)
{
Thread.Sleep(60000);
//do work here
}
}
Is there any way to resume the checkAlert thread during it's sleep period?( Thread.Sleep(60000);)
I tried using Thread.Interrupt() but it flows a ThreadInterruptedException, how should I handle this exception? or is there any way to resume the thread?
Edited:
I need to wake up the thread before the "sleep" end because when the user wants to quit the program, the program will have to wait for some time before it really quits ( checkAlert is still running) Is there any way to improve this case?
Based on your comments what it looks like is you need to re-design how CheckAlert works so it does not use Sleep's at all. What you should be doing is using a Timer instead.
System.Timers.Timer timer = null;
public FrmMain()
{
InitializeComponent();
timer = new System.Timers.Timer(60000);
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
//If you want OnTimedEvent to happen on the UI thread instead of a ThreadPool thread, uncomment the following line.
//timer.SynchronizingObject = this;
if(this.components == null)
this.components = new System.ComponentModel.Container();
//This makes it so when the form is disposed the timer will be disposed with it.
this.componets.Add(timer);
}
private void frmMain_Load(object sender, EventArgs e)
{
timer.Start();
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
//It is good practice not to do complicated logic in a event handler
// if we move the logic to its own method it is much easier to test (you are writing unit tests, right? ;) )
CheckAlert();
}
void CheckAlert()
{
//do work here
}
private void frmMain_Close(object sender, EventArgs e)
{
timer.Stop();
}
If you want the thread to exit automatically when your program quits, simply make it a background thread.
checkAlert = new Thread(CheckAlert);
checkAlert.IsBackground = true;
checkAlert.Start();
It looks to me like you're trying to create a thread which handles two types of events: do something and stop running.
Rather than using a shared variable (isStop) and some other technique to interrupt the thread in order to do work, you might want to use threading events (not to be confused high-level UI Event objects) to control your thread.
AutoResetEvent stop = new AutoResetEvent(false);
AutoResetEvent check = new AutoResetEvent(false);
private void CheckAlert() {
WaitHandle[] handles = new WaitHandle[] { stop, check };
for (;;) {
switch (AutoResetEvent.WaitAny(handles)) {
case 0:
return;
case 1:
// do work
break;
}
}
}
Calling check.Set() in your code will trigger the "do work" branch in the thread and stop.Set() will cause the thread to terminate gracefully.
Once your code has called stop.Set() to terminate the thread, it can call the thread's Join() method to wait until the thread terminates.
EDIT
I misunderstood the question. I will leave the code above in case anyone finds it useful.
If all you want to do is have a thread that performs a task once a minute and stop on demand, you can use the following code:
AutoResetEvent stop = new AutoResetEvent(false);
void CheckAlert() {
var time = new TimeSpan(0, 1, 0); // one minute
while (!stop.WaitOne(time)) {
// do work
}
}
private Thread checkThread;
private void frmMain_Load(object sender, EventArgs e) {
checkThread = new Thread(CheckAlert);
checkThread.Start();
}
private void frmMain_Close(object sender, EventArgs e) {
stop.Set(); // signal thread to stop
checkThread.Join(); // wait for thread to terminate
}
You can see an explanation on how to wake a sleeping thread here:
https://msdn.microsoft.com/en-us/library/tttdef8x%28v=vs.100%29.aspx
and this is a complete example (as you can see, Thread.Interrupt is the good choise... however you have to catch it to continue normal thread execution):
public class HVCSensor : HVCDevice, IDisposable
{
private Thread myThread;
private const int execute_timeout = ((10 + 10 + 6 + 3 + 15 + 15 + 1 + 1 + 15 + 10) * 1000);
private bool disposed = false;
private bool paused = false;
public delegate void HVCResultsHandler(HVC_RESULT res);
public event HVCResultsHandler HVCResultsArrived;
private void OnHVCResultsArrived(HVC_RESULT res)
{
if (HVCResultsArrived != null) {
HVCResultsArrived(res);
}
}
public HVCSensor() {
myThread = new Thread(new ThreadStart(this.execute));
}
private void execute(){
while (!disposed) {
if (!paused && this.IsConnected)
{
HVC_RESULT outRes;
byte status;
try
{
this.ExecuteEx(execute_timeout, activeDetections, imageAcquire, out outRes, out status);
OnHVCResultsArrived(outRes);
}
catch (Exception ex) {
}
}
else {
try
{
Thread.Sleep(1000);
}
catch (ThreadInterruptedException e)
{
}
}
}
}
public HVC_EXECUTION_IMAGE imageAcquire
{
get;
set;
}
public HVC_EXECUTION_FLAG activeDetections
{
get;
set;
}
public void startDetection() {
if(myThread.ThreadState==ThreadState.Unstarted)
myThread.Start();
}
public void pauseDetection() {
paused = true;
}
public void resumeDetection() {
paused = false;
if (myThread.ThreadState == ThreadState.WaitSleepJoin)
myThread.Interrupt();
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
disposed = true;
myThread.Interrupt();
}
}

Refresh Progressbar UI using Thread

I have a FTP proccess that run without UI. and have a winform that use this ftp control. in that window I have a progressbar that show the ftp upload progress. The progress arrives to the window via interfase that is updated on the underliying presenter (I'm using MVP pattern).
My problem is when try to update the progress, it allways throw me this exception.
Through threads illegal operation: control 'prgProgresoSubido' is accessed from a thread other than that in which you created it.
That problem persists even if I use a BackGroundWorker in the Form.
// This is a delegated on presenter when a File finish to upload
void client_FileUploadCompletedHandler(object sender, FileUploadCompletedEventArgs e)
{
string log = string.Format("{0} Upload from {1} to {2} is completed. Length: {3}. ",
DateTime.Now, e.LocalFile.FullName, e.ServerPath, e.LocalFile.Length);
archivosSubidos += 1;
_Publicacion.ProgresoSubida = (int)((archivosSubidos / archivosXSubir) * 100);
//this.lstLog.Items.Add(log);
//this.lstLog.SelectedIndex = this.lstLog.Items.Count - 1;
}
// This is My interfase
public interface IPublicacion
{
...
int ProgresoSubida { set; }
}
/// And Here is the implementartion of the interfase on the form
public partial class PublicarForm : Form ,IPublicacion
{
//Credenciales para conectarse al servicio FTP
public FTPClientManager client = null;
public XmlDocument conf = new XmlDocument();
public string workingDir = null;
public webTalk wt = new webTalk();
private readonly PublicacionesWebBL _Publicador;
public PublicarForm()
{
InitializeComponent();
String[] laPath = { System.AppDomain.CurrentDomain.BaseDirectory};
String lcPath = System.IO.Path.Combine(laPath);
_Publicador = new PublicacionesWebBL(this, lcPath);
}
public int ProgresoSubida
{
set
{
// This is my prograss bar, here it throw the exception.
prgProgresoSubido.Value = value;
}
}
}
How can I do to avoid this problem ?
In general, all updates to the User Interface and Controls has to be done from the main thread (event dispatcher). If you attempt to modify the properties of a control from a different thread you will get an exception.
You must call Control.Invoke to invoke on the event dispatcher the method that updates your UI
Control.Invoke
Here, place a button and a label on a form, then try this
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(TestThread));
t.Start();
}
private void TestThread()
{
for (int i = 0; i < 10; i++)
{
UpdateCounter(i);
Thread.Sleep(1000);
}
}
private void UpdateCounter(int i)
{
if (label1.InvokeRequired)
{
label1.Invoke(new ThreadStart(delegate { UpdateCounter(i); }));
}
else
{
label1.Text = i.ToString();
}
}
}
Realize, that if you are firing an event from a thread, that the event will be on the same Thread. Therefore, if that thread is not the event dispatcher, you'll need to invoke.
Also, there may be mechanisms that BackgroundWorker gives you (As the commentator said) that simplify this for you, but I've never used it before so I'll leave that up to you to investigate.
As Alan has just pointed out, you must do all operations with UI controls in UI thread.
Just modify your property like this:
public int ProgresoSubida
{
set
{
MethodInvoker invoker = delegate
{
prgProgresoSubido.Value = value;
}
if (this.InvokeRequired)
{
Invoke(invoker);
}
else
{
invoker();
}
}
}

Categories