Currently I am working on a project which need to consume large amount data from web services,
There is service class sending input date to the server and get back the result,
due to it time consuming process, it is required to user combined progressbar and backgroundworker to show user the process percentage. I have browsing quite a few sample code on this topic, but still could not figure out the best way to do it. Would you please help,
My code is following,
private MyCollection[] callWebService(string[] Inputs, string method)
{
List<string> results = new List<string>();
string fiel dNames = ""; // todo - fix this if nothing left in loop
int sizeOfArray = 500;
for (int i = 0; i < Inputs.Length; i = i + sizeOfArray)
{
string[] outputRecords;
int errorCode;
string errorString;
string[] thisFiveHundred = createSubArray(Inputs, i, sizeOfArray);
iq.NameValuePair[] namevaluepairs = new iq.NameValuePair[0];
fieldNames = iqOfficeWebservice.BatchStan(method, thisFiveHundred, null, "", out outputRecords, out errorCode, out errorString);
results.AddRange(outputRecords);
}
results.ToArray();
IAddress[] formattedResults = convertStringsToInputs(fieldNames, results);
return formattedResults;
}
private void cmdButton_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 101; i++)
{
worker.ReportProgress(i);
System.Threading.Thread.Sleep(1000);
}
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lblProgress.Text = ("Progress: " + e.ProgressPercentage.ToString() + "%");
}
Additional info can be found here.
Apart from the technical implementation in WPF or Winforms for example there is an essential aspect to consider.
Do you get feedback of the progress from the web services?
If not, you are likely not able to predict correctly the time the web service will need. This will depend on factors you cannot influence (like server load, network traffic, ...). In this case a progress bar is not recommended as it will give the user a weird experience.
Then you have options like displaying an text information that the request could take minutes and display a progress with an IsIndeterminate flag set (WPF) to show continuous progress feedback. A sandhour cursor is not an option as you are using a background thread.
An alternative is to break down the big request into smaller parts for which you can report progress.
I've just put together a variation of Adil's answer that encapsulates the work and updates, as well as properly detaching events and disposing of the worker. Please upvote Adil's answer if you upvote mine.
private void cmdButton_Click(object sender, EventArgs e)
{
var worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
DoWorkEventHandler doWork = (dws, dwe) =>
{
for (int i = 0; i < 101; i++)
{
worker.ReportProgress(i);
System.Threading.Thread.Sleep(100);
}
};
ProgressChangedEventHandler progressChanged = (pcs, pce) =>
{
lblProgress.Text = String.Format("Progress: {0}%", pce.ProgressPercentage);
};
RunWorkerCompletedEventHandler runWorkerCompleted = null;
runWorkerCompleted = (rwcs, rwce) =>
{
worker.DoWork -= doWork;
worker.ProgressChanged -= progressChanged;
worker.RunWorkerCompleted -= runWorkerCompleted;
worker.Dispose();
lblProgress.Text = "Done.";
};
worker.DoWork += doWork;
worker.ProgressChanged += progressChanged;
worker.RunWorkerCompleted += runWorkerCompleted;
worker.RunWorkerAsync();
}
Related
I have a button .It will open a file and process a file .I want to show the progress bar while processing the file .
when i am doing .its working
public MainframeDataExchangeTool()
{
InitializeComponent();
_ProgressBar.Style = ProgressBarStyle.Marquee;
_ProgressBar.Visible = false;
_Random = new Random();
InitializeBackgroundWorker();
}
private void InitializeBackgroundWorker()
{
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.DoWork += (sender, e) => ((MethodInvoker)e.Argument).Invoke();
_BackgroundWorker.ProgressChanged += (sender, e) =>
{
_ProgressBar.Style = ProgressBarStyle.Continuous;
_ProgressBar.Value = e.ProgressPercentage;
};
_BackgroundWorker.RunWorkerCompleted += (sender, e) =>
{
if (_ProgressBar.Style == ProgressBarStyle.Marquee)
{
_ProgressBar.Visible = false;
}
};
}
In my button click i am doing
private void btnOpenScriptFile_Click(object sender, EventArgs e)
{
try
{
loadScriptFlDlg.Filter = Constants.SCRIPT_FILE_FILTER;
loadScriptFlDlg.FilterIndex = 3;
loadScriptFlDlg.RestoreDirectory = true;
loadScriptFlDlg.FileName = string.Empty;
DialogResult objDialogResult = loadScriptFlDlg.ShowDialog();
if (objDialogResult.Equals(DialogResult.OK))
{
_BackgroundWorker.RunWorkerAsync(new MethodInvoker(() =>
{
_ProgressBar.BeginInvoke(new MethodInvoker(() => _ProgressBar.Visible = true));
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
_BackgroundWorker.ReportProgress(i);
}
}));
EnableDisableControls("OpenScript");
string strScriptError = LoadScriptFromFile(loadScriptFlDlg.FileName);///loading will taking time but progress bar not showing
Basically progress bar is showing at the end of data load but not while loading the data
You cannot see the progress as UI thread cannot update UI because it is busy loading your file. You must call LoadScriptFromFile from the background worker and keep UI thread free to process events and update UI.
I used this in my project with 2 methode (exemple):
1* Invoke(new Action(() => _ProgressBar.Visible = true));
2* or use Application.DoEvents() after your _BackgroundWorker.ReportProgress(i);
Surprised the compiler didn't throw a hissy fit...because you need an extra }.
You open 4 levels of nesting, but only close 3.
I have modified your code as shown below:
private void btnOpenScriptFile_Click(object sender, EventArgs e)
{
try
{
if (objDialogResult.Equals(DialogResult.OK))
{
_ProgressBar.Style = ProgressBarStyle.Marquee;
_ProgressBar.BeginInvoke(new MethodInvoker(() => _ProgressBar.Visible = true));
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
_ProgressBar.BeginInvoke(new Action(() => _ProgressBar.Value = i));
//Process my file here
}
}
}
Catch
{
}
}
I would always suggest reducing the number of empty lines, for readability.
I also find that the 'allman' style bracketing is easiest to debug.
But as always, each to his own.
EDIT:
After OP editted code:
Try adding:
application.doevents()
after your:
_BackgroundWorker.ReportProgress(i);
Why? The code will stay in the for loop until completes. By doing 'doevents' you are telling it to update externally, before the next loop.
Without the 'doevents' i guess you would see 0 and 100 only.
Simply do something like this with a BackgroundWorker (bgw).
private void MyMethod()
{
bgw.RunWorkerAsync(); //this calls the DoWork event
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
//Expensive task
//Calculate how far you through your task (ie has read X of Y bytes of file)
bgw.ReportProgress(myInteger);
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
}
Make sure you set the BackgroundWorker's "WorkerReportsProgress" property to True!
I have a simple app which fires of a series of data intensive tasks. I'm not very experienced with WinForms and I was wondering the best way to do this without locking the interface. Can backgroundWorker be re-used, or is there another way to do this?
Thanks
BackgroundWorker is a thread that also includes notification synchronization. For example, if you wanted to update your UI when the scan completes, a regular Thread cannot access the UI objects (only the UI thread can do that); so, BackgroundWorker provides a Completed event handler that runs on the UI thread when the operation completes.
for more info see: Walkthrough: Multithreading with the BackgroundWorker Component (MSDN)
and a simple sample code:
var worker = new System.ComponentModel.BackgroundWorker();
worker.DoWork += (sender,e) => Thread.Sleep(60000);
worker.RunWorkerCompleted += (sender,e) => MessageBox.Show("Hello there!");
worker.RunWorkerAsync();
backgroundWorker can be used.
its benefit - it allows you to update a progress bar and interact with UI controls. (WorkerReportsProgress)
Also it has a cancellation mechanism. (WorkerSupportsCancellation)
You can use BackgroundWorker for such requirements. Below is a sample which updates a label status based on percentage task [long running] completion. Also, there is a sample business class which sets some value and the value is set back to UI via ProgressChanged handler. DoWork is the place where you write your long running task logic. Copy-Paste the code below after adding a label and backgroundworker component on a Winforms app & give it a shot. You may debug across various handler [RunWorkerCompleted, ProgressChanged, DoWork] and have a look at InitWorker method. Notice the cancellation feature too.
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
BusinessClass _biz = new BusinessClass();
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
int junk = 0;
for (i = 0; i <= 199990000; i++)
{
int result = _biz.MyFunction(junk);
junk++;
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// note I can pass the business class result also and display the same in the LABEL
_worker.ReportProgress(percentComplete, result);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
}
else if (e.Error != null)
{
// Do something with the error
}
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
}
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
}
The background worker would be a good choice to start with
For more info look here
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
i have a code which is like this :
private void testToolStripMenuItem_Click(object sender, EventArgs e)
{
toolStripStatusLabel1.Text = " Device Testing...";
positive = false;
clearsensors_gui();
datarec = false;
cmd = 04;
datarec = serialport_FT(0, 1);
if (datarec)
{
char ab = Convert.ToChar(rec_data[1]);
//MessageBox.Show("\n" + ab + "\n");
int cab = Convert.ToInt16(ab);
int cabc1 = cab & 1;
int cabc2 = cab & 2;
int cabc3 = cab & 4;
int cabc4 = cab & 8;
int cabc5 = cab & 16;
int cabc6 = cab & 32;
if (cabc1 == 1)
ovalShape1.FillColor = Color.Green;
else
ovalShape1.FillColor = Color.Red;
if (cabc2 == 2)
ovalShape2.FillColor = Color.Green;
else
ovalShape2.FillColor = Color.Red;
if (cabc3 == 4)
ovalShape3.FillColor = Color.Green;
else
ovalShape3.FillColor = Color.Red;
if (cabc4 == 8)
ovalShape4.FillColor = Color.Green;
else
ovalShape4.FillColor = Color.Red;
if (cabc5 == 16)
ovalShape5.FillColor = Color.Green;
else
ovalShape5.FillColor = Color.Red;
if (cabc6 == 32)
ovalShape6.FillColor = Color.Green;
else
ovalShape6.FillColor = Color.Red;
toolStripStatusLabel1.Text = " Device Tested";
}
else
{
toolStripStatusLabel1.Text = "Try Again or Communication With Device Failure....";
}
}
the above code is to read a the sensors i.e datarec = serialport_FT(0, 1); function provides me a sensor output at the GUI side which'll be later depicted with red\green ovalShapeX(1-6)
Question: datarec = serialport_FT(0, 1); this function takes liltime and so the GUI freezes till that time how to avoid this?
i tried using background worker but didn't get where to put this whole process
also encountered with cross-threaded operation error when it goes to ovalShape and changing its properties.
I'm not getting what part of the function to be used in the background and where and when to get back to the 1st thread
please help me to use backgroundworker or use invoke if i have to use Threading
You could do something like this:
toolStripStatusLabel1.Text = " Device Testing...";
positive = false;
clearsensors_gui();
datarec = false;
cmd = 04;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
// Will be run on background thread
args.Result = serialport_FT(0, 1);
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
bool result = (bool)args.Result;
if (result)
{
// Do your UI updates here
}
};
worker.RunWorkerAsync();
One improvement could be to combine datarec and rec_data as a Tuple in args.Result.
In the background worker you use the DoWork event.
worker.DoWork += new DoWorkEventHandler(yourEventHandler);
void yourEventHandler(object sender, DoWorkEventArgs e)
{
//your work here
}
As you are using WinForms, here is a great MSDN article to get you started with using multiple threads in an application: Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads
The article is 'a few days old', but the principles remain absolutely valid today.
If you are working in a .NET 4.x version, you can also use the Task Parallel Library to make working with multiple threads easier.
The upcoming .NET 4.5 also offers the even more comfortable await and asyc keywords: Asynchronous Programming with Async and Await.
Put this in a background thread as you already tried (or better yet, a Task), but be careful to call GUI-related operations only through Control.Invoke (for WinForms) or Dispatcher.Invoke (for WPF).
Used a Label which updates in real time as the task progresses. You can try this code [using BackGroundWorker]. Look at DoWork where you put your business logic [See BusinessClass usage in the code], then see the ProgressChanged where the background task signals the UI in real time as the task progresses & finally see the RunWorkerCompleted where you handle code after task completion, error or cancellation.
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
BusinessClass _biz = new BusinessClass();
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
int junk = 0;
for (i = 0; i <= 199990000; i++)
{
int result = _biz.MyFunction(junk);
junk++;
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// note I can pass the business class result also and display the same in the LABEL
_worker.ReportProgress(percentComplete, result);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
}
else if (e.Error != null)
{
// Do something with the error
}
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
}
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
}
Let's say I have the following function in C#:
void ProcessResults()
{
using (FormProgress f = new FormProgress()) {
f.ProgressAmount = 10;
// I want to have the following line run in a BackgroundWorkerThread
RetrieveAndDisplayResults();
f.ProgressAmount = 100;
}
}
What would I need to do for the line RetrieveAndDisplayResults(); to be run in a BackgroundWorkerThread?
var f = new FormProgress()
f.ProgressAmount = 10;
var worker = new BackgroundWorker();
worker.DoWork += (o, e) => RetrieveAndDisplayResults();
worker.RunWorkerCompleted += (o, e) =>
{
f.ProgressAmount = 100;
f.Close();
}
worker.RunWorkerAsync();
If your method is updating the UI, you'll have to change it to return the results, and then display them in the worker's RunWorkerCompleted event.
You can also use the ProgressChanged event and ReportProgress method to have more granular progress updates.
Your current approach is not well suited for using a Thread (Bgw or otherwise).
The main problem is the 'waiting' part before setting progress=100. What should the this method be doing in that time?
You can reverse the logic, launch a Bgw and use the Progress and Completed events to Update resp. Close your form.
You will probably have to change your approach, but the code below should be able to give a scaffolding for a long-running task that updates the UI as it progresses:
private void LaunchWorker()
{
var worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(OnDoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(OnProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnRunWorkerCompleted);
worker.RunWorkerAsync();
}
void OnDoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
while (aLongTime)
{
worker.ReportProgress(percentageDone, partialResults);
}
e.Result = results;
}
void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
var progress = e.ProgressPercentage;
var partialResults = e.UserState;
// Update UI based on progress
}
void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var results = e.Result;
// Do something with results
}
i am developing a project (WPF) and i have a Datagrid the load more than 5000 records from the database so i used a BackgroundWorker to advice the user the data is loading but it is so slow , i need to wait almost 2 minutes to load the data from the database,instead if i don't use the BackgroundWorker i need to wait just 3 second to load the data in the Datagrid.
Here i write down the code snippet that i use for the BackgroundWorker :
private void RunWorker()
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker senderWorker = sender as BackgroundWorker;
dc = new DataClasses1DataContext();
var query = from c in dc.Contact_DDBB_Xavis
select
new
{
c.ContactID,
c.Continent,
c.Country,
c.City,
c.PostalCode,
c.CompanyName,
c.UserCreated,
c.DateCreated,
c.UserModified,
c.DateModified
};
if (query.Count() > 0)
{
for (int i = 0; i < query.Count(); i++)
{
int progressInPercent = (int)(((decimal)(i + 1) / (decimal)query.Count()) * 100);
worker.ReportProgress(progressInPercent, i);
System.Threading.Thread.Sleep(10);
e.Result = query.ToList();
}
}
if (senderWorker.CancellationPending)
{
e.Cancel = true;
}
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
this.dataGrid.DataContext = e.Result;
backGround.Visibility = Visibility.Collapsed;
duracel.Visibility = Visibility.Collapsed;
txtBackWORK.Visibility = Visibility.Collapsed;
}
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
for (double i = 0.0; i < e.ProgressPercentage; i++)
{
duracel.pthFiller.Width = 0;
double max = 312;
max = (double)e.ProgressPercentage;
duracel.pthFiller.Width = e.ProgressPercentage * 3.12;
duracel.txtStatus.Text = e.ProgressPercentage + " %";
txtBackWORK.Text = String.Format("Loading " + e.ProgressPercentage + " %");
}
}
now i don't know if there is something wrong in my code so i ask you some advice to how load faster the data from database without wait so long time.
Thanks for your attention.
Have a good time.
Cheers
Each time you call query.Count(), you're running another SQL query.
You should call Count() once, and store it in a local variable.
Also, you should only call `ReportProgress if the progress actually changed. (There's no point in calling it 1,000 times)
Umm... you are calling System.Threading.Thread.Sleep() in your code, in a loop, which appears to be calling the query multiple times.
Your update code also seems to be needlessly looping from 0 to the current percentage... not sure why?
Get rid of the whole progress indicator block as it exists. You are iterating over every single record, pausing your thread, re-calling the query and assigning it to e.Result and generally incurring overhead 5,000 times after your data has already loaded. This is useless.
If the data loads in a matter of seconds, show a marquee progressbar on the UI thread and everyone will be happy.