C# write into textbox (which is the Console Output) from Backgroundworker - c#

I am making a GUI to a bigger program.
In order to include a Abort button, i had to use Backgroundworker.
void _oWorker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Starter.Handling(cBLog, cBSelC, cBSelT);
if (_oWorker.CancellationPending)
{
e.Cancel = true;
return;
}
}
catch (Exception exp)
{
MessageBox.Show(exp.Message,"Exception Caught:");
}
}
Now however I have stumbled upon a bigger problem, not beeing able to access the Textbox which is loaded as follows:
private void Form1_Load(object sender, EventArgs e)
{
// Instantiate the writer
_writer = new TextBoxStreamWriter(txtConsole);
// Redirect the out Console stream
Console.SetOut(_writer);
}
The Problem is, the TextBox has to stay the Console Output since I don't want to change the subprograms (which also contain while loops).
Any suggestions?

Related

C# backgroundworker advice

I am having a backgroundworker issue - I am new to threading so I am trying to get this done as painlessly as possible.
My main issue is the end users will only have .NET 4.0 so I cannot use await / async and been told BGW is the best thing for the framework I am using.
I have a "Please Wait" form with a gif animation that I would like to load while the datagridview is being populated. I need to somehow do a check to make sure it has finished populating to close the "Please Wait" down but am a bit stuck on how to achieve this.
public void btnSearch_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
Application.DoEvents();
try
{
this.TestDataTableAdapter.Fill(this.TesteDataData.TestDataTable, txtHotName.Text, ((System.DateTime)(System.Convert.ChangeType(txtDepartFrom.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtDepartTo.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtBookFrom.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtBookTo.Text, typeof(System.DateTime)))));
int RowC = TestDataTableDataGridView.RowCount;
if (RowC == 0)
{
MessageBox.Show(GlobVar.NoResults, "", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
catch (System.Exception exc)
{
MessageBox.Show
(
"Problem" +
exc.Message, "An error has occured", MessageBoxButtons.OK, MessageBoxIcon.Warning
);
}
finally
{
//pleaseWait.Close();
}
This is my button to load the data in to my DataGridView. And so far this is my DoWork event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
pleaseWait.ShowDialog();
}
The finally will not work due to cross threading ( so comment out currently ) but I need to do a loop/check to find out if the DataGridView has been filled and action is complete to then close the DoWork. Or some how force it to jump to RunWorkerCompleted then I can just put a pleaseWait.Close(); in there instead.
Any suggestions please?
You must show your pleaseWait dialog in your main ui thread,not in the backgroundWorker1.DoWork, and hide it in the RunWorkerCompleted event of the backgroundWorker1. Is the this.TestDataTableAdapter.Fill part the one that should go in the backgroundWorker1.DoWork, so your code should look more or less like this:
public void btnSearch_Click(object sender, EventArgs e)
{
pleaseWait.ShowDialog();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
this.TestDataTableAdapter.Fill(this.TesteDataData.TestDataTable, txtHotName.Text, ((System.DateTime)(System.Convert.ChangeType(txtDepartFrom.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtDepartTo.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtBookFrom.Text, typeof(System.DateTime)))), ((System.DateTime)(System.Convert.ChangeType(txtBookTo.Text, typeof(System.DateTime)))));
}
catch (System.Exception exc)
{
//You can't show a messagebox here,as it is not in the UI thread
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pleaseWait.Close();
int RowC = TestDataTableDataGridView.RowCount;
if (RowC == 0)
{
MessageBox.Show(GlobVar.NoResults, "", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
Of course, in this code you have a problem, as the TestDataTableAdapter.Fill code won't work because you are trying to access some TextBoxes and you can't from another thread.
You have several solutions to this. You could use some variables to read the values before calling the backgroundworker, and accessing this variables instead of the TextBoxes. Or you could call the backgroundworker with parameters.
I recommend you to read more about BackGroundWorker, for example in MSDN. Or this question about sending parameters to a BackgroundWorker.

WPF C# Windows throws "Application has stopped working" upon Application.Current.Shutdown(); call

I'm in process of learning C# and WPF specifically. I have a program currently written but it's having an issue closing. When a user closes the main window, the program ends, but the process continues running in the background. Each time a new window is open, a new process is created and never ends. I attempted to fix this by adding the following code to my MainWindow.xaml.cs file:
private void InventoryUpdater_Closed(object sender, EventArgs e)
{
Application.Current.Shutdown();
}
This closes the process but causes a window to pop up that acts like the program shutdown incorrectly and prompting the user to "Close the program". That window states, "[Application Name] has stopped working.
How can I end my application process without it prompting the user each time they want to close? Is it possible I've not properly disposed something?
So I figured out what was causing the prompt and what was causing the unending process.
The prompt had to do with my opening and closing of the other 3 windows. Here is what I changed to stop getting the error from Application.Current.Shutdown(). I changed this code:
private void settingsWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
updateSettings();
e.Cancel = true;
main.Show();
Hide();
}
private void settingsButton_MouseUp(object sender, RoutedEventArgs e)
{
settingsWindow settingsWindow = new settingsWindow(this);
if (!settingsWindow.settingsOpen)
{
settingsWindow.Show();
Hide();
}
}
To this code:
private void settingsWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
updateSettings();
}
private void settingsButton_MouseUp(object sender, RoutedEventArgs e)
{
settingsWindow settingsWindow = new settingsWindow(this);
if (!settingsWindow.settingsOpen)
{
settingsWindow.ShowDialog();
}
}
Once I close main it was going back through the other window instances that I had initialized on load and was cycling through their Closing events. Once it was getting to main.Show(), it was failing to Show something that had already been closed.
Once I did that, I was about to figure out that the original initializing of the additional windows in my Loaded event was causing the additional trace backs that weren't ending. Here is what I changed. I changed this:
private void InventoryUpdater_Loaded(object sender, RoutedEventArgs e)
{
settingsWindow settings = new settingsWindow(this);
sftpSettings severSettings = new sftpSettings(settings);
TimeSpan na = queueCustomTask();
Stop = true;
runManually.IsEnabled = true;
ssdRunManually.IsEnabled = true;
startAutoRun.IsEnabled = true;
}
To this:
private void InventoryUpdater_Loaded(object sender, RoutedEventArgs e)
{
TimeSpan na = queueCustomTask();
Stop = true;
runManually.IsEnabled = true;
ssdRunManually.IsEnabled = true;
startAutoRun.IsEnabled = true;
}
I'm not even sure why the initializing was still there. Everything had been switched to static global variables so removing those two lines affected nothing. Thanks for all the shoves in the right direction!

I want to run a function when ever the form is clicked on taskbar and it opens

The issue doing so is i have used tips and tricks here but the seems to run in a loop and wont give the results.
Basically i run a BackgroundWorker to hit a url get result and paste result to some Labels.
I have used Form_Activated but it just keeps on running in a loop and wont stop ever reached to the BackgroundWorker completed event .
MAIN CODE BLOCKS:
On Form_Load I Run the Function and get the results and show:
private void Form1_Load(object sender, EventArgs e)
{
pictureBox1.Show();
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
else
{
MessageBox.Show("Thread already running....");
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
loadData(); // scrape a URL and paste info to Labels ....
}
This is it, now the user will minimize the application , now whenever he hits the the taskbar icon the form should rerun the same as in Form_Load. I hope that make sense , i have been able to do that using Form_Activate but it keeps going on .
Any suggestion how to get it done ?
I would store a boolean to remember if the form was minimized at the last FormResized event, and then if it was and if the form isn't currently minimized then call your method.
private bool minimized = false;
public void FormResized(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
minimized = true;
}
if (minimized && this.WindowState != FormWindowState.Minimized)
{
minimized = false;
MyMethod();
}
}

How can I restart a backgroundworker in a Windows Forms Application?

What my code basically does is it allows the user to browse for a folder that has a bunch of files, and when the user clicks a "Start" button, a code that makes files with the same name and different extensions for the ones in the folder runs. What I want to do is add a cancel button so if the user clicks the start button on a folder that was the incorrect one for example, the user can cancel out, browse to the correct folder, and restart the operation.
This is the code that fires when the start button is clicked:
class ExcelCode
{
public static void DoExcel (string FolderPath)
{
System.IO.StreamWriter file = new System.IO.StreamWriter(FolderPath + ".fdf");
Thread.Sleep(1000);
}
}
What I tried so far after hours upon hours of researching was to have the backgroundworker.CancelAsync(); called on the Cancel button, and a global variable that keeps track of the cancellation, ie. the DoWork() method looks like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if(backgroundWorker1.IsBusy)
for (int i = 0; i < 100; i++)
{
foreach (string file in filePaths)
{
if(backgroundWorker1.CancellationPending)
{
e.Cancel = true;
restartWorker = true;
return;
}
ExcelCode.DoExcel(file);
//write to textbox
}
backgroundWorker1.ReportProgress(i);
}
}
And my RunWorkerComplete method looks like this:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled && restartWorker)
{
if (backgroundWorker1.IsBusy)
{
restartWorker = true;
backgroundWorker1.CancelAsync();
return;
}
MessageBox.Show("Worker has cancelled");
restartWorker = false;
return;
}
}
Start Button:
private void button1_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
{
restartWorker = true;
backgroundWorker1.CancelAsync();
}
else
{
backgroundWorker1.RunWorkerAsync();
}
button2.Enabled = true;
}
Cancel Button:
private void button2_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
Now this all works well for cancelling out just once, when I try to start again on a different folder I get this error:
An exception of type 'System.IO.IOException' occurred in mscorlib.dll but was not handled in user code
Additional information: The process cannot access the file 'C:\Users\Pierre\Desktop\Test Folder - Copy\New Text Document - Copy - Copy - Copy - Copy - Copy (8).txt.fdf' because it is being used by another process.
The code has everything I researched on so it should pretty much show my entire progress on this. Any guidance as to what should be done is greatly appreciated.
EDIT: The code actually works for the second folder, that is it generates the file but throws the exception anyway.
You are not explicitly releasing the StreamWriter object, so it will only be released when GC kicks off on the object and releases the file. To avoid this issue, change your DoExcel method as follows:
public static void DoExcel (string FolderPath)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(FolderPath + ".fdf"))
{
Thread.Sleep(1000);
file.Close();
}
}

