progressbar won't update with backgroundworker? - c#

im making file transfer (Server-Client) TCP
i've already looked for same questions like this one .. but no answer worked for me ..
ProgressBar doesn't update with backgroundworker .. i've searched for tutorials to do this .. and i exactly followed the steps.
the form lags while sending and after the file sent.. the progressbar goes to 100%
The file sent succesfully the send method code works fine... my problem just with updating the progressbar .. how do i fix that ??
Here i call (backgroundWorker1.RunWorkerAsync)
public void Send(string destPath)
{
if (listView1.Items.Count > 0)
{
List<String> job = new List<string>();
job.Add(listView1.Items[0].ToolTipText);
job.Add(destPath);
backgroundWorker1.RunWorkerAsync(job);
}
}
DoWork Method
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
List<string> job = (List<string>)e.Argument;
SendFile(job[0],job[1]);
}
here's the SEND method which i use (backgroundWorker1.ReportProgress)
private void SendFile(string srcPath, string destPath)
{
string dest = Path.Combine(destPath, Path.GetFileName(srcPath));
using (fs = new FileStream(srcPath, FileMode.Open, FileAccess.Read))
{
try
{
long fileSize = fs.Length;
sizeAll = fileSize;
long sum = 0;
int count = 0;
data = new byte[fs.Length];
SendCommand("receive<" + dest + "<" + fs.Length.ToString());
while (sum < fileSize)
{
if (fileSize - sum < packetSize)
{
count = fs.Read(data, 0, (int)(fileSize - sum));
network.Write(data, 0, (int)(fileSize - sum));
}
else
{
count = fs.Read(data, 0, data.Length);
network.Write(data, 0, data.Length);
}
fs.Seek(sum, SeekOrigin.Begin);
sum += count;
sumAll += count;
backgroundWorker1.ReportProgress((int)((sum * 100) / fileSize));
}
network.Flush();
}
finally
{
CloseTransfer();
}
}
}
and here is backgroundWorker1_ProgressChanged
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBarFile.Value= e.ProgressPercentage;
}

Seems strange the you're able to assign a value to the a UI control from another thread without getting any exception. Or that is, may be, a real issue.
Like a first thing I would do, if the code of ProgressChanged is inside (say) WindowsForm class, write like this:
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
this.Invoke(new Action(()=>
progressBarFile.Value= e.ProgressPercentage;
));
}
Something like this.

Did you set?
worker.WorkerReportsProgress = true;
(Either in code or in the properties window)
EDIT:
Shouldn't you be reading like this
count = fs.Read(data, 0, packetSize);
instead of reading data.Length bytes? Since you set data = new byte[fs.Length] the file will be read all at once, instead of in little pieces, which is required in order to see the progress bar change progressively.

thanks for [Nikola Markovinović] his answer was:
the line that causes the error was :
data = new byte[fs.Length];
after correcting the code :
data = new byte[packetSize];
while (sum < fileSize)
{
count = fs.Read(data, 0, data.Length);
network.Write(data, 0, count);
sum += count;
backgroundWorker1.ReportProgress((int)((sum * 100) / fileSize));
}
network.Flush();

The background worker is dorking around on a new thread, therefore, calling anything back to the original form and its controls (or original thread) will require some delegation to accomplish what you desire. I have several code examples that I can share but they would take up far too much space here on this site. As a quick assistance, try some of this information from this site.
Background Worker with Delegates
Of course, you can always Google for more regarding threading, delegates, etc. I hope that helps.

Well it may be something quirky but always worth checking the steps its easy to overlook first:
Have you set WorkerReportsProgress on the BackgroundWorker to true?
Have you hooked up the ProgressChanged event to your event handler?
Finally just compare your solution to a sample at the link below - it may remind you of something you've forgotten to do:
http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx

Related

How to display data received from serial port in a textbox without the text disappearing in Visual Studio C#?

