BackgroundWorker questions on cancellation - c#

I'm trying (yet again) to implement a Backgroundworker in my app so that the UI will be responsive. The user selects files to be processed which end up on a grid. When the processing starts it's like this:
for (int i = 0; i<Grid.Rows.Count; i++)
{
Worker work = new Worker();
//set up data for processing based on the current row in the grid
bw.RunWorkerAsync(work);
addStatusToGrid();
//clean up; indicate work on this file is done
work=null;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
Worker work = (Worker)e.Argument;
if (work.doTheWork() == false)
{
//indicate failure
}
else
{
//indicate success
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//not sure what to do here
}
BUT what happens is that as soon as bw.RunWorkerAsync( ) is called the app immediately goes to the next line in the for loop and then starts the same process again. Now, I sort of want this but I get an error message, "This backgroundworker is currently busy and cannot run multiple tasks concurrently". If I just process one item, addStatusToGrid() gets called immediately and of course there is no status to add as the work is not done.
SEVERAL QUESTIONS: If I can use this mechanism to initiate several processing sessions at once, that would be a good thing. But how do I prevent addStatusToGrid() getting called immediately?
Right now, the UI does get updated a lot. I have a progress bar and it often shows updates. I cannot cancel the operations with a cancel button I guess because the UI thread is busy.

From your question it appears that you have multiple rows in the grid that need to be processed. Now the background worker executes the method in another thread thus freeing up the UI to perform additional actions. (IE status updates). As this is executed on another thread the UI is free to continue processing as normal. The UI thread will not wait for the BackgroundWorker to finish. If it did wait, there would be no use using the background worker. Additionally the BackgroundWorker must finish the operation before starting another operation (thus your error message).
Now it is hard to understand what you are doing in the work.doTheWork() but I suspect you would want to take another approach here.
Before we begin tackling the question the first thing you must understand is the Cancellation of a background worker doesn't actually cancel the thread but provides a "flag" for your code to check if the BackgroundWorker is pending calculation (MSDN on this flag ).
Moving forward. As the Grid is held on the UI thread you need to understand the data from the grid that we want to capture and pass it into the BackgroundWorker context. As this inst provided I am going to do a very basic example. Now you could run multiple background workers to process each row individually thus spawning 1 worker per row in the Grid. For your requirement this may be ideal but for larger grids you are essentially creating 1 thread per row to process and in a grid with hundreds of rows this could be disastrous.
So to being you can create a method something like below. I have commented the code to help you run through it. Bascially this shows the ability to cancel the worker, report progress and iterate through each row item individually. I also added some basic classes for use inside the worker. [Note this is demo code only]
class DoWorkTester
{
int currentIndex = 0;
GridRow[] rows;
public void ExecuteWorkers()
{
GridRow rowA = new GridRow
{
PropertyA = "abc",
PropertyB = "def"
};
GridRow rowB = new GridRow
{
PropertyA = "123",
PropertyB = "456"
};
GridRow rowC = new GridRow
{
PropertyA = "xyz",
PropertyB = "789"
};
rows = new GridRow[] { rowA, rowB, rowC };
currentIndex = 0;
runWorkers();
}
BackgroundWorker worker;
void runWorkers()
{
//done all rows
if (currentIndex >= rows.Length - 1)
return;
//is the worker busy?
if (worker != null && worker.IsBusy)
{
//TODO: Trying to execute on a running worker.
return;
}
//create a new worker
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (o, e) =>
{
//TODO: Update the UI that the progress has changed
};
worker.DoWork += (o, e) =>
{
if (currentIndex >= rows.Length - 1)
{
//indicate done
e.Result = new WorkResult
{
Message = "",
Status = WorkerResultStatus.DONE
};
return;
}
//check if the worker is cancelling
else if (worker.CancellationPending)
{
e.Result = WorkResult.Cancelled;
return;
}
currentIndex++;
//report the progress to the UI thread.
worker.ReportProgress(currentIndex);
//TODO: Execute your logic.
if (worker.CancellationPending)
{
e.Result = WorkResult.Cancelled;
return;
}
e.Result = WorkResult.Completed;
};
worker.RunWorkerCompleted += (o, e) =>
{
var result = e.Result as WorkResult;
if (result == null || result.Status != WorkerResultStatus.DONE)
{
//TODO: Code for cancelled \ failed results
worker.Dispose();
worker = null;
return;
}
//Re-call the run workers thread
runWorkers();
};
worker.RunWorkerAsync(rows[currentIndex]);
}
/// <summary>
/// cancel the worker
/// </summary>
void cancelWorker()
{
//is the worker set to an instance and is it busy?
if (worker != null && worker.IsBusy)
worker.CancelAsync();
}
}
enum WorkerResultStatus
{
DONE,
CANCELLED,
FAILED
}
class WorkResult
{
public string Message { get; set; }
public WorkerResultStatus Status { get; set; }
public static WorkResult Completed
{
get
{
return new WorkResult
{
Status = WorkerResultStatus.DONE,
Message = ""
};
}
}
public static WorkResult Cancelled
{
get
{
return new WorkResult
{
Message = "Cancelled by user",
Status = WorkerResultStatus.CANCELLED
};
}
}
}
class GridRow
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
}
Now if you wanted to process multiple rows at a time you will have to adapt the code to use some sort of Worker Pooling or pass all the row data to the first background worker thus removing the recursion.
Cheers.

