I want to:
Show a form with a textbox.
Run an external program (notepad.exe for ease of example).
Continue to allow the user to enter data into the form textbox whilst notepad is running.
Run some more (continue) native form code when notepad closes. This will update the form, amongst other things.
I'm having problems making this happen. I'm aware of a multitude of posts about this similar issue, but haven't found a solution that works for me.
I have tried:
Doing a waitforexit, but this of course blocks the UI and users cannot enter data.
Attempting an asynchronous process call, where another method is called when this process is completed. This causes a problem where the new method is called from another thread and can't update the form.
Doing a wait/sleep loop in the UI, but again this will naturally block the UI.
What would be the neatest, and simplest solution for a simple Windows Form program? There are no extra classes used, and all code is in the Form1 class.
The Process class fires an Exited event when the process exits. You can add a handler to that event to execute code when the process exits without blocking the UI thread:
process.EnableRaisingEvents = true;
process.Exited += (s, args) => DoStuff();
Alternatively you could create a Task that represents the completion of the process to leverage the TPL for asynchrony:
public static Task WhenExited(this Process process)
{
var tcs = new TaskCompletionSource<bool>();
process.EnableRaisingEvents = true;
process.Exited += (s, args) => tcs.TrySetResult(true);
return tcs.Task;
}
This would allow you to write:
await process.WhenExited();
UpdateUI();
Here you go:
void Form1_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
var p = Process.Start("notepad.exe");
p.WaitForExit();
}).ContinueWith(antecedant => { MessageBox.Show("Notepad closed"); });
}
Here is my favorite way to do something like this with a BackgroundWorker. This has the advantage of the RunWorkerCompleted callback being on the main thread, so it can interact with the UI.
public partial class Form1 : Form
{
...
private BackgroundWorker wrk;
private void button1_Click(object sender, EventArgs e)
{
wrk = new BackgroundWorker();
wrk.DoWork += (s, ea) => { /*Create your process and wait here*/ };
wrk.RunWorkerCompleted += (s, ea) => { textBox1.Text = "Finished"; };
wrk.RunWorkerAsync();
}
}
You should start process in BackgroundWorker so you can catch complete event on same thread.
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate {
Process proc = Process.Start("YOUR-PROCESS-PATH");
proc.Start();
proc.WaitForExit();
}
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
then catch the worker ended event on called thread;
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Do your thing o UI thread
}
Related
I'm trying to execute a thread without blocking UI , I've used this code but when I execute my application , it won't execute the thread and nothing is shown after clicking on DoButton event
public void DoThread()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyFunctionDoThread;
var frame = new DispatcherFrame();
worker.RunWorkerCompleted += (sender, args) => {
frame.Continue = false;
};
worker.RunWorkerAsync();
Dispatcher.PushFrame(frame);
}
private void Dobutton_Click(object sender, RoutedEventArgs e)
{
DoThread(); // Process will be executed
}
public void MyFunctionDoThread()
{
// Some Tasks
ProcessStartInfo startInfo = new ProcessStartInfo();
Process.Start(startInfo);
// ...
}
How I can perform a task ( thread ) without blocking the UI?
You should really use Task/async/await for any background work. BackgroundWorker is rather old.
public async void Dobutton_Click(object sender, RoutedEventArgs e)
{
try{
var result = await Task.Run(MyFunctionDoThread);
// Update the UI, or otherwise deal with the result
}
catch{
// deal with failures, like showing a dialog to the user
}
}
how can I use it , the await require to return task action
await requires the method to be marked with async, it does not require the method to return a task. It is a guideline to return a task, so that the caller can deal with any failures. But for things like button event handlers you are at the end of the line, there is no one else to deal with any failure, so you should instead make sure you do it yourself with a try/catch.
While developing a solution on Windows Forms I went into a routine of showing continuous progress to user. I implemented simple dummy window with continuous progress bar:
In solution tree it is situated on the same level as the Main Window:
The simplest working approach to show continuous progress while doing something is the following code. It does work:
//This method works
private void DoSomeBackgroundStuffWithShow()
{
ContinuousProgressWindow continuousProgressWindow =
new ContinuousProgressWindow();
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, arguments) =>
{
//Do some stuff for 4 seconds
Thread.Sleep(4000);
};
backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
{
//Window is closed when needed. Great!
continuousProgressWindow.Dispose();
};
continuousProgressWindow.Show(this);
backgroundWorker.RunWorkerAsync();
}
But I need this window to appear topmost and block its parent while working. The following code is quite similar, and it does not work - the dialog is shown, but never closed:
//This method DOES NOT WORK
private void DoSomeBackgroundStuffWithShowDialog()
{
ContinuousProgressWindow continuousProgressWindow =
new ContinuousProgressWindow();
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, arguments) =>
{
//Do some important stuff for 4 seconds
Thread.Sleep(4000);
};
backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
{
//None of the following work for "ShowDialog() method"
//Ran with debugger - breakpoints not hit!
continuousProgressWindow.DialogResult = DialogResult.OK;
continuousProgressWindow.Close();
continuousProgressWindow.Dispose();
};
continuousProgressWindow.ShowDialog(this);
backgroundWorker.RunWorkerAsync();
}
Then, I realize the problem is about UI threads flow: when the progress window is ran as a dialog, MainWindow thread is frozen and it cannot be invoked by BackgroundWorker in RunWorkerCompleted delegate to close the dialog.
What is the simplest solution to make it work as wanted?
continuousProgressWindow.ShowDialog(this);
backgroundWorker.RunWorkerAsync();
You've got a simple chicken-and-egg problem, you don't start the worker until after the dialog closes. ShowDialog() is a blocking call. So the RunWorkerCompleted event doesn't fire because the worker didn't get started. The simplest workaround is to swap the two statements:
backgroundWorker.RunWorkerAsync();
continuousProgressWindow.ShowDialog(this);
That is not entirely safe to do. Not a problem with this snippet but in real code there is a danger that the worker completes before the dialog is displayed. Low odds but not zero. To solve that, you want to delay the worker until you are sure the dialog is up and running. That can be done with an AutoResetEvent that is Set() by the dialog's OnShown() method. Or, more elegantly, by taking advantage of a trick:
this.BeginInvoke(new Action(() => backgroundWorker.RunWorkerAsync()));
continuousProgressWindow.ShowDialog(this);
The delegate target of Control.BeginInvoke() runs when the program re-enters the message loop. That happens after the dialog becomes visible :)
The issue here is that you are calling continuousProgressWindow.ShowDialog(this) before backgroundWorker.RunWorkerAsync(). So backgroundWorker.RunWorkerAsync() will be called once you close the window.
I think following code should work, as suggested by #Steven Mills also.
private void DoSomeBackgroundStuffWithShowDialog()
{
ContinuousProgressWindow continuousProgressWindow =
new ContinuousProgressWindow();
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, arguments) =>
{
//Do some important stuff for 4 seconds
Thread.Sleep(4000);
};
backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
{
//None of the following work for "ShowDialog() method"
//Ran with debugger - breakpoints not hit!
continuousProgressWindow.DialogResult = DialogResult.OK;
continuousProgressWindow.Close();
continuousProgressWindow.Dispose();
};
backgroundWorker.RunWorkerAsync();
continuousProgressWindow.ShowDialog(this);
}
I'm running some scripts in runtime, but it's freezing my UI, I'm calling the CodeProvider inside a Thread, but it still freezing.
In my form I call:
var mre = new ManualResetEvent(false);
Thread tr = new Thread(() =>
{
Script sp = new Script();
code = textBox.Text;
sp.Comp(code);
mre.Set();
});
tr.Start();
mre.WaitOne();
I'm using the mre.WaitOne() because I want to wait the thread finish to keep running my code.
Tried to use the same way inside the Compile method too:
public bool Comps(string code)
{
var mre = new ManualResetEvent(false);
Thread tr = new Thread(() =>
{
//Code to generate a CompilerResult and generate the assembly
Run();
mre.Set();
});
tr.Start();
mre.WaitOne();
return true;
}
But while it's waiting it still freezing the UI.
Any ideas?
Thanks
I'm using the mre.WaitOne() because I want to wait the thread finish
to keep running my code.
What did you expect to happen if you force the calling thread to freeze until your processing thread has completed processing? Doing it this way, there is no point in having that extra thread and if the calling thread is the UI thread, of course it will freeze.
If you do background processing you cannot wait for the result synchronously, instead you have to notify the UI in some sort of fashion that the processing is done, i.e. using a callback or dispatching the result back to the UI in some other form.
The entire point of multi-threading is to allow the Thread to execute on it's own, independent of any other threads. What you want to do is use a callback to signal the completion of your thread and then have your UI respond to the completion.
The BackgroundWorker class has an event already built in for this purpose.
There are three events you want to subscribe to:
bw.DoWork +=
new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged +=
new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
DoWork is where your work will happen. ProgressChanged allows you to update the UI of progress. RunWorkerCompleted will pop the event with your DoWork function has completed.
This object handles the threading and can be set to run asynchronously by running the bw.RunWorkerAsync() call.
See the following page for detail for this:
http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
As an example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(String.Format("UI thread: {0}", Thread.CurrentThread.ManagedThreadId));
this.Invoke(new MethodInvoker(delegate() { MessageBox.Show(String.Format("Invoke thread: {0}", Thread.CurrentThread.ManagedThreadId)); }));
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
MessageBox.Show(String.Format("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId));
}
}
This example can be built by adding one button and one background worker to a form. Wire up the events through the events designer for the button1_Click and the backgroundWorker1_DoWork function. You should have three MessagesBoxes that pop up after clicking button1. You'll notice the Id for the UI thread and the Invoke thread are the same, which means that any processing you do from the invoke will cause your UI thread to wait. The third popup is from the worker thread, which has a different ID.
Use BeginInvoke when done. For example:
delegate void MyAction();
void Form1_Load( object sender, EventArgs e )
{
Thread tr = new Thread( () =>
{
Script sp = new Script();
code = textBox.Text;
sp.Comp(code);
BeginInvoke( new MyAction( ThreadOperationEnded ) );
} );
tr.Start();
}
void ThreadOperationEnded()
{
MessageBox.Show( "Finished!" );
}
I'm currently writing a little GUI program that does some work and exits afterwards. While work is done, the GUI thread is updated with infos for the user.
This is the pattern I'm currently using and I'm thinking it's not the most elegant one:
static void MainForm_Loaded(BeoExport exporter)
{
// Thread 1 runs the Export
workerThread = new Thread(() =>
{
exporter.StartExport();
// don't exit immediately, so the user sees someting if the work is done fast
Thread.Sleep(1000);
});
// Thread 2 waits for Thread 1 and exits the program afterwards
waiterThread = new Thread(() =>
{
workerThread.Join();
Application.Exit();
});
workerThread.Start();
waiterThread.Start();
}
So what pattern/mechanics would you use to do the same?
To clarify: I was not interested in a way to update the GUI thread. That's already done. This might sound esoteric but I was lookig for the right way to quit the application.
If I could, I would give Dave the credits, since he pointed out the usefulness of the BackgroundWorker.
Have you considered a BackgroundWorker thread instead? You can use its ReportProgress method and ProgressChanged event to update the GUI (with a progress bar perhaps), assuming that you can refactor BeoExport.StartExport method to also report progress. This gives the users visible feedback that work is actually happening.
I don't understand why do you use two threads. You can use threadpool:
ThreadPool.QueueUserWorkItem((state)=>{
exporter.StartExport();
Thread.Sleep(1000);
Application.Exit();
});
I suggest you to use the BackgroundWorker class. It's thought to do the kind of job you're doing. You could do domething like this:
public class Form1 : Form
{
private BackgroundWorker worker;
private ProgressBar bar;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
bar= new ProgressBar();
bar.Dock = DockStyle.Top;
Controls.Add(bar);
worker = new BackgroundWorker();
worker.WorkerReportsProgress=true;
worker.RunWorkerCompleted += delegate
{
Close();
};
worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs ev)
{
bar.Value = ev.ProgressPercentage;
};
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//do your work here. For the example, just sleep a bit
//and report progress
for (var i = 0; i < 100;i++ )
{
Thread.Sleep(50);
worker.ReportProgress(i);
}
}
}
You can use an AutoResetEvent. The main thread waits for the autoreset event to be reset.
var wh = new AutoResetEvent(false);
var workerThread = new Thread(() =>
{
exporter.StartExport();
// don't exit immediately, so the user sees something if the work is done fast
Thread.Sleep(5000);
wh.Set();
});
workerThread.Start();
wh.WaitOne();
Application.Current.Shutdown();
Have you taken a look at the Task Parallel Library in .net 4 you can set up a task and the library will work out to best pararellise it for you, either threading, working a seperate CPU core's the is a load of great information about it online.
Regards
Iain
To add a little to Lain's answer, here's a Console sample using a Task from the System.Threading.Tasks namespace.
class Program
{
static void Main(string[] args)
{
Task<int> task = Task<int>.Factory.StartNew(() =>
{
Exporter exporter = new Exporter();
int i = exporter.StartExport();
return i;
});
int iResult = task.Result;
Console.WriteLine(iResult);
Console.ReadLine();
}
class Exporter {
public int StartExport()
{
//simulate some work
System.Threading.Thread.Sleep(500);
return 5;
}
}
}
Using a BackgroundWorker might help you implement your background processing. If you wanted to stick with your current pattern then consider the following.
static void MainForm_Loaded(BeoExport exporter)
{
workerThread = new Thread(() =>
{
exporter.StartExport();
Thread.Sleep(1000);
MainForm.BeginInvoke(
(Action)(() =>
{
MainForm.Close();
});
});
workerThread.IsBackground = true;
workerThread.Start();
}
Have the worker thread send a notification message of some description to the main thread. The GUI can then either exit or display a "done" message as appropriate.
I have c# app that has UI and background threads. Based on user input I like to stop and start the background thread. I have two options here as I see:
1) totally stop and then start background thread as new thread ( I have not been able to this. I keep getting my process ended message)
2) Pause the background thread until user click run again.
Here is the code that I call again after bw.CancelAsync();
private void StartBackgroundWorker()
{
bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync("Background Worker");
}
you can't start and stop a background worker like that, but in your DoWork event, you can have it ask whether it should execute or wait.
you can also subclass BackgroundWorker (override the OnDoWork() method), and add start/pause methods to it that toggle a private wait handle, which is much nicer than having your UI know about the ManualResetEvent.
//using System.Threading;
//the worker will ask this if it can run
ManualResetEvent wh = new ManualResetEvent(false);
//this holds UI state for the start/stop button
bool canRun = false;
private void StartBackgroundWorker()
{
bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync("Background Worker");
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//it waits here until someone calls Set() on wh (via user input)
// it will pass every time after that after Set is called until Reset() is called
wh.WaitOne()
//do your work
}
}
//background worker can't start until Set() is called on wh
void btnStartStop_Clicked(object sender, EventArgs e)
{
//toggle the wait handle based on state
if(canRun)
{
wh.Reset();
}
else {wh.Set();}
canRun= !canRun;
//btnStartStop.Text = canRun ? "Stop" : "Start";
}
You can always abort a thread and catch the ThreadAbortedException. Im not sure if this is the most neat solution since an exception causes a lot of overhead but i think it is better than spreading WaitOne in the code like Dan suggested.
Another solution is to inherit from the thread class, and add a function to this class that stops or pauses the thread. This way you can hide the details of the implementation.