So, I'm trying to develop a simple application in visual C# which gets data from serial port and displays it in a textbox (to monitor temperature). I'm acquiring and displaying the data successfully, using the DataReceived event to update a global string variable and a timer to update the text field on my text box, as shown:
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
try
{
globalVar.updateTemp = port.ReadLine(); //This is my global string
}
catch (IOException)
{
}
catch (InvalidOperationException)
{
}
catch (TimeoutException)
{
}
}
private void timer1_Tick(object sender, EventArgs e)
{
tempDisplayBox.Text = globalVar.updateTemp; //This is my textbox updating
}
The only issue I have is that the value shown in the textbox keeps flashing, making it hard to read. My timer is set to trigger every 10 ms (which should be fast enough, right?). Is there any way to make it more stable? I realize this may be a newb question, but to be fair I am a newb :) Any help is appreciated! Thanks!
Do you really need it updating every 10ms? What about every 500 ms or if not that then 100ms. 100ms will require your update method run 10 times less and therefore update 10 times less. The flickering you are expiriencing is due to the refresh speed. You could create custom method which will only update the temp only when target Label or textBox value is different than source port. But that will only sort the flickering when temp is steady, when temp will start vary it will bring back the flickering. Good luck ;-)
UPDATE
Hi I tried to reproduce the conditions and could not make my textbox nor Label flash. The way I tested it was by assigning int ntick = 0; and then increment the ++ntick; inside of the timer_tick method. The results didn't make any of the controls flash and were updated even every milisecond at some point. I also tried string.Format to put some load on the method. Is your app responsive?
The trick is to use double buffering. This way the operating system will redraw the Control off-screen, and only show the control when it is fully redrawn.
I have had the same problem, and solved it by extending the TextBox control like this:
public FastLogBox()
{
InitializeComponent();
_logBoxText = new StringBuilder(150000);
timer1.Interval = 20;
timer1.Tick += timer1_Tick;
timer1.Start();
SetStyle(ControlStyles.DoubleBuffer, true);
}
void timer1_Tick(object sender, EventArgs e)
{
if (_timeToClear)
{
_logBoxText.Clear();
_timeToClear = false;
}
if (_logQueue.Count <= 0) return;
while (!_logQueue.IsEmpty)
{
string element;
if (!_logQueue.TryDequeue(out element)) continue;
{
_logBoxText.Insert(0, element + "\r\n");
}
}
if (_logBoxText.Length > 150000)
{
_logBoxText.Remove(150000, _logBoxText.Length - 150001);
}
Text = _logBoxText.ToString();
}
public new void Clear()
{
_timeToClear = true;
while (!_logQueue.IsEmpty)
{
string element;
_logQueue.TryDequeue(out element);
}
}
public void AddToQueue(string message)
{
_logQueue.Enqueue(message);
}
}
I also use a timer and a concurrentQueue to avoid using Invoke to update the control from another thread. I also use a StringBuilder to prepare the string before putting it into the TextBox. StringBuilder is faster when building larger strings.
You can use ReadExisting() to read the whole data at a time.
You need to handle DataReceived Event of SerialPort
serialPort1.ReadExisting();
Sample:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
String myData=serialPort1.ReadExisting();
}
Example Code: Here i would like to show you the code to Read Data(RFID Tag Code which is basically of length 12)
String macid = "";
private void DoWork()
{
Invoke(
new SetTextDeleg(machineExe ),
new object[] { macid });
macid = "";
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string str1;
macid += serialPort1.ReadExisting();
if (macid.Length == 12)
{
macid = macid.Substring(0, 10);
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();
}
}
public void machineExe(string text)
{
TextBox1.Text=text;
}
Thank you so much for the answers! I found a way to work around this issue:
Instead of replacing the contents of my textbox by rewriting the TextBox.Text property - which, as HenningNT implied, refreshes the control and causes the flickering - I'm now using the TextBox.AppendText method. Though, as I want to display only one line of data at a time, I use the textbox in multiline mode and the Environment.NewLine to jump to a new line before appending the text. As for the method of updating, I've gone back to using the timer because with the invoke method was crashing my application when I close the form, for some reason. Also, enabling double buffering didn't do me much good, although I guess I was doing it wrong... It still flickers a bit, but it's much better now :) I know this is not really a perfect solution (much more of a workaround), so I'll keep looking for it. If I find it, I'll be sure to update it here ;) My code:
private void timer1_Tick(object sender, EventArgs e) //Timer to update textbox
{
if (tempDisplayBox.Text != globalVar.updateTemp) //Only update if temperature is different
{
try
{
tempDisplayBox.AppendText(Environment.NewLine);
tempDisplayBox.AppendText(globalVar.updateTemp);
}
catch (NullReferenceException)
{
}
}
}

C# Download lag