Related

Main process gets frozen after starting another thread

I am trying to write an application which transfers data between 2 systems. This application is used by a user, so it is WinForm application. When data transfering is started by a click of the user, the GUI gets frozen even though I start the data transfering in another thread. I am doing something wrong but I couldnt figure it out. here is my SIMPLIFIED code below....
What am I doing wrong?
// Button Click Event
private void btnStart_Click(object sender, EventArgs e)
{
StartThread();
}
// This starts the threaad.
public static void StartThread()
{
string msg = string.Empty;
int i = 0;
continue_ = true;
if (list != null)
{
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
}
}
// This is a simplified code.
public static void Test()
{
string msg = string.Empty;
int i = 0;
continue_ = true;
while (continue_)
{
i++;
Thread.Sleep(5000);
FormMain.dal.ExecuteQuery("INSERT INTO A_TEST VALUES('"+i+"')",null,CommandType.Text,out msg);
}
}
Your StartThread() method includes a Thread.Sleep(5000) ... this is happening in your button click method, thus is making the UI thread sleep. Also, it looks like you have an infinite loop on the UI thread as continue_ never gets set to false
I'm guessing what you're trying to achieve here, but this may help:
public static void StartThread()
{
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
Let's have a look at this block in StartThread:
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
You have a while loop dependen on continue_, but you never change it to false. So you get first of all an infinite loop, which causes the GUI to freeze.
why you are modifying i, but never using it, so just remove it.
You don't need also Thread.Sleep(5000);. However, if you really want to wait a time period, you can use an async delay. It will give the GUI free, so that the GUI works until the delay is finished. But for this, you have to declare StartThread as async.
In your:
if (list != null)
{
while (continue_)
{
i++;
Thread.Sleep(5000);
Thread thrd1 = new System.Threading.Thread(() => Test());
thrd1.Start();
}
}
You use Thread.Sleep(5000);
This however still targets your main thread.
I would suggest you to remove this line.
Also, why do you use the variable 'i' while you never use it?

Update ObservableCollection in list box in thread

Hy,
I have a Observable Collection which is bind with a list box. I add logs to the Observable Collection. I always add the message immediately to the Observable Collecten. But the list gets only updated when the loop is finished but I want to Update it when I add one item in the for loop. This is why I use a Thread but I have a few problems.
I have a thread safe ObservableCollection:
class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler collectionChanged = this.CollectionChanged;
if (collectionChanged != null)
foreach (NotifyCollectionChangedEventHandler handler in collectionChanged.GetInvocationList())
{
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
if (dispatcherObject != null)
{
Dispatcher dispatcher = dispatcherObject.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => handler.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
handler.Invoke(this, e);
}
}
}
This is my test class:
public partial class MainWindow : Window
{
ThreadSafeObservableCollection<Animal> list = new ThreadSafeObservableCollection<Animal>();
public MainWindow()
{
InitializeComponent();
list.Add(new Animal() { Name = "test1" });
list.Add(new Animal() { Name = "test2" });
this.DataContext = list;
}
private void dsofsdkfd(object sender, RoutedEventArgs e)
{
//Version 1
Task.Factory.StartNew(() => test());
//Version2
/*
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var token = Task.Factory.CancellationToken;
Task.Factory.StartNew(() => test(), token, TaskCreationOptions.None, uiScheduler);
*/
}
public void test()
{
for (int i = 0; i < 10000; i++)
{
list.Add(new Animal() { Name = "test" + i });
System.Threading.Thread.Sleep(1);
}
}
}
See the private void dsofsdkfd(object sender, RoutedEventArgs e) function to the comment Version1.
In the beginning it works so the list updates everytime I add a item. After a few entries I get an exception:
"Information for developers (use Text Visualizer to read
this):\r\nThis exception was thrown because the generator for control
'System.Windows.Controls.ListBox Items.Count:1089' with name 'Logger'
has received sequence of CollectionChanged events that do not agree
with the current state of the Items collection. The following
differences were detected:\r\n Accumulated count 994 is different
from actual count 1089. [Accumulated count is (Count at last Reset +
Adds - #Removes since last Reset).]\r\n\r\nOne or more of the following sources may have raised the wrong events:\r\n
System.Windows.Controls.ItemContainerGenerator\r\n
System.Windows.Controls.ItemCollection\r\n
System.Windows.Data.ListCollectionView\r\n *
WpfApplication1.ThreadSafeObservableCollection`1[[WpfApplication1.Animal,
WpfApplication1, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]\r\n(The starred sources are considered more
likely to be the cause of the problem.)\r\n\r\nThe most common causes
are (a) changing the collection or its Count without raising a
corresponding event, and (b) raising an event with an incorrect index
or item parameter.\r\n\r\nThe exception's stack trace describes how
the inconsistencies were detected, not how they occurred. To get a
more timely exception, set the attached property
'PresentationTraceSources.TraceLevel' on the generator to value 'High'
and rerun the scenario. One way to do this is to run a command
similar to the following:\n
System.Diagnostics.PresentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator,
System.Diagnostics.PresentationTraceLevel.High)\r\nfrom the Immediate
window. This causes the detection logic to run after every
CollectionChanged event, so it will slow down the application.\r\n"
See private void dsofsdkfd(object sender, RoutedEventArgs e) function to the comment Version2.
I also tried it with the TaskScheduler using FromCurrentSynchronizationContext.
Then it throws no exception but I have the same problem like at the beginning, so the list box refreshes only if the for each loop is finished.
How I can accomplish that the list box updates when I add an element?
Best regards
I wouldn't roll my own ObservableCollection for this. I'd just perform the .Add call on the UI thread.
public void test()
{
for (var i = 0; i < 10000; i++)
{
// create object
var animal = new Animal {Name = "test" + i};
// invoke list.Add on the UI thread
this.Dispatcher.Invoke(new Action(() => list.Add(animal)));
// sleep
System.Threading.Thread.Sleep(1);
}
}
Note that since you're in a Window subclass, this.Dispatcher will correspond to the dispatcher for the UI thread. If you move this logic to, say, a model or view model class, you'll need to explicitly capture the value of Dispatcher.Current on the UI thread, and pass that dispatcher manually to the background thread.
EDIT: OP asked for more information on using the Dispatcher outside of a FrameworkElement class. Here's how you would do that. The dispatcher for the UI thread is acquired on the UI thread by calling Dispatcher.CurrentDispatcher. That dispatcher is then passed directly into the background thread procedure.
public class MainWindowViewModel
{
// this should be called on the UI thread
public void Start()
{
// get the dispatcher for the UI thread
var uiDispatcher = Dispatcher.CurrentDispatcher;
// start the background thread and pass it the UI thread dispatcher
Task.Factory.StartNew(() => BackgroundThreadProc(uiDispatcher));
}
// this is called on the background thread
public void BackgroundThreadProc(Dispatcher uiDispatcher)
{
for (var i = 0; i < 10000; i++)
{
// create object
var animal = new Animal { Name = "test" + i };
// invoke list.Add on the UI thread
uiDispatcher.Invoke(new Action(() => list.Add(animal)));
// sleep
System.Threading.Thread.Sleep(1);
}
}
}
You need to maintain current dispatcher thread for the same. You must update collection in current dispatcher thread only. One way to do it is to use BiginInvoke() method of dispatcher class.
Save current dispatcher in a variable in constructor and then use it when needed.
_currentDispatcher = Application.Current.Dispatcher;
For example: We have a scenario where we popup an error window if we have an error. We need to close an Error window if error count is zero. Now if we are handling events and message in another thread (not on UI thread) then we need to save the UI thread dispatcher object and need to use it to update collection or any other action. Here I am closing Error Window. (I don't have solution ready for updating collection.)
if (ErrorNotifications.Count == 0)
_currentDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<ErrorNotificationWindow>(CloseErrorNotificationWindow), _errWindow);
Here CloseErrorNotificationWindow is method with parameter _errWindow.
private void CloseErrorNotificationWindow(ErrorNotificationWindow _errWindow)
{
if (_errWindow == null)
return;
if (_errWindow.IsActive)
_errWindow.Close();
}
In CloseErrorNotificationWindow() method you can update your collections and it should not give any exception as you would be using main UI thread to do it.
Hope this will helpful.

In C#, how do you lock a form?

I'm trying to lock the main form while a please wait box is shown on the screen, but it won't work. Here's my dilemma.
I have 2 forms. The main form that the user clicks a refresh button to load the list of SQL Servers, and a Please wait form that shows while it's loading the list. The SQL Server thread is a separate thread by default while using C# and it locks out the main thread in order to process the SQL request.
I can add a background worker, but then I can't update my combo box to show the list as its a UI control. If I use a handler for that, my show_dialog() for the please wait box will stop locking down the main form.
How is it even possible to lock this form down without the left click queue being run after the main thread goes active again? I added the code that needs to be executed while the user waits.
public void PullServers()
{
bool ServersFound = false;
foreach (string Value in SQL.LocateSqlInstances())
{
this.cmbServer.Items.Add(Value);
ServersFound = true;
}
if (!ServersFound)
{
this.cmbServer.Items.Add(Strings.Lang("ddServerNoneFound"));
this.cmbServer.SelectedIndex = 0;
}
else
{
if (!s.empty(General.setting("SQLSERVER")))
{
this.cmbServer.Text = General.setting("SQLSERVER");
}
else
{
this.cmbServer.SelectedIndex = 0;
}
}
this.picRefreshServers.Image = Properties.Resources.Refresh;
}
public static Array LocateSqlInstances()
{
using (DataTable sqlSources = System.Data.Sql.SqlDataSourceEnumerator.Instance.GetDataSources())
{
string Servers = null;
foreach (DataRow source in sqlSources.Rows)
{
string instanceName = source["InstanceName"].ToString();
if (!s.empty(instanceName))
{
Servers += source["ServerName"].ToString() + "\\" + instanceName + "[[SERVBREAK]]";
}
}
string[] ServersList = Servers.Split(new string[] { "[[SERVBREAK]]" }, StringSplitOptions.RemoveEmptyEntries);
return ServersList;
}
}
I think you are on the right track with a BackgroundWorker. I have found the following pattern to work well for me.
In your main form, you need to perform the following steps.
Create a BackgroundWorker to perform the long running operation.
Start the BackgroundWorker.
Display the waiting form as a modal dialog.
// Step 1:
BackgroundWorker bg = new BackgroundWorker()
bg.DoWork += new DoWorkEventHandler(bg_DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
// Step 2:
bg.RunWorkerAsync();
// Step 3:
waitingForm = new WaitingForm();
waitingForm.ShowDialog();
As you know, you can't update the UI from the bg_DoWork handler since it does not run on the UI thread. So just get the data you need here and pass it on to the the bg_RunWorkerCompleted handler using the e.Result parameter.
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
Array servers = SQL.LocateSqlInstances();
e.Result = servers;
}
The bg_RunWorkerCompleted runs on the UI thread so it is safe to update your controls here. This is where you should close the waiting form and then update your UI.
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Close the Waiting form.
waitingForm.Close();
// Retrieve the result of bg_DoWork().
Array servers = e.Result as Array;
bool ServersFound = false;
foreach (string Value in servers)
{
this.cmbServer.Items.Add(Value);
ServersFound = true;
}
if (!ServersFound)
{
this.cmbServer.Items.Add(Strings.Lang("ddServerNoneFound"));
this.cmbServer.SelectedIndex = 0;
}
else
{
if (!s.empty(General.setting("SQLSERVER")))
{
this.cmbServer.Text = General.setting("SQLSERVER");
}
else
{
this.cmbServer.SelectedIndex = 0;
}
}
this.picRefreshServers.Image = Properties.Resources.Refresh;
}

How to display a loop's progress without pausing the loop?

I have a for loop (running in its own thread) in which I'm calculating the loop's progress and I want to display the progress value every time it changes, but I want to run the message display command outside the loop, so it doesn't pause the loop.
I have read How do I display progress during a busy loop?, but I don't want to use a background worker because I already have one that uses the instance of the class that starts the loop (i.e. I do not want to nest background workers). I am assuming that the alternative would be raising and listening to events, but I am not sure how to implement that in this case.
So, how can I solve this problem without the use of a background worker?
If it's Winforms, you can just do a MyForm.BeginInvoke() with an anonymous delegate that updates the display of the progress. BeginInvoke is asynchronous so it won't block the current thread.
You need to notify something like a LoopWatcher out of the loop thread.
public class ProgressEventArgs : EventArgs
{
public int Number { get; private set; }
public ProgressEventArgs(int num)
{
this.Number = num;
}
}
public class Worker
{
public event EventHandler<ProgressEventArgs> ProgressChanged = delegate { };
public void DoSomething()
{
for (int i = 0; i < 10; i++)
{
ProgressChanged(this, new ProgressEventArgs(i));
Thread.Sleep(1000); //just an example here
}
}
}
You can see in Worker.DoSomething, there is a loop which costs time. I add an event in Worker, so outside the class, the subscriber can know the progress is changed.
var worker = new Worker();
worker.ProgressChanged += (s, e) => Console.WriteLine(e.Number);
Thread t = new Thread(worker.DoSomething);
t.Start();
Since the provided answers didn't meet my requirements, I did some research on events and solved the issue the way I initially wanted.
I declared an event in the class that is starting the loop:
public delegate void ProgressChangedEvHandler(int progress);
public event ProgressChangedEvHandler ProgressChanged;
private void OnProgressChanged(int progress)
{
var handler = ProgressChanged;
if (handler != null) handler(progress);
}
Then I invoked the event from within the loop:
for (var index = 0; index < arrayListCount; index++)
{
var progress = (int) (100*(double) index/(double) arrayListCount);
OnProgressChanged(progress);
}
and then I just created the listener (LoopClassInstance.ProgressChanged += LoopClassInstance_ProgressChanged;) in a different class (on a different thread):
private void LoopClassInstance_ProgressChanged(int progress)
{
toolStripProgressBar1.Value = progress;
}

My Custom progressBar Form Hangs when i show it

I made a form that plays a progressbar role here's the code i made
public partial class PXProgressBar : Form
{
public delegate bool CancelEvent();
public event CancelEvent cancel_e;
public Boolean ProcessCancelled
{
get;
set;
}
public PXProgressBar(bool EnableCancel)
{
InitializeComponent();
ProcessCancelled = false;
progressBar1.Minimum = 0;
if (!EnableCancel)
Cancelbtn.Visible = false;
}
public void increament(int step)
{
if (progressBar1.Value < progressBar1.Maximum-1)
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
progressBar1.Refresh();
}
else
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
if (this.TopMost)
this.TopMost = false;
this.Update();
this.Hide();
this.WindowState = FormWindowState.Minimized;
// this.Dispose();
}
}
public void SetMaximum(int MaximumValue)
{
if (MaximumValue <= 0)
{
progressBar1.Maximum = 0;
return;
}
if (progressBar1.Minimum != 0 && MaximumValue < progressBar1.Minimum)
{
progressBar1.Maximum = progressBar1.Minimum;
return;
}
progressBar1.Maximum = MaximumValue;
}
public void SetMinimum(int MinimumValue)
{
progressBar1.Value = 0;
if (MinimumValue <= 0)
{
progressBar1.Minimum = 0;
return;
}
if (progressBar1.Maximum != 100 && MinimumValue > progressBar1.Maximum)
{
progressBar1.Minimum = progressBar1.Maximum;
return;
}
progressBar1.Minimum= MinimumValue;
}
public void SetTitle(string ProcessTitle)
{
this.ProgressTitlelb.Text =ProcessTitle;// ProcessTitle;
//this.ProgressTitlelb.Left = (this.panel1.Width - this.ProgressTitlelb.Width) / 2;
//this.ProgressTitlelb.Top = (this.panel1.Height - this.ProgressTitlelb.Height) / 2;
this.Update();
}
private void Cancelbtn_Click(object sender, EventArgs e)
{
ProcessCancelled = true;
bool disposeRequired =cancel_e();
if(disposeRequired)
this.Dispose();
}
private void PXProgressBar_Shown(object sender, EventArgs e)
{
this.Update();
}
}
and i call the form through this code
if (ProgressBar == null)
ProgressBar = new PXProgressBar(true);
ProgressBar.SetTitle("Saving ...");
ProgressBar.SetMinimum(0);
ProgressBar.SetMaximum(100);
ProgressBar.TopMost = true;
ProgressBar.Show();
Application.DoEvents();
regarding that the past few lines are in a unction that is called throught a thread
but when i run it the form hangs so i cant set a Cancel Button in the form to let the user cancel the operation
Your code looks like it should be fine so I can only assume that you are doing a long running operation on the UI thread which would cause the UI to look like its hung. You need to perform long running operations on a background thread so that the UI thread remains responsive enough to respond to button clicks etc. There are many many articles about this if you consult your friend Google.
More info on that here http://www.idevforfun.com/index.php/2010/01/10/windows-ui-threading/
I agree with Skizz on the DoEvents call ... there's only a very few rare cases where that call is needed and mostly its in the framework itself that it gets used.
You need to make sure the GUI elements are created on the main form thread and not from a separate thread. So, you need to get you thread that is doing the work to get the main form thread to display and update the progress bar. This is going to take a bit of refactoring.
So, in your worker thread:
void DoWork () // of whatever it's called
{
main_form.CreateProgressBar ();
while (doing stuff)
{
main_form.IncrementProgressBar ();
do stuff
}
main_form.DestroyProgressBar ();
}
And in the main form:
delegate void Callback ();
void CreateProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (CreateProgressBar));
}
else
{
progress_bar = CreateProgressBar ();
}
}
void IncrementProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (IncrementProgressBar ));
}
else
{
progress_bar.IncrementProgressBar ();
}
}
void DestroyProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (DestroyProgressBar));
}
else
{
progress_bar.Close ();
progress_bar = null;
}
}
The InvokeRequired determines if the calling thread is the same as the GUI thread. If the calling thread is not the GUI thread, the Invoke is used to changed thread context. This is the synchronous version and won't complete until the invoked method is finished. There is an asynchronous version called BeginInvoke but this isn't really needed for what your doing.
The problem might be the DoEvents method call. From this MSDN page:
Calling this method causes the current thread to be suspended while
all waiting window messages are processed. If a message causes an
event to be triggered, then other areas of your application code may
execute. This can cause your application to exhibit unexpected
behaviors that are difficult to debug. If you perform operations or
computations that take a long time, it is often preferable to perform
those operations on a new thread. For more information about
asynchronous programming, see Asynchronous Programming Overview.
I don't think the DoEvents call is necessary. If you need to halt the code after the Show for the operation to complete, then use a System.Threading.EventWaitHandle instead.
There's some link maybe helpful for you about progressbar:
How do I implement a progress bar in C#?
Hope this help.
Just create other thread for progressbar and use it in background

Categories

Resources