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).
Related
I'm trying to create a custom download app. Its all working except for the download all button that cant pick up the "percent1" variable from the "DownloadProgressChangedEventArgs". I have instantiated it prior to the mainForm constructor but it wont read the changed value.
Here's the code, partially stripped since most of it isnt relevant to the question:
public partial class Main : Form
{
//Variables (not all, just the one im having issues with)
private double percentage1;
//Main form constructor
public Main(){...}
//Download File Async custom method
public void DldFile(string url, string fileName, string localPath, AsyncCompletedEventHandler completedName, DownloadProgressChangedEventHandler progressName)
{
WebClient webClient = new WebClient();
webClient.DownloadFileAsync(new Uri(url), localPath + "\\" + fileName);
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(completedName);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(progressName);
}
//Button 1 click event to start download
private void btnDld1_Click(object sender, EventArgs e)
{
if (url1 != "" && Directory.Exists(localPath1))
{
_startDate1 = DateTime.Now;
DldFile(url1, fileName1, localPath1, completed1, progress1);
}
//took out the try/catch, other ifs to try and cut it down
}
//Download Progress Changed event for Download 1
public void progress1(object sender, DownloadProgressChangedEventArgs e)
{
percentage1 = e.ProgressPercentage; //THIS IS WHERE I WAS EXPECTING TO UPDATE "percentage1"
progressBar1.Value = int.Parse(Math.Truncate(percentage1).ToString());
}
//Button that starts all downloads click event where all my problems are at the moment
private void btnDldAll_Click(object sender, EventArgs e)
{
//The progress bar that should let me know the global status for all webClients
progressBarAll.Value = (
int.Parse(Math.Truncate(percentage1).ToString()) + //HERE IS MY PROBLEM
int.Parse(Math.Truncate(percentage2).ToString()) + //HERE IS MY PROBLEM
int.Parse(Math.Truncate(percentage3).ToString()) + //HERE IS MY PROBLEM
int.Parse(Math.Truncate(percentage4).ToString()) + //HERE IS MY PROBLEM
int.Parse(Math.Truncate(percentage5).ToString())) / 5; //HERE IS MY PROBLEM
//Checks if the link exists and starts it from the download button click event
if (url1 != "")
{
btnDld1.PerformClick();
}
//Continues for url2, 3, 4, 5 and else
}
}
So this is the shortest way i found of letting you know what im trying to pull off, if there's something missing please let me know, i'll try to add any info as fast as possible.
I have tried to instantiate "progress1" to try and acess its percentage1 variable, but it didnt work. I've tried doing the same thing with the webClient but didnt work either. I have used google and stackflow search to no avail. So im not sure if the question is too dumb, or there's a diferent way to look at the issue thats completely out of my mindset.
So main problem is updating the "percentage1" variable and using it.
There are other problems regarding the "progressBarAll.Value" calculation that will be solved when i can get my hands on the right value. So no need to worry about that if you see it.
Try not to think about 'using the event arguments outside the event'. Think about updating the state of your form.
Use properties to simplify the update logic:
public partial class Main : Form
{
private double percentage1;
private double percentage2;
private double percentage3;
private double percentage4;
private double percentage5;
private double Percentage1
{
get
{
return this.percentage1;
}
set
{
this.percentage1 = value;
this.UpdatePercentageAll(); // this will update overall progress whenever the first one changes
progressBar1.Value = GetValueFromPercentage(value);
}
}
private double Percentage2
// same code as for Percentage1
void UpdatePercentageAll()
{
this.PercentageAll = (this.Percentage1 + this.Percentage2 + this.Percentage3 + this.Percentage4 + this.Percentage5) / 5;
}
static int GetValueFromPercentage(double percentage)
{
return (int)Math.Truncate(percentage);
}
double percentageAll;
private double PercentageAll
{
get
{
return this.percentageAll;
}
set
{
this.percentageAll = value;
progressBarAll.Value = GetValueFromPercentage(value);
}
}
//Download File Async custom method
public void DldFile(string url, string fileName, string localPath, AsyncCompletedEventHandler completedName, DownloadProgressChangedEventHandler progressName)
{
WebClient webClient = new WebClient();
webClient.DownloadFileAsync(new Uri(url), localPath + "\\" + fileName);
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(completedName);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(progressName);
}
//Button 1 click event to start download
private void btnDld1_Click(object sender, EventArgs e)
{
if (url1 != "" && Directory.Exists(localPath1))
{
this.StartDownloadFile1();
}
//took out the try/catch, other ifs to try and cut it down
}
void StartDownloadFile1()
{
this.Percentage1 = 0;
_startDate1 = DateTime.Now;
DldFile(url1, fileName1, localPath1, completed1, progress1);
}
//Download Progress Changed event for Download 1
public void progress1(object sender, DownloadProgressChangedEventArgs e)
{
this.Percentage1 = e.ProgressPercentage; // update property, not field
//this will be done in property setters
//progressBar1.Value = int.Parse(Math.Truncate(percentage1).ToString());
}
// then add similar code for other download buttons
//Button that starts all downloads click event where all my problems are at the moment
private void btnDldAll_Click(object sender, EventArgs e)
{
//Checks if the link exists and starts it from the download button click event
if (url1 != "")
{
this.StartDownloadFile1();
}
//Continues for url2, 3, 4, 5 and else
}
}
I would refactor the code even further, but I think it will be easier for you to understand if the code is closer to the original.
The main idea is to create a set of linked properties which work like mathematical functions. When writing the PercentageX properties I'm kind of saying 'let PercentageAll be the average of all percentages'. Then I have each download update it's own progress. Once any progress is updated it updates the average, and I don't have to rememver that inside the progress changed event handler.
And the last point is updating progress bars from percentage properties. It's quite straightforward: once a percentage is changed, I need to update a bar. If so, why bother writing something like
this.Percentage1 = x;
this.progressBar1.Value = (int)Math.Truncate(x);
In this case I have to remember everywhere that once I change the Percentage1 I have to update the bar. And in my example I just create a strict rule for that which is only in one place and works everytime. So I just cannot forget it. And if I need to change the rule, I need to change only one place, so again I cannot make a mistake.
The technique I demonstrate can be expressed as a well-known rule: 'one rule - one place', which means that you should try to have only single place in code that expresses each logical rule that exists in your program. It is a very important idea, I suggest you learn and use it.
In C#, using the System.Windows.Forms.HtmlDocument class (or another class that allows DOM parsing), is it possible to wait until a webpage finishes its javascript manipulations of the HTML before retrieving that HTML? Certain sites add innerhtml to pages through javascript, but those changes do not show up when I parse the HtmlElements of the HtmlDocument.
One possibility would be to update the HtmlDocument of the page after a second. Does anybody know how to do this?
Someone revived this question by posting what I think is an incorrect answer. So, here are my thoughts to address it.
Non-deterministically, it's possible to get close to finding out if the page has finished its AJAX stuff. However, it completely depends on the logic of that particular page: some pages are perpetually dynamic.
To approach this, one can handle DocumentCompleted event first, then asynchronously poll the WebBrowser.IsBusy property and monitor the current HTML snapshot of the page for changes, like below.
The complete sample can be found here.
// get the root element
var documentElement = this.webBrowser.Document.GetElementsByTagName("html")[0];
// poll the current HTML for changes asynchronosly
var html = documentElement.OuterHtml;
while (true)
{
// wait asynchronously, this will throw if cancellation requested
await Task.Delay(500, token);
// continue polling if the WebBrowser is still busy
if (this.webBrowser.IsBusy)
continue;
var htmlNow = documentElement.OuterHtml;
if (html == htmlNow)
break; // no changes detected, end the poll loop
html = htmlNow;
}
In general aswer is "no" - unless script on the page notifies your code in some way you have to simply wait some time and grab HTML. Waiting a second after document ready notification likley will cover most sites (i.e. jQuery's $(code) cases).
You need to give the application a second to process the Java. Simply halting the current thread will delay the java processing as well so your doc will still come up outdated.
WebBrowserDocumentCompletedEventArgs cachedLoadArgs;
private void TimerDone(object sender, EventArgs e)
{
((Timer)sender).Stop();
respondToPageLoaded(cachedLoadArgs);
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
cachedLoadArgs = e;
System.Windows.Forms.Timer timer = new Timer();
int interval = 1000;
timer.Interval = interval;
timer.Tick += new EventHandler(TimerDone);
timer.Start();
}
What about using 'WebBrowser.Navigated' event?
I made with WEbBrowser take a look at my class:
public class MYCLASSProduct: IProduct
{
public string Name { get; set; }
public double Price { get; set; }
public string Url { get; set; }
private WebBrowser _WebBrowser;
private AutoResetEvent _lock;
public void Load(string url)
{
_lock = new AutoResetEvent(false);
this.Url = url;
browserInitializeBecauseJavascriptLoadThePage();
}
private void browserInitializeBecauseJavascriptLoadThePage()
{
_WebBrowser = new WebBrowser();
_WebBrowser.DocumentCompleted += webBrowser_DocumentCompleted;
_WebBrowser.Dock = DockStyle.Fill;
_WebBrowser.Name = "webBrowser";
_WebBrowser.ScrollBarsEnabled = false;
_WebBrowser.TabIndex = 0;
_WebBrowser.Navigate(Url);
Form form = new Form();
form.Hide();
form.Controls.Add(_WebBrowser);
Application.Run(form);
_lock.WaitOne();
}
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
HtmlAgilityPack.HtmlDocument hDocument = new HtmlAgilityPack.HtmlDocument();
hDocument.LoadHtml(_WebBrowser.Document.Body.OuterHtml);
this.Price = Convert.ToDouble(hDocument.DocumentNode.SelectNodes("//td[#class='ask']").FirstOrDefault().InnerText.Trim());
_WebBrowser.FindForm().Close();
_lock.Set();
}
if your trying to do this in a console application, you need to put this tag above your main, because Windows needs to communicate with COM Components:
[STAThread]
static void Main(string[] args)
I did not like this solution, But I think that is no one better!
In my (Silverlight) weather app I am downloading up to 6 seperate weather radar images (each one taken about 20 mins apart) from a web site and what I need to do is display each image for a second then at the end of the loop, pause 2 seconds then start the loop again. (This means the loop of images will play until the user clicks the back or home button which is what I want.)
So, I have a RadarImage class as follows, and each image is getting downloaded (via WebClient) and then loaded into a instance of RadarImage which is then added to a collection (ie: List<RadarImage>)...
//Following code is in my radar.xaml.cs to download the images....
int imagesToDownload = 6;
int imagesDownloaded = 0;
RadarImage rdr = new RadarImage(<image url>); //this happens in a loop of image URLs
rdr.FileCompleteEvent += ImageDownloadedEventHandler;
//This code in a class library.
public class RadarImage
{
public int ImageIndex;
public string ImageURL;
public DateTime ImageTime;
public Boolean Downloaded;
public BitmapImage Bitmap;
private WebClient client;
public delegate void FileCompleteHandler(object sender);
public event FileCompleteHandler FileCompleteEvent;
public RadarImage(int index, string imageURL)
{
this.ImageIndex = index;
this.ImageURL = imageURL;
//...other code here to load in datetime properties etc...
client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
client.OpenReadAsync(new Uri(this.ImageURL, UriKind.Absolute));
}
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null)
{
StreamResourceInfo sri = new StreamResourceInfo(e.Result as Stream, null);
this.Bitmap = new BitmapImage();
this.Bitmap.SetSource(sri.Stream);
this.Downloaded = true;
FileCompleteEvent(this); //Fire the event to let the app page know to add it to it's List<RadarImage> collection
}
}
}
As you can see, in the class above I have exposed an event handler to let my app page know when each image has downloaded. When they have all downloaded I then run the following code in my xaml page - but only the last image ever shows up and I can't work out why!
private void ImageDownloadedEventHandler(object sender)
{
imagesDownloaded++;
if (imagesDownloaded == imagesToDownload)
{
AllImagesDownloaded = true;
DisplayRadarImages();
}
}
private void DisplayRadarImages()
{
TimerSingleton.Timer.Stop();
foreach (RadarImage img in radarImages)
{
imgRadar.Source = img.Bitmap;
Thread.Sleep(1000);
}
TimerSingleton.Timer.Start(); //Tick poroperty is set to 2000 milliseconds
}
private void SingleTimer_Tick(object sender, EventArgs e)
{
DisplayRadarImages();
}
So you can see that I have a static instance of a timer class which is stopped (if running), then the loop should show each image for a second. When all 6 have been displayed then it pauses, the timer starts and after two seconds DisplayRadarImages() gets called again.
But as I said before, I can only ever get the last image to show for some reason and I can't seem to get this working properly.
I'm fairly new to WP7 development (though not to .Net) so just wondering how best to do this - I was thinking of trying this with a web browser control but surely there must be a more elegant way to loop through a bunch of images!
Sorry this is so long but any help or suggestions would be really appreciated.
Mike
You can use a background thread with either a Timer or Sleep to periodically update your image control.
Phạm Tiểu Giao - Threads in WP7
You'll need to dispatch updates to the UI with
Dispatcher.BeginInvoke( () => { /* your UI code */ } );
Why don't you add the last image twice to radarImages, set the Timer to 1000 and display just one image on each tick?
I wanna use AxWebBrowser on console application, but it give me following exception:
Exception of type 'System.Windows.Forms.AxHost+InvalidActiveXStateException' was thrown.
anybody please help me on this by any sample code for using AxWebBrowser in console application c# without any exeption ...
Yes, the [STAThread] attribute is required on your Main() method so that COM is initialized properly to make the main thread a Single Threaded Apartment. That's not all though, you will also need to pump a message loop. That's a requirement for an STA. Without one, WebBrowser cannot update its state or run its event handlers, you'll never get the DocumentCompleted event for example. You can get a message loop with Application.Run().
Your console application is now indistinguishable from a Windows Forms application. It is actually easier to get everything right by starting a new project with the Windows Forms application project template, then Project + Properties, Output type = Console Application. Edit the Application.Run() call in Program.cs so it doesn't create a form. It won't make dealing with Application.Run() any easier, consider a Timer to run code.
Add the STAThread attribute to your Main method.
However, you should not be using the "raw" ActiveX control.
Instead, add a reference to System.Windows.Forms.dll and use the WebBrowser class. (Yes, you can do that in a Console app)
Also, automating IE is not ideal. You should consider using the WebCLient class.
My class is as below but in the run time it gives me System.Windows.Forms.AxHost+InvalidActiveXStateException:
public class Browse
{
private static AxWebBrowser wBrowser;
public static Result StartBrowse(string url)
{
var validUri = (url.Contains("http://") ? url : "http://" + url);
wBrowser = new AxWebBrowser();
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(AxWebBrowser));
((ISupportInitialize) (wBrowser)).BeginInit();
wBrowser.OcxState = ((AxHost.State)(resources.GetObject("wBrowser.OcxState")));
wBrowser.NewWindow2 += wBrowser_NewWindow2;
wBrowser.NewWindow3 += wBrowser_NewWindow3;
wBrowser.DocumentComplete += wBrowser_DocumentComplete;
wBrowser.DownloadComplete += wBrowser_DownloadComplete;
if (string.IsNullOrEmpty(html) || validUri != url)
{
object empty = System.Reflection.Missing.Value;
wBrowser.Silent = true;
wBrowser.Navigate(validUri, ref empty, ref empty, ref empty, ref empty);
}
return null;
}
static void wBrowser_DownloadComplete(object sender, EventArgs e)
{
doAlgorithm();
}
static void wBrowser_DocumentComplete(object sender, DWebBrowserEvents2_DocumentCompleteEvent e)
{
doAlgorithm();
}
static void wBrowser_NewWindow3(object sender, DWebBrowserEvents2_NewWindow3Event e)
{
e.cancel = true;
}
static void wBrowser_NewWindow2(object sender, DWebBrowserEvents2_NewWindow2Event e)
{
e.cancel = true;
}
}
First, the thread in which the control is hosted must be in single-threaded apartment, you can either put the STAThread in your Main method, or create a separated Thread like this:
var thread = new Thread(() =>
{
//My code
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join(); //Wait for thread termination
Second, you must start a message loop:
while (true) //Put some exit condition
System.Windows.Forms.Application.DoEvents();
Third the control must be hosted in a visible form. The form must be visible just once, so to avoid "flickering", you can write this code:
var browser = new AxWebBrowser();
var hostForm = new Form();
//Set form 0 size, without any control box / title / icon
hostForm.Width = 0;
hostForm.Height = 0;
hostForm.ShowInTaskbar = false;
hostForm.ControlBox = false;
hostForm.ShowIcon = false;
hostForm.MinimizeBox = false;
hostForm.MaximizeBox = false;
//Add browser control
hostForm.Controls.Add(browser);
//Show and immediately hide
hostForm.Show();
hostForm.Hide();
Finally you might want to disable the "click" sound (How to disable click sound in WebBrowser Control)
The final code:
class Program
{
[STAThread]
static void Main(string[] args)
{
URLSecurityZoneAPI.InternetSetFeatureEnabled(URLSecurityZoneAPI.InternetFeaturelist.DISABLE_NAVIGATION_SOUNDS, URLSecurityZoneAPI.SetFeatureOn.PROCESS, true);
var browser = new AxWebBrowser();
var hostForm = new Form();
hostForm.Width = 0;
hostForm.Height = 0;
hostForm.ShowInTaskbar = false;
hostForm.ControlBox = false;
hostForm.ShowIcon = false;
hostForm.MinimizeBox = false;
hostForm.MaximizeBox = false;
hostForm.Controls.Add(browser);
hostForm.Show();
hostForm.Hide();
browser.DocumentComplete += delegate(object sender, DWebBrowserEvents2_DocumentCompleteEvent e)
{
var doc = (IHTMLDocument3)browser.Document;
Console.WriteLine(doc.documentElement.innerHTML);
};
browser.Navigate("www.google.com");
while (true)
System.Windows.Forms.Application.DoEvents();
}
}
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.