I have a serious problem. I'm currently doing a patcher program.
There's a "Patch" button in the program, and if the user click it, the download is starting.
There are currently 5 files that the program needs to download. The downloading is correct, this part of the program is working but when I click the Patch button, the program starts lagging, and I can't close, or change the position of it.
Here's the code:
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(this.download));
thread.Start();
}
public void download()
{
int downloaded = 0;
int all = 5;
WebClient myWebClient = new WebClient();
if (button1.InvokeRequired)
{
MethodInvoker MethodControll = new MethodInvoker(download);
this.button1.Invoke(MethodControll);
}
else
{
double state;
jelenlegidownload.Text = "alut.dll";
myWebClient.DownloadFile(Files.alutDLL, "alut.dll");
downloaded++;
state = downloaded / all * 100;
progressBar.Value = Convert.ToInt32(state);
progressBar.Refresh();
this.Refresh();
jelenlegidownload.Text = "BlackBox.dll";
myWebClient.DownloadFile(Files.BlackBoxDLL, "BlackBox.dll");
downloaded++;
state = downloaded / all * 100;
progressBar.Value = Convert.ToInt32(state);
progressBar.Refresh();
this.Refresh();
jelenlegidownload.Text = "DevIL.dll";
myWebClient.DownloadFile(Files.DevILDLL, "DevIL.dll");
downloaded++;
state = downloaded / all * 100;
progressBar.Value = Convert.ToInt32(state);
progressBar.Refresh();
this.Refresh();
jelenlegidownload.Text = "fltkdll.dll";
myWebClient.DownloadFile(Files.fltkdllDLL, "fltkdll.dll");
downloaded++;
state = downloaded / all * 100;
progressBar.Value = Convert.ToInt32(state);
progressBar.Refresh();
this.Refresh();
jelenlegidownload.Text = "glut32.dll";
myWebClient.DownloadFile(Files.glut32DLL, "glut32.dll");
downloaded++;
state = downloaded / all * 100;
progressBar.Value = Convert.ToInt32(state);
progressBar.Refresh();
this.Refresh();
The Files.cs:
public class Files
{
public static string alutDLL = "https://dl.dropbox.com/s/62tt9w194xefk7t/alut.dll?token_hash=AAHQmybYdR44TRrS9bWQWV7jlZBzZQ-mmmjNy1Kv_qR4cg&dl=1";
public static string BlackBoxDLL = "https://dl.dropbox.com/s/vtdrl8qdpky8p08/BlackBox.dll?token_hash=AAHCtQPBJ5s-3aL5B4FqrmOUIGP6BVvW8ZQeWd-xBzysTw&dl=1";
public static string DevILDLL = "https://dl.dropbox.com/s/spni307vmk4zng9/DevIL.dll?token_hash=AAEmZdQj3dv2NIEh6tcWwkgyJHCytSsX65QXZyNGY2Vl1w&dl=1";
public static string fltkdllDLL = "https://dl.dropbox.com/s/fsa29pelfwgk5ha/fltkdll.dll?token_hash=AAF55SuU_8bfli5gIiPpA-VLWUmZKLbOK-Ys8iokuJ8_XA&dl=1";
public static string glut32DLL = "https://dl.dropbox.com/s/cptiwxv17nhtywp/glut32.dll?token_hash=AAGCNXQPpwrByjp-uG_avBbkNyNjTfOJFxbY3ieNAfLzVw&dl=1";
}
How can I fix the lagging? (As I said before, if I click the "Patch" button, the files are downloading, but the program "stops")
To stop the download part of your application to block your UI part of the application, why don't you do the download job on a different thread. This way your UI will still be responsive even if the download has not been finished.
Edit: I think this is a great article that applies to your case:
http://www.developerfusion.com/article/4134/net-threading-part-i/
I think you have misunderstood InvokeRequired.
From MSDN:
Gets a value indicating whether the caller must call an invoke method
when making method calls to the control because the caller is on a
different thread than the one the control was created on.
Basically, you start your download on another thread. But since this will cause InvokeRequired to be true, you recall the method on the GUI thread, locking the application.
Does it work better if you remove this section?
if (button1.InvokeRequired)
{
MethodInvoker MethodControll = new MethodInvoker(download);
this.button1.Invoke(MethodControll);
}
Update
To rule out that your doing something wrong with your progress bar, try simplify your download method to this:
public void download()
{
WebClient myWebClient = new WebClient();
myWebClient.DownloadFile(Files.alutDLL, "alut.dll");
myWebClient.DownloadFile(Files.BlackBoxDLL, "BlackBox.dll");
myWebClient.DownloadFile(Files.DevILDLL, "DevIL.dll");
myWebClient.DownloadFile(Files.fltkdllDLL, "fltkdll.dll");
myWebClient.DownloadFile(Files.glut32DLL, "glut32.dll");
}
This should make the files downloaded without lag, but you want see any updates in your progress bar (obviously).

WPF - print during a loop

I have a simple issue, but the solution appears to be tricky. I want to print using the WPF control canvas during a loop; but for each iteration, I want to udpate the canvas control.
If I want to print a canvas control in WPF, I can simply call
PrintDialog dialog = new PrintDialog();
dialog.PrintVisual(this.canvas, "");
And it prints as expected to my default printer. Wonderful.
However, if I want to perform this multiple times in a loop and make an update to the canvas during each iteration, only the final iteration of the loop is printed.
private void methodName()
{
for (int i = 0; i < 2; i++)
{
updateTextBox(i.ToString());
PrintDialog dialog = new PrintDialog();
dialog.PrintVisual(this.canvas, "");
}
}
private void updateTextBox(string text)
{
txtTextBox.Text = text;
}
Any idea what I need to do to ensure that I get 2 print outs, the first with the txtTextBox.Text value of 0, the second time it has the value of 1?
I am about to implement something similar in my application and found out that my previous answer wasn't good enough. The problem for me was that although the canvas is updated in each iteration, it has not yet rendered itself before being sent to PrintVisual. It surprises me that you get your final iteration printed, I only got the first one. Anyway, this is how I made it work, basically queueing the print command after the already pending render operation:
for (int i = 0; i < 2; i++)
{
updateTextBox(i.ToString());
this.canvas.InvalidateVisual(); // Maybe not needed in your case
PrintDialog dialog = new PrintDialog();
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, (Action)delegate()
{
dialog.PrintVisual(this.canvas, "" + i);
});
}
Yes it's somewhat similar (but not identical) to SalGad's answer and the post you're referring to, but I'm not able to comment that answer, so please try this out, it works for me.
I also had problems with disappearing prints when using empty description for the print jobs, thus the + i. Don't know if that is a generic problem, or just something with my printer setup.
I got the idea from this post, which also mentions an alternative solution using ViewBox.
OK
I solved it.
I removed all the dispatcher object methods so it runs on a single thread.
To update the canvas, I used the canvas.UpdateLayout() method.
I also ensured that the print had finished before updating the next canvas (the next iteration).
private void methodName()
{
for (int i = 0; i < 2; i++)
{
updateTextBox(i.ToString());
this.canvas.UpdateLayout();
PrintDialog dialog = new PrintDialog();
dialog.PrintVisual(this.canvas, "ABC");
dialog.PrintQueue.Refresh();
while (dialog.PrintQueue.NumberOfJobs != 0)
{
bool isQueued = false;
foreach (var job in dialog.PrintQueue.GetPrintJobInfoCollection())
{
if (job.Name == "ABC")
isQueued = true;
}
if (!isQueued)
break;
Thread.Sleep(500);
dialog.PrintQueue.Refresh();
}
}
}
private void updateTextBox(string text)
{
txtTextBox.Text = text;
}
I also could have just done thread.sleep(3000) - this worked as it was enough time to ensure the print job had completed, but it was also a little bit 'hopeful' and I wanted something more secure.
Thank you to everyone for your suggestions.
If you are going to call PrintVisual multiple times you have to look into PrintDocument and DocumentPaginator.
Just a guess, but it might be worth trying RenderTargetBitmap to force rendering the canvas in each iteration, and then create an Image with that source, which then can be sent to PrintVisual. See this post for code example:
Printing viewport
Just taking a shot here, but can you try refreshing the WPF canvas controls at the start of every for loop iteration? Here is the code snippet:
// declare this
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
}
// the loop
for (int i = 0; i < 2; i++)
{
updateTextBox(i.ToString());
PrintDialog dialog = new PrintDialog();
dialog.PrintVisual(this.canvas, "");
}
}
// update this method
private void updateTextBox(string text)
{
txtTextBox.Text = text;
txtTextBox.Refresh();
Thread.Sleep(500);
}
I am sourcing this idea from here

