I've been trying to figure out how to get my textbox's text or other property from within a background worker. Does anybody know how to do this? I cannot pass it as a param because it needs to be real-time. Thanks for the help!
I think you need to just invoke the property (pseudo-code):
private void bgw1_DoWork(object sender, DoWorkEventArgs e)
{
// looping through stuff
{
this.Invoke(new MethodInvoker(delegate { Text = textBox1.Text; }));
}
}
Use the ReportProgress method and event of the Background worker. That will switch to the correct thread for you.
Or if needed in WPF:
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
string text = null;
myTextBox.Dispatcher.Invoke(new Action(delegate()
{
text = myTextBox.Text;
}));
}
i think you should use invoke method.
here's my example.
delegate void myDelegate(string name);
//...
private void writeToTextbox(string fCounter)
{
if (this.InvokeRequired)
{
myDelegate textWriter = new myDelegate(displayFNums);
this.Invoke(textWriter, new object[] { fCounter });
}
else
{
textbox1.Text = "Processing file: " + fileCounter + "of" + 100;
}
}
//...
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//...
writeToTextbox(fileCounter.ToString());
}
in dowork i manipulate some textfile and i inform the user about how many files i have processed so far.
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 have a method in my class that has some loops inside.
Main purpose of this method is converting some files so I put a progressbar in my form that should get updated after each file has been converted.
I tried every possible combination and I read everything I could but I couldn't solve this issue.
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
converterProgressBar.Value = e.ProgressPercentage;
}
is called only after the main loop of my method has been executed.
This is my method:
public string Convert()
{
convertBtn.Enabled = false;
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
totalCount = files.length;
bw.RunWorkerAsync();
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
countFile++;
if (chk.Checked)
{
class1.DoJob();
}
using (// some code))
{
using (//some other code))
{
try
{
using (// again some code)
{
// job executing
}
}
catch (exception
{
}
}
}
convertedVideosL.Text = txtToUpdate;
convertedVideosL.Refresh();
}
countFile = countFile + 1;
MessageBox.Show("Done");
countFile = -1;
return outputFile;
}
And here are the BackgroundWorker Event Handlers:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= totalCount; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
}
else
{
int progress = Convert.ToInt32(i * 100 / totalCount);
(sender as BackgroundWorker).ReportProgress(progress, i);
}
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
converterProgressBar.Value = e.ProgressPercentage;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == false)
{
convertedVideosL.Text = "Finished!";
}
else
{
convertedVideosL.Text = "Operation has been cancelled!";
}
}
But I cannot get to update the progress bar for every file that is converted.
It waits for the foreach loop to end and then calls bw_ProgressChanged.
If I put RunWorkerAsync() inside the foreach loop an exception is thrown that says the BackgroundWorker is busy and cannot execute other tasks.
It seems to me obvious that DoWork() only executes a for loop then it shouldn't be aware of the conversion going on but ProgressChanged should be fired by ReportProgress(progress,i).
Could please someone explain me why and help me with a solution?
Thanks!
Currently the conversion is not executed by the instance of the BackgroundWorker type. The conversion should be called from the DoWork event handler.
Please consider extracting the conversion-related functionality:
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
// Details...
}
into the separate method. After that just call the method from the DoWork event handler.
Pseudo-code to demonstrate the idea:
public void StartConversion()
{
...
TWorkerArgument workerArgument = ...;
worker.RunWorkerAsync(workerArgument);
// No message box here because of asynchronous execution (please see below).
}
private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = Convert(worker, (TWorkerArgument)e.Argument);
}
private static TWorkerResult Convert(BackgroundWorker worker, TWorkerArgument workerArgument)
{
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
// Details...
worker.ReportProgress(percentComplete);
}
return ...;
}
private void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Show the message box here if required.
}
Please replace the TWorkerArgument and TWorkerResult types appropriately.
Also, please refer to the example which uses the BackgroundWorker class for the additional details: How to: Implement a Form That Uses a Background Operation, MSDN.
I've looked in many places for this but still haven't found a solution. What I'm trying to achieve is being able to use BackgroundWorker on a timed basis. Here's an example:
public Main()
{
isDbAvail = new BackgroundWorker();
isDbAvail.DoWork += isOnline;
isDbAvail.RunWorkerCompleted += rewriteOnlineStatus;
}
private void rewriteOnlineStatus(object sender, RunWorkerCompletedEventArgs e)
{
Subs.Connection connection = new Subs.Connection();
changeStatus(connection.isDbAvail());
}
private void isOnline(object sender, DoWorkEventArgs e)
{
while (true)
{
Console.WriteLine("Checking database connection");
System.Threading.Thread.Sleep(8000);
}
}
public void changeStatus(bool status)
{
if (status)
{
serverStatusVal.Text = "Connected";
serverStatusVal.ForeColor = System.Drawing.Color.DarkGreen;
}
else
{
serverStatusVal.Text = "Not connected";
serverStatusVal.ForeColor = System.Drawing.Color.Red;
}
}
What's happening here is that the isOnline method checks if there is a connection to the database (just an example) every 8 seconds and changes the text accordingly. What I've noticed though, is that the while loop inside the isOnline method causes the rewriteOnlineStatus method never to fire because it runs indefinitely. Is there another workaround to this?
I suggest you use BackgroundWorker.ReportProgress, and check connectivity in the background thread.
Something like this:
public Main()
{
isDbAvail = new BackgroundWorker();
isDbAvail.WorkerReportsProgress = true;
isDbAvail.DoWork += isOnline;
isDbAvail.ProgressChanged += rewriteOnlineStatus;
isDbAvail.RunWorkerAsync();
}
private void rewriteOnlineStatus(object sender, ProgressChangedEventArgs e)
{
changeStatus((bool)e.UserState);
}
private void isOnline(object sender, DoWorkEventArgs e)
{
while (true)
{
Console.WriteLine("Checking database connection");
Subs.Connection connection = new Subs.Connection();
isDbAvail.ReportProgress(0, connection.isDbAvail);
System.Threading.Thread.Sleep(8000);
}
}
Now the BackgroundWorker is doing the work, and reporting back to the UI thread via ProgressChanged.
I have a deserialize function called DeSerializeXML that returns a string. I am calling it from a different class I need it to run based on a Timer so I have it being called in the ElapsedEvent for the Timer
private void frm_MyForm_Load(object sender, EventArgs e)
{
System.Timers.Timer myTimer = new Timer(2000);
myTimer.Elapsed += new ElapsedEventHandler(ElapsedEvent);
myTimer.Enabled = true;
}
public static void ElapsedEvent(object source, ElapsedEventArgs e)
{
string x = OtherClass.DeSerializeXML();
}
How can i pull that string value out of the ElapsedEvent so I can assign it to a textbox on the MyForm form?? I apologize if this is a poorly worded question, I will add comments/more code as need be. Thanks all
Using Control.Invoke Method
public void ElapsedEvent(object source, ElapsedEventArgs e)
{
string x = OtherClass.DeSerializeXML();
frm.Invoke((Action)(() => textBox.Text = x);
}
Note: Remove static keyword from the method's signature
Try setting the returned string as Text property of text-box.
textBox1.Text = OtherClass.DeSerializeXML();
If you get any complications regarding non-UI thread try using Invoke. Like this:
var txt = OtherClass.DeSerializeXML();
if (InvokeRequired)
{
//this is the Form
this.Invoke(new Action<string>(textBox1), new object[] {txt});
return;
}
textBox1.Text = txt;