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.
Related
I have a parent form that send a data table to a child form by Delegate.
the Delegate is executed and i have also gotten the the table on the child side.
i want to append a text into my richtextbox control to announce the user what is going on and then run a backgroundworker. but i get the STAThread Exception. i know some thing about Invoke(Delegate) and about single-Thread but i do not know how can i overcome to this cross-threading. Any help is appreciated.
The codes from Appent To RichTextBox are not execute with debugging (i know it is possible with run of the *.exe file).
//What i am doing and trying:(SetDaTableAndFileNameFn is my received Delegate)
public void SetDaTableAndFileNameFn(System.Data.DataTable DataTable)
{
//Test The Parent Has Sent And Child Has Received.
MessageBox.Show("Ruger Parent...");
dt.Clear();
dt = DataTable;
//Check whether My dt Filled Correctly.
MessageBox.Show(dt.Rows[2][2].ToString());
richTxtBxExprtr.AppendText(">>> Creating And Transferring Data To The File...");
//BGWorker.
bGWExprtrLod.WorkerReportsProgress = true;
bGWExprtrLod.RunWorkerAsync();
}
private void ExportToTxtIrrigularly(System.Data.DataTable DataTable)
{
// Using Microsoft.Office.Interop.Word.Application to export datatable.
}
private void xBtnExprt_Click(object sender, EventArgs e)
{
SaveFileDialog svFDialXls = new SaveFileDialog();
svFDialXls.Filter = "Plain text(*.txt)| *.txt";
svFDialXls.Title = "Export Data As Text";
svFDialXls.InitialDirectory = #"Desktop";
if (svFDialXls.ShowDialog() == DialogResult.OK && svFDialXls.FileName != null)
{
WordFilePath = svFDialXls.FileName.ToString();
//Fire An EventHandler In The Parent Side To Fill A datatable With A DGV.
ExportImport ExportTxFile = new ExportImport(allRowsExprt, fRowTEndExprt, fRowTEndValExprt, FTRowExprt,
FTRowValFExprt, FTRowValTExprt, allFieldsExprt, visFieldExprt, slctdFieldExprt);
OnExportTxFile(ExportTxFile);
//Filled datatable Will Send Back from parent side with theSetDaTableAndFileNameFn
}
}
private void bGWExprtrLod_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke(new Action(() =>
{
richTxtBxExprtr.AppendText(">>> Start Processing...\n>>> Copying Data Take A Little Time.\n>>> Be Patient...\n>>> Loadind Data...\n-----------------------------------------------\n");
ExportToTxtIrrigularly(dt);
}));
}
private void bGWExprtrLod_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Invoke(new Action(() =>{richTxtBxExprtr.AppendText(">>> Line NO. [" + e.ProgressPercentage.ToString() + "] Is In Progress...\n");
richTxtBxExprtr.ScrollToCaret();
}));
}
private void bGWExprtrLod_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{this.Invoke(new Action(() => { richTxtBxExprtr.AppendText(">>> The Process Is Completed Successfuly.\n"); }));
}
the executed mthod in the BGWorker will open a savedialogbox() and then export my datatable to a text format using
Microsoft.Office.Interop.Word.Application
Not really sure what kind of setup you've got, but it will look something like this:
private BackgroundWorker bGWExprtrLod;
private void Form1_Load(object sender, EventArgs e)
{
bGWExprtrLod = new BackgroundWorker();
bGWExprtrLod.WorkerReportsProgress = true;
bGWExprtrLod.ProgressChanged += BGWExprtrLod_ProgressChanged;
bGWExprtrLod.RunWorkerCompleted += BGWExprtrLod_RunWorkerCompleted;
bGWExprtrLod.DoWork += BGWExprtrLod_DoWork;
}
public void SetDaTableAndFileNameFn(System.Data.DataTable DataTable)
{
// ... other code ...
bGWExprtrLod.RunWorkerAsync();
}
private void BGWExprtrLod_DoWork(object sender, DoWorkEventArgs e)
{
bGWExprtrLod.ReportProgress(0, ">>> Creating And Transferring Data To The File...");
// ... do some work ...
}
private void BGWExprtrLod_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string msg = e.UserState.ToString();
richTxtBxExprtr.AppendText(msg);
}
private void BGWExprtrLod_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
richTxtBxExprtr.AppendText("Transfer Complete!");
}
** EDIT **
Start by moving the call to you export method outside the Invoke() call:
private void bGWExprtrLod_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke(new Action(() =>
{
richTxtBxExprtr.AppendText(">>> Start Processing...\n>>> Copying Data Take A Little Time.\n>>> Be Patient...\n>>> Loadind Data...\n-----------------------------------------------\n");
}));
ExportToTxtIrrigularly(dt);
}
Unfortunately I was not able to find relevant answer to my problem. I have a object encoder that has an event "VideoEncoding". It passes custom EncodingEventArgs that include various Properties like Progress, Size etc. I can output this info to Console or write to text file. But when I try to utilize it in WinForms I'm not able to pass that information to UI like label or progress bar. I tried different approaches. Background Worker seems like a good idea, The problem is that Background Worker cannot subscribe to VideoEncoding event, neither it will take my custom EventArgs. This is what i was able to put together. Maybe there is a different way to do it using delegates that would communicate with UI. Any suggestions are welcome. Thank you.
public partial class Form1 : Form
{
private BackgroundWorker bw;
int _progress;
public Form1()
{
InitializeComponent();
this.bw = new BackgroundWorker();
this.bw.DoWork += new DoWorkEventHandler(bw_DoWork);
this.bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
this.bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
this.bw.WorkerReportsProgress = true;
this.button1.Click += new EventHandler(button1_Click);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.label1.Text = "The job is: " + e.Result.ToString();
this.button1.Enabled = true;
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.label2.Text = e.ProgressPercentage.ToString() + "% complete";
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
this.Encode
worker.ReportProgress(_progress);
e.Result = "Completed";
}
private void button1_Click(object sender, EventArgs e)
{
if (!this.bw.IsBusy)
{
this.bw.RunWorkerAsync();
this.button1.Enabled = false;
}
}
public void Encode()
{
var job = new EncodingJob();
//setup encoding job
//subscribe to an event
ffmpeg.VideoEncoding += GetProgress;
ffmpeg.DoWork(job);
}
public void GetProgress(object sender, EncodingEventArgs e)
{
_progress = (int)e.Progress;
}
}
Try to call the background workers ReportProgress in the GetProgress Method. How should the form know your progress if you don't signalize it?
I'm working on a downloader manger to my app but I couldn't know how to make the Stop button work I search for solution but I couldn't find anything can help me
The code is
[c#]
private void btnDownload_Click(object sender, EventArgs e)
{
btnDownload.Enabled = false;
btnStop.Enabled = true;
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
webClient.DownloadFileAsync(new Uri(url.Text), path.Text ; )
}
private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
private void Completed(object sender, AsyncCompletedEventArgs e)
{
MessageBox.Show("Download completed!");
}
private void btnstop (object sender , e)
{
btnDownload.Enabled = true;
btnstop.Enabled = false;
progressbar.value = 0;
}
As Nitro.de says, you should use WebClient.CancelAsync.
Cancels a pending asynchronous operation.
and remember checking if the e.Cancelled is true
private void Completed(object sender, AsyncCompletedEventArgs e)
{
if(e.Cancelled)
MessageBox.Show("Download cancelled!");
else
MessageBox.Show("Download completed!");
}
To cancel a WebClient async request, you can call the WebClient.CancelAsync method as found here. Note that this still calls the download completed handler, so you will have to check if e.Canceled is true in your Completed function before showing the message box.
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;
}
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