Pause and Resume a For-Loop in C#

I'm working on a windows App in C#, I have a for-loop which update something in a loop, and I have 3 buttons on the form named "Stop,Pause,Resume". So the purpose is as same as the buttons named. Does anyone know how to do this?
Here is the Loop
private void btnCompleteAuto_Click(object sender, EventArgs e)
{
setGeneralValue();
for (int i = 1; i <= autoGridView.Rows.Count - 1; i++)
{
if (SRP == "Pause") // this is what I was thinking but it won't work
{ // it will step into end-less loop
do // how to stop this loop on "Resume" button click
{
}while(SRP!="Resume")
}
car = false;
try
{
MemberID = Convert.ToInt64(autoGridView.Rows[0].Cells["Member_ID"].Value);
DispID = Convert.ToString(autoGridView.Rows[0].Cells["Disp_Id"].Value);
Mobile = Convert.ToString(autoGridView.Rows[0].Cells["Mobile"].Value);
DueDate = Convert.ToString(autoGridView.Rows[0].Cells["Due_Date"].Value);
}
catch (Exception)
{
MessageBox.Show("Row Not Found");
}
AutoRecharge(network_name, pack_name, Mobile, Mobile, Convert.ToString(autoGridView.Rows[0].Cells["Rck_Amt"].Value), vendor_id, vendor_pwd, pack_id, oxinetwork_id);
autoGridView.Rows.RemoveAt(0);
}
}
Here are the 3 button events in which I'm setting a variable
private void btnPause_Click(object sender, EventArgs e)
{
SRP = "Pause";
}
private void btnStop_Click(object sender, EventArgs e)
{
SRP = "Stop";
autoGridView.DataSource = "";
}
private void btnResume_Click(object sender, EventArgs e)
{
SRP = "Resume";
}
The reason this doesn't work as you expect is this:
A Windows Forms application uses a single UI thread, which continually processes incoming messages from a queue. Any event handlers you attach to the events of a Windows Forms control get sent to this queue and processed by the UI thread as quickly as possible.
Your btnCompleteAuto_Click is one such handler. Once it starts, nothing else will be processed by the UI thread until it exits. Thus any other handlers you attach to other events (btnPause_Click, btnStop_Click, etc.) must wait their turn, as they will run on the same (UI) thread.
If you want pause/resume functionality, this has to be achieved on a separate thread.
A possible way to implement it might be to use a BackgroundWorker, as suggested by saurabh.
Here is a rough sketch of what your updated code might look like (I have not even attempted to compile this, let alone debug it; it's intended only as a basic outline of how you might accomplish this functionality).
You need to be aware, however, that accessing UI controls directly from a non-UI thread is a no-no. Use a mechanism such as the BackgroundWorker.ProgressChanged event to handle any UI updates that you need to happen based on activity on a non-UI thread.
ManualResetEvent _busy = new ManualResetEvent(false);
private void btnCompleteAuto_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
{
_busy.Set();
btnAutoCompleteAuto.Text = "Pause";
backgroundWorker.RunWorkerAsync();
}
else
{
_busy.Reset();
btnAutoCompleteAuto.Text = "Resume";
}
btnStop.Enabled = true;
}
private void btnStop_Click(object sender, EventArgs e)
{
_busy.Set();
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// for (something)
// {
_busy.WaitOne();
if (backgroundWorker.CancellationPending)
{
return;
}
// Do your work here.
// }
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_busy.Reset();
btnAutoCompleteAuto.Text = "Start";
btnStop.Enabled = false;
}
After Reading your actual requirement in our comment , i would suggest that use Background worker class which supports cancellation of running process.
See here

Categories