Carry on with UI while waiting for WebClient - c#

I'm trying to get a progress bar to show the program is loading (using ProgressStyle.Marquee) while a WebClient object reads from a site. The problem is that upon clicking the button that begins the call, the entire UI freezes up. I've tried putting it in a BackgroundWorker doWork but that causes it to never get any data at all. I've also tried delegates and various Thread options but all seem to have the same issue.
I'm fairly new to C# and have mostly been trying results I've found on the web but none seem to solve my problem.
What I have:
private void getInfoButton_Click(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
getInfoButton.Enabled = false;
StreamReader reader = new StreamReader(client.OpenRead("URI here"));
while ((line = reader.ReadLine()) != null) {
// Do stuff
}
progressBar1.Style = ProgressBarStyle.Continuous;
getInfoButton.Enabled = true;
}
My attempt with BackgroundWorker
private void getInfoButton_Click(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
getInfoButton.Enabled = false;
BackgroundWorker bw = new BackgroundWorker();
bw.DoWord += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync("URI here");
while (bw.IsBusy)
;
progressBar1.Style = ProgressBarStyle.Continuous;
getInfoButton.Enabled = true;
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
WebClient client = new WebClient();
StreamReader reader = new StreamReader(e.Argument);
while ((line = reader.ReadLine()) != null) {
// Do stuff
}
}

After looking at my BackgroundWorker attempt I realised it was defeating the point of asynchronicity and the getInfoButton_Click method would be stalling (effectively freezing the UI) until it completed. Playing around with the BackgroundWorker some more I managed to get it to work:
private void getInfoButton_Click(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
getInfoButton.Enabled = false;
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync("URI Here");
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
WebClient client = new WebClient();
StreamReader reader = new StreamReader(client.OpenRead(e.Argument));
e.Result = reader;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
StreamReader reader = (StreamReader)e.Result;
while ((line = reader.ReadLine()) != null) {
// Do stuff
}
progressBar1.Style = ProgressBarStyle.Continuous;
getInfoButton.Enabled = true;
}

Related

Background worker not starting

I've implemented a background worker onLoad of my window. The code in the Progress_Load is reached but after that the DoWork function is not called. The function excel.Read() reads a quite big excel tabel into a list, this takes about 1.5 min and that's why i want to do it a-syn.
public List<Part> partList = new List<Part>() { };
//boolean that will be set when the backgroundworker is done
public bool listRead = false;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_Excel excel = new _Excel();
partList = excel.Read();
backgroundWorker1.ReportProgress(100);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Message m;
if (e.Cancelled == true)
{
m = new Message("The operation has been canceld!", "Canceled");
this.Close();
}
else if (e.Error != null)
{
Error er = new Error("Error: " + e.Error.Message, "Error!");
this.Close();
}
else
{
listRead = true;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
//Set the text.
this.Text = e.ProgressPercentage.ToString();
}
private void Progress_Load(object sender, EventArgs e)
{
if (backgroundWorker1 == null)
{
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerAsync();
}
Probably it's not null when loading the form. You may have added the BackgroundWorker via the designer. If so it's never null, you can hook up the event handlers also from its Properties/Events.
Try this
private void Progress_Load(object sender, EventArgs e)
{
if (backgroundWorker1 == null)
{
backgroundWorker1 = new BackgroundWorker();
}
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerAsync();
}
In WPF Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction); is a another option because report progress is not much useful in your scenario. Here example for Dispatcher and Background worker comparison.

BackgroundWorker doesn't trigger DoWork_Event

