I am using BackgroundWorker in scanning a document using C#.NET.
The problem is that while I call the BackgroundWorker.RunWorkerAsync() method, application doesn't respond. After finishing the do work it start responding again. As I know BackgroundWorker thread is used to work at background while user can do work in main UI thread... Please make me correct if i am wrong.
Here is the BackgroundWorker code.
BackgroundWorker backWorker = new BackgroundWorker();
backWorker.WorkerReportsProgress = true;
backWorker.DoWork += new DoWorkEventHandler(backWorker_DoWork);
backWorker.ProgressChanged += new ProgressChangedEventHandler(backWorker_ProgressChanged);
backWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backWorker_RunWorkerCompleted);
progressBar.Visible = true;
backWorker.RunWorkerAsync();
}
private void backWorker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
List
string currFilename;
// Create a scanner instance (the user can select if more than one)
ItemClass scanner = (ItemClass)wiaManager.Create(ref missing);
// Show the standard scanning dialog (this is not a required step...)
CollectionClass scans = scanner.GetItemsFromUI(
WiaFlag.SingleImage, WiaIntent.ImageTypeText)
as CollectionClass;
// If the user clicks Cancel, collection is NULL
if (scans != null && scans.Count > 0)
{
// Transfer any scanned pictures to disk
ItemClass scan;
foreach (object wiaObj in scans)
{
scan = (ItemClass)Marshal.CreateWrapperOfType(wiaObj, typeof(ItemClass));
// create temporary file for image
currFilename = Path.GetTempFileName();
// transfer picture to our temporary file
scan.Transfer(currFilename, false);
// Create a Bitmap from the loaded file (Image.FromFile locks the file...)
using (FileStream fs = new FileStream(currFilename, FileMode.Open, FileAccess.Read))
{
// KLUDGE: Must wrap the FromStream Image with a new Bitmap.
// Otherwise get OutOfMemoryException later when using ColorMatrix on it.
docs.Add(new Bitmap(Image.FromStream(fs)));
fs.Close();
}
// Don't leave junk behind!
File.Delete(currFilename);
if (docs.Count > 0)
OpenImage(docs[0]);
}
}
}
catch (Exception ex)
{
MessageBox.Show(string.Format("Sorry, image did not scan. Please try again.\n{0}", ex.Message), "Scanner error");
}
}
private void backWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
private void backWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar.Visible = false;
backWorker.ReportProgress(0);
}
The time where application is not responding to user is while executing the code that I have made bold
// transfer picture to our temporary file
scan.Transfer(currFilename, false);
The output is always ok. I just need to make application respond while scanning document at background.
Please help me if you can.
Thanks in advance.
Khalid MushtaqPosted Sep 22, 2010, 4:19 AM
Khalid MushtaqPosted Sep 22, 2010, 3:59 AM
Thanks.
Sam HobbsPosted Sep 21, 2010, 10:17 PM
JurePosted Sep 21, 2010, 1:13 PM
I myself use the timer specifically because of control over the frequency of updates.
I hope this helps.
Khalid MushtaqPosted Sep 21, 2010, 11:57 AM
The problem is that if I don't use the progress bar. the situation is same. I am surprised why backgroundworker hangs the GUI for almost 10 seconds at the specific command i.e. scan.Transfer(currFilename, false).
Thanks again.
JurePosted Sep 21, 2010, 10:53 AM
ThreadPool.QueueUserWorkItem(new WaitCallback(yourFunction), ""); // "" goes for the object, the object can't be null
The function:
private void yourFunction(object o)
{
count_prog = 0;
count_all = // count all items to process
this.BeginInvoke(new WaitCallback((object o) => this.timer.Start()));
// do your stuff, increment count_prog after each item
this.BeginInvoke(new WaitCallback((object o) => this.timer.Stop()));
}
Set the timer to interval of ~100ms, but don't go too low.
Also, you can set the progress bar's "boost animation" to be more responsive by writing:
this.progBar.MarqueeAnimationSpeed = 50; //default is 100
Now for the timer:
this.timer.Tick += new System.EventHandler((object o, EventArgs e) =>
{
this.progBar.Value = (int)((float)count_prog / count_all * 100);
});
If you need to update anything else GUI-wise from the ThreadPool thread, use BeginInvoke, such as:
this.BeginInvoke(new WaitCallback((object o) =>
{
this.statusLabel.Text = (string)o;
}), status); //status is the object to pass
EDIT: Actually, if you use lambda expression for BeginInvoke method, you don't have to pass anything, because all the stuff that's in scope in the function is also in scope inside the lambda block of code.
You have to use BeginInvoke because only the main thread (STAThread) has control over GUI.