VS2010, C#, passing parameter bulk into threads, speed issue

I have the code below where it update the data within listview, I was trying to pass parameter into main code which update the listview table.
I have external code that can send burst data stream and it shown same value on the list.
Below is based on anonymous method which work but earlier data get overwritten.
The listview is too slow which slow down main program (not listed here) the burst data goes to this code and use separate thread to handle the display (about 20 set). The burst data is about 20 set of dataTX array, etc.
I'm open for suggestion how to fix this.
==========================================================================
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
this.ReqAddMessageThread = new Thread(delegate() { ReqAddMessageThreadProc(isCRCIncluded, dataTX, d); }); //anonymous method
this.ReqAddMessageThread.Start();
}
#endregion
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
test1Callback del = new test1Callback(ReqAddMessageThreadProc);
this.Invoke(del, new object[] { isCRCIncluded, dataTX,d });
return;
}
if (this.Visible == true)
{
SendMessage(this.Handle, WM_SETREDRAW, false, 0);
}
int length = myLinTools.LINDataLength;
int pCRC = 0;
elem = new ListViewItem(m_Item.ToString());
elem.SubItems.Add(d.Date.ToShortDateString());
elem.SubItems.Add(d.ToShortTimeString() + ":" + d.Second.ToString());
elem.SubItems.Add("");
for (int i = 0; i < length + 1; i++)
{
elem.SubItems.Add(dataTX[i].ToString("X2"));
pCRC = i;
}
for (int i = length; i < 8; i++)
{
elem.SubItems.Add(" "); // fill gaps
}
if (isCRCIncluded == true) // Does the message contains processed CRC data?
{
elem.SubItems.Add(dataTX[pCRC + 1].ToString("X2"));
}
else // No, then make one for display only!!
{
Byte CRC = myLinTools.CRC_Processor(false);
elem.SubItems.Add(CRC.ToString("X2"));
}
this.OutputView.Items.Add(elem);
this.OutputView.EnsureVisible(m_Item);
if (myLinTools.IsRequestResponse == true) // Request Message Only
{
if (this.Visible == true) // Is form open?
{
SendMessage(this.Handle, WM_SETREDRAW, true, 0);
this.Refresh();
}
}
m_Item++;
}
=================================================================================
Thanks KazR, I modified the code which worked fine, however it slow down the other high level program (call it main program), that making data transfer to this program that display data. One of the requirement that the main program stream the data without delay or pause cause by listview in this display program. That why I'm looking for way to use thread, so it release the control back to main program and thus operates faster, but however there is issue in keep data from being over-written by next thread, since listview is slow. Perhaps I should consider a buffer, which update only when there is no activity in main program.
I do not wish to use virtual, I'm open for alternative suggestion.
==================================================================================
delegate void ReqAddMessageTCallback(bool isCRCIncluded, byte[] dataTX, DateTime d);
#region//==================================================LIN_Request_Add_Message
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
ReqAddMessageThreadProc(isCRCIncluded, dataTX, d);
}
#endregion
#region//==================================================ReqAddMessageThreadProc
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
ReqAddMessageTCallback del = new ReqAddMessageTCallback(ReqAddMessageThreadProc);
this.BeginInvoke(del, new object[] { isCRCIncluded, dataTX, d });
return;
}
if (this.Visible == true)
{
SendMessage(this.Handle, WM_SETREDRAW, false, 0);
}
int length = myLinTools.LINDataLength;
int pCRC = 0;
elem = new ListViewItem(m_Item.ToString());
From your code example it appears that you're creating a new Thread object each time you receive data and all this thread is doing is calling the ReqAddMessageThreadProc method. Assuming that calls to LIN_Request_Add_Message are not being made in the main UI thread, you could try removing the Thread creation & start calls, replace them with a direct call the ReqAddMessageThreadProc and use BeginInvoke rather than Invoke.
e.g.
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
ReqAddMessageThreadProc(isCRCIncluded, dataTX, d);
}
#endregion
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
test1Callback del = new test1Callback(ReqAddMessageThreadProc);
this.BeginInvoke(del, new object[] { isCRCIncluded, dataTX,d });
return;
}
etc...
The BeginInvoke call is the async version of Invoke, this should negate the need to use separate new Thread objects each time you receive new data.
You should make your ListView virtual. Then you can operate at full speed with your threads and the listview will display only the items which are currently visible. If you do append things to the ListView and not force to make it visible every time something is added you can let the user decide when he does want to scroll down. He does see when things are appended because the scrollbar is becoming smaller and smaller while items are added.
This way you can display millions of entries without any issues and you will get rid of ivoking, redrawing, refreshing the listview via SendMessage. Here is a virtual list view sample how you can change it.
By doing so your code will become simpler and faster because you do not mix background data processing with UI stuff. E.g. sorting can be done at raw data array level without doing anything in the UI except to trigger it.

BackgroundWorker Help needed

I have code that does a web-service request.
While doing this request I need a progress-bar to be moving independently.
My problem is that I just need to say run a progress update every 1 or 2 seconds and check to see if progress of the request has been completed.
NetBasisServicesSoapClient client = new NetBasisServicesSoapClient();
TransactionDetails[] transactions = new TransactionDetails[dataGridView1.Rows.Count - 1];
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
transactions[i] = new TransactionDetails();
transactions[i].TransactionDate = (string)dataGridView1.Rows[i].Cells[2].Value;
transactions[i].TransactionType = (string)dataGridView1.Rows[i].Cells[3].Value;
transactions[i].Shares = (string)dataGridView1.Rows[i].Cells[4].Value;
transactions[i].Pershare = (string)dataGridView1.Rows[i].Cells[5].Value;
transactions[i].TotalAmount = (string)dataGridView1.Rows[i].Cells[6].Value;
}
CostbasisResult result = client.Costbasis(dataGridView1.Rows[0].Cells[0].Value.ToString(), dataGridView1.Rows[0].Cells[1].Value.ToString(), transactions, false, "", "", "FIFO", true);
string result1 = ConvertStringArrayToString(result.Details);
I use Background Workers all the time, they are great for processing long time actions.
from your code
#region Background Work of My Request
private void ProcessMyRequest()
{
if (!bkgWorkerMyRequest.IsBusy)
{
lblMessageToUser.Text = "Processing Request...";
btnProcessRequest.Enabled = false;
bkgWorkerMyRequest.RunWorkerAsync();
}
}
private void bkgWorkerMyRequest_DoWork(object sender, DoWorkEventArgs e)
{
// let's process what we need in a diferrent thread than the UI thread
string r = GetStuffDone();
e.Result = r;
}
private void bkgWorkerMyRequest_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
string myResult = (String)e.Result;
lblMessageToUser.Text = myResult;
btnProcessRequest.Enabled = true;
}
#endregion
private function string GetStuffDone()
{
NetBasisServicesSoapClient client = new NetBasisServicesSoapClient();
TransactionDetails[] transactions = new TransactionDetails[dataGridView1.Rows.Count - 1];
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
transactions[i] = new TransactionDetails();
transactions[i].TransactionDate = (string)dataGridView1.Rows[i].Cells[2].Value;
transactions[i].TransactionType = (string)dataGridView1.Rows[i].Cells[3].Value;
transactions[i].Shares = (string)dataGridView1.Rows[i].Cells[4].Value;
transactions[i].Pershare = (string)dataGridView1.Rows[i].Cells[5].Value;
transactions[i].TotalAmount = (string)dataGridView1.Rows[i].Cells[6].Value;
}
CostbasisResult result = client.Costbasis(dataGridView1.Rows[0].Cells[0].Value.ToString(), dataGridView1.Rows[0].Cells[1].Value.ToString(), transactions, false, "", "", "FIFO", true);
return ConvertStringArrayToString(result.Details);
}
all you need to do is call the method:
ProcessMyRequest();
and it will do the job. If you need to let the main Thread to be aware of progress, you can use the ProgressChanged event
private void bkgWorkerMyRequest_ProgressChanged(
object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
in the bkgWorkerMyRequest_DoWork method you need to change the code to have
//reports a percentage between 0 and 100
bkgWorkerMyRequest.ReportProgress(i * 10);
Remember:
alt text http://www.balexandre.com/temp/2010-04-07_1200.png
You will, however get stuck when trying to Debug the method GetStuffDone as it's that kinda hard debugging multi threaded applications
So, what I do is, debug everything without workers and then apply the workers.
Works fine for me, let me know if you need any more help on this.
added
I didn't aware that you were getting the Grid in the worker, sorry, for this, just send the grid as a argument and use it, please change:
bkgWorkerMyRequest.RunWorkerAsync(dataGridView1);
string r = GetStuffDone((GridView)e.Argument);
private function string GetStuffDone(GridView dataGridView1)
Create a BackgroundWorker (call the instance "bgw") and type "bgw.DoWork += " followed by TAB TAB. Visual Studio then generates the DoWork event handler method. Copy the code above into the handler and voila.
I don't think it makes sense for you to report progress, since your progress is determined by the duration of the web service request over which you have no influence (and you cannot break it into smaller tasks). As such, just display the "doing work" dialog and initiate the background task to call the web service. When it's done close the "doing work" dialog.

Categories

Resources