BackgroundWorker worker = new BackgroundWorker();
private void btn_Start_Click(object sender, EventArgs e)
{
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.WorkerReportsProgress = true;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
using (WebClient webclient = new WebClient())
{
using (SPSite site = new SPSite(tb_sourceSite.Text))
{
using (SPWeb web = site.OpenWeb())
{
webclient.Credentials = new NetworkCredential(tb_NC_Username.Text, tb_NC_Password.Text, tb_NC_Domain.Text);
SPList list = web.Lists[tb_sourceList.Text];
SPQuery qry = new SPQuery();
qry.Query = "<OrderBy><FieldRef Name='ID' /></OrderBy>";
qry.RowLimit = Convert.ToUInt32(NumberOfImages.Value);
SPListItemCollection items = list.GetItems(qry);
int i = 0;
progressBar1.Maximum = items.Count;
foreach (SPListItem item in items)
{
string filePathUrl = String.Concat(web.Url, "/", item.Url);
webclient.DownloadFile(filePathUrl, tb_DownloadFolder.Text);
worker.ReportProgress(i);
i++;
}
}
}
}
}
The Startbutton works, i can debug into it.
But nothing happens in Backgroundworker.
It doesn't download anything oder changes anything.
I am new to Backgroundworker. Am I doing something wrong?
while debugging you might get cross-thread error. Try by creating a delegate to set label text. The below code is just a short example how to use BackgroundWorker with delegate.
delegate void SetLabel(string msg);
BackgroundWorker worker = new BackgroundWorker();
private void btn_Start_Click(object sender, EventArgs e)
{
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.WorkerReportsProgress = true;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke(new SetLabel(SetLabelMethod), new object[]{
"Connecting..."})
}
void SetLabelMethod(string msg)
{
lbl_status.Text = msg;
}

BackgroundWorker WPF difficulties with Progress Bar

I am developing a WPF, C# application and I have to read a very big CSV file and write to a database. This process takes a long time so I want to at least show a progress bar that grows in size as it nears completition.
Now, I have the following code at the top:
private readonly BackgroundWorker worker = new BackgroundWorker();
Then inside the loop I have this:
worker.WorkerReportsProgress = true;
worker.ReportProgress((100 * i) / 10000);
and I have a private sub like this:
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
prgBar.Value = Math.Min(e.ProgressPercentage, 100);
}
Yet I don't see the progress bar updating or anything happening, program still hangs. Am I doing something wrong?
UPDATE: Tried the MSDN guide here http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx and I somehow failed to get it working with that example. I adjusted the looping part of course. Heh. It still stays idle and nothing updates. Heck, all I want to do is get a small counter that increases every time a line is read and added.
UPDATED CODE:
private BackgroundWorker bw = new BackgroundWorker();
public Process()
{
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Perform a time consuming operation and report progress.
int d = 0;
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Set filter for file extension and default file extension
dlg.DefaultExt = ".csv";
dlg.Filter = "CSV File (*.csv)|*.csv";
dlg.Title = "file";
// Display OpenFileDialog by calling ShowDialog method
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
// Open document
string filename = dlg.FileName;
List<String[]> fileContent = new List<string[]>();
using (FileStream reader = File.OpenRead(#filename)) // mind the encoding - UTF8
using (TextFieldParser parser = new TextFieldParser(reader))
{
using (SqlConnection conn = new SqlConnection(Classes.PublicVariables.Connection))
{
parser.Delimiters = new[] { "," };
parser.HasFieldsEnclosedInQuotes = true;
while (!parser.EndOfData)
{
string[] line = parser.ReadFields();
fileContent.Add(line);
SqlCommand comm = QUERYANDPARAMETERS
d += 1;
comm.ExecuteNonQuery();
worker.ReportProgress((d * 10));
}
}
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblCount.Content = "Complete";
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.lblCount.Content = (e.ProgressPercentage.ToString() + "%");
}
I am getting an excerption when the worker runs.
An exception of type 'System.NullReferenceException' occurred inSolution.exe but was not handled in user code
Additional information: Object reference not set to an instance of an object.
UPDATE3
Got it working! All I needed was the same code as above but this:
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
else
{
bw_DoWork(null, null);
}
In the event handler for the button press.
This will not compile as-is, but should get you started in the right direction:
private readonly BackgroundWorker worker
= new BackgroundWorker { WorkerReportsProgress = true };
public MainWindow()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
}
private void worker_DoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
// Do some long process, break it up into a loop so you can periodically
// call worker.ReportProgress()
worker.ReportProgress(i); // Pass back some meaningful value
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
prgBar.Value = Math.Min(e.ProgressPercentage, 100);
}
is the varible i int? if it is, the value of (100 * i) / 10000 will return 0 while i < 100. and the param of ReportProgress method should be a precent value. you can change it to worker.ReportProgress(i);,try it.

UI hangs when long search loop executed in c#

in my Winform a read file method is implemented on button click.when big files are read my Ui hangs until the loop is over.I need to have control on my UI all the time.
i know that is done before and i tried some but i am still having an
error when i try to use some form controls like this :(translated!)
the access of control element comboBox1 is from another thread rather than the thread in which it is created in !!!
private void button1_Click(object sender, EventArgs e)
{
//some code
using (StreamReader sr = new StreamReader(file, System.Text.Encoding.ASCII))
{
while (sr.EndOfStream == false)
{
line = sr.ReadLine();
UpdateProgressBar(line.Length);
}
}
//some code
}
Add a BackgroundWorker to your class on Form (or Control) load. Then hookup its events:
BackgroundWorker _worker;
void Form_Load(object sender, EventArgs e)
{
_worker = new BackgroundWorker();
_worker.DoWork += _worker_DoWork;
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
_worker.ProgressChanged +=_worker_ProgressChanged;
_worker.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
_worker.RunWorkerAsync(file);//pass on the file name
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
var file = e.Argument as String;
using (StreamReader sr = new StreamReader(file, Encoding.ASCII))
{
while (sr.EndOfStream == false)
{
line = sr.ReadLine();
_worker.ReportProgress(line.Length);
}
}
}
private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Report porogress bar change
UpdateProgressBar(e.ProgressPercentage);
}
private void _worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//do any stuff you want after reading the file.
}
Read more about this here.
Use a BackgroundWorker to do the work on a separate thread.
Use BackgroundWorker
[edit] Tutorial available here
You really should have consulted the documentation before asking a question. Anyways, here's an example of how you can do it using a BackgroundWorker:
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker;
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
worker.DoWork += ReadStream;
worker.RunWorkerAsync(comboBox1.Text);
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UpdateProgressBar(e.ProgressPercentage);
comboBox1.Text = e.UserState.ToString();
}
private void ReadStream(object sender, DoWorkEventArgs doWorkEventArgs)
{
BackgroundWorker worker = sender as BackgroundWorker;
string line;
string comboBoxText = doWorkEventArgs.Argument.ToString();
using (StreamReader sr = new StreamReader("file", System.Text.Encoding.ASCII))
{
while (!sr.EndOfStream)
{
line = sr.ReadLine();
worker.ReportProgress(line.Length);
worker.ReportProgress(line.Length, "NEW COMBOBOX TEXT");
}
}
}
use backgroundworker to carry out heavy operations.
it has event to report progress of the task , so you can use it to update progress bar.

How to pass an array as a parameter for doWork in C#

I need to use form controls like comboBox1.text and comboBox2.Text inside the readstream function an this deliver an error message (translated from German):
The access of control element comboBox1/comboBox2 is from another thread rather than the thread in which it is created in !!!
What I need is to pass these controls to the readstream function, but I don't know how exactly.
Code
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker;
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
worker.DoWork += ReadStream;
//Need to pass the comoBox Texts from here!!!
string start = comboBox1.Text;
string end = comboBox2.Text;
worker.RunWorkerAsync();
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UpdateProgressBar(e.ProgressPercentage);
comboBox1.Text = e.UserState.ToString();
}
private void ReadStream(object sender, DoWorkEventArgs doWorkEventArgs)
{
BackgroundWorker worker = sender as BackgroundWorker;
string line;
//And use the values here !!!!
using (StreamReader sr = new StreamReader("file", System.Text.Encoding.ASCII))
{
while (!sr.EndOfStream)
{
line = sr.ReadLine();
worker.ReportProgress(line.Length);
}
}
}
Before you call worker.RunWorkerAsync();, do this:
string[] texts = new string[] {start, end};
worker.RunWorkerAsync(texts);
Then, in ReadStream(...)
string[] extracted = (string[])doWorkEventArgs.Argument;
string start = extracted[0];
string end = extracted[1];
Try this code to pass array as parameter:
worker.RunWorkerAsync(array);
Use this code to get this array:
doWorkEventArgs.Argument

Categories