Thread that talk with UI? - c#

I have this code:
private void buttonStart_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => GeneraListaCartelle())
.ContinueWith(t => GeneraListaCartelleCompletata()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext());
}
private void GeneraListaCartelle()
{
try
{
... some operation ....
}
catch (Exception err)
{
txtErrors.AppendText(err.Message);
}
}
GeneraListaCartelleCompletata()
{
... process finished...
}
and txtErrors is in the "main" thread (the UI). When I catch an error, the asynch thread cannot write to the UI control, and I get an invalid cross-thread exception.
Can I dialogate with the UI inside a Thread?

If you are using WinForms, you will need to Invoke your method on the UI-thread like
catch (Exception err)
{
if(this.InvokeRequired){
Action<Exception> act = ((ex) => {
txtErrors.AppendText(ex.Message);
});
this.Invoke(act, new object[] { err });
}
else{
txtErrors.AppendText(err.Message);
}
}
If you are using WPF you will need to
catch (Exception err)
{
if(this.Dispatcher.CheckAccess()){
txtErrors.AppendText(err.Message);
}
else {
Action<Exception> act = ((ex) => {
txtErrors.AppendText(ex.Message);
});
this.Dispatcher.Invoke(act, new object[] { err });
}
}

if You are targetting WinForm Application then:
try
{
... some operation ....
}
catch (Exception err)
{
if (txtErrors.InvokeRequired)
{
txtErrors.BeginInvoke(new MethodInvoker(
delegate { txtErrors.AppendText(err.Message); })
);
}
}

There's an example from msdn:
// This delegate enables asynchronous calls for setting
// the text property on a TextBox control.
delegate void SetTextCallback(string text);
// This method demonstrates a pattern for making thread-safe
// calls on a Windows Forms control.
//
// If the calling thread is different from the thread that
// created the TextBox control, this method creates a
// SetTextCallback and calls itself asynchronously using the
// Invoke method.
//
// If the calling thread is the same as the thread that created
// the TextBox control, the Text property is set directly.
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}

set CheckForIllegalCrossThreadCalls to false in the called form_load event handler

Pulled this out of an old project where I had to deal with updating the UI from another thread. Should work for you as well.
delegate void addtoValProg();
addtoValProg myDelegate;
myDelegate = new addtoValProg(invokeControl);
private void GeneraListaCartelle()
{
try
{
//... some operation ....
}
catch (Exception err)
{
invokeControl();
}
}
private void invokeControl()
{
if (this.InvokeRequired)
{
this.Invoke(this.myDelegate);
}
else
{
txtErrors.AppendText(err.Message);
txtErrors.Update();
}
}

Related

Convert BackgroundWorker to Task, best practice?

I have a class SimpleTask which looks like this:
public class SimpleTask<T>
{
private readonly Action<Exception> _errorAction;
private readonly Func<T> _produce;
private readonly Action<T> _then;
public SimpleTask(Func<T> produce, Action<T> then, Action<Exception> errorAction)
{
_then = then ?? throw new ArgumentNullException(nameof(then));
_errorAction = errorAction ?? throw new ArgumentNullException(nameof(errorAction));
_produce = produce ?? throw new ArgumentNullException(nameof(produce));
}
public void Run()
{
using (var backgroundWorker = new BackgroundWorker())
{
var item = default(T);
backgroundWorker.DoWork += (_, e) => item = _produce();
backgroundWorker.RunWorkerCompleted += (_, e) =>
{
if (e.Error != null)
{
_errorAction(e.Error);
return;
}
_then(item);
};
backgroundWorker.RunWorkerAsync();
}
}
}
I would like to use a Task instead of a BackgroundWorker but I end up with something like this:
public class SimpleTask<T>
{
private readonly Action<Exception> _errorAction;
private readonly Func<T> _produce;
private readonly Action<T> _then;
public SimpleTask(Func<T> produce, Action<T> then, Action<Exception> errorAction)
{
_then = then ?? throw new ArgumentNullException(nameof(then));
_errorAction = errorAction ?? throw new ArgumentNullException(nameof(errorAction));
_produce = produce ?? throw new ArgumentNullException(nameof(produce));
}
public void Run()
{
try
{
var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(_produce,CancellationToken.None).ContinueWith(t =>
{
if (t.IsFaulted)
{
_errorAction(t.Exception);
}
else if (t.IsCompleted)
{
_then(t.Result);
}
}, CancellationToken.None,TaskContinuationOptions.ExecuteSynchronously,synchronizationContext);
}
catch (Exception ex)
{
_errorAction(ex);
}
}
}
Which is not the same after all. In my unit tests I have to add:
[SetUp]
public void TestSetUp()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
I wonder if I should keep using the BackgroundWorker and not pollute my code with task/async structures? What is best practice?
You are reinventing the wheel, because the new async functionality handles most of this for you.
I assume you want to:
Run in a background task a compute bound method that returns a result
Handle any exception thrown by that method
Access the returned value if no exception occurred
Here's an example of how to do this using await.
In this example, the compute-bound method is int computeBoundFunction(). The code assumes you have a Windows Forms form with a button called button1 and a multiline TextBox called textBox1:
async void button1_Click(object sender, EventArgs e)
{
textBox1.AppendText("Starting task\r\n");
try
{
int result = await Task.Run(computeBoundFunction);
// Instead of your "then" action, just call the code here.
// In this example, I'm just appending to a multiline text box.
// This runs on the UI thread.
textBox1.AppendText("Task returned " + result);
}
// Instead of your "errorAction" action, handle exceptions here.
// Note that this runs on the UI thread, so you can update controls safely at this point.
catch (Exception exception)
{
textBox1.AppendText("Exception: " + exception.Message);
}
}
int computeBoundFunction()
{
Thread.Sleep(2000); // Emulate workload.
return 42;
// Comment out the return above and uncomment the line below to test the exception handling:
//throw new InvalidOperationException("Test exception");
}
Note that normally you would never use async void instead of async Task, but this rule is relaxed for event handlers such as button1_Click() in this example.

How to show waiting Gif image during form load event

i have one form which is doing some long process on form load event,
So i want to show One Gif image "Please Wait" during form load event.
below is code.
private void frmWaitShow()
{
try
{
frmWaitwithstatus objWait = new frmWaitwithstatus();// this form has Gif Image for Processing
objWait.lblStatus.Text = "Processing Request, Please wait...";
objWait.ShowDialog();
}
catch (Exception ex)
{
Logger.SystemException(ex);
Logger.FTSError(" ERROR :" + ex.Message + "frmTest || frmWaitShow");
}
}
Thread oThread;
private void frmTest_Load(object sender, EventArgs e)
{
try
{
oThread = new Thread(new ThreadStart(frmWaitShow));
oThread.Start();
//Functions for Connection with devices
if (LoadDatafromDB() == false) return;
if (ElectTestLoad() == false) return;
if (PowerOnSelfTest() == false) { return; }
InitiControlsElectTest();
SetSystemMode(SystemMode.ElectricalMode);
oThread.Abort();
}
catch (Exception ex)
{
oThread.Abort();
Logger.SystemException(ex);
}
}
after Thread.start() my debugger go one one step in each thread main and one i created but after it go to below line.
frmWaitwithstatus.cs constructor first line
public frmWaitwithstatus()
it stop execute my Thread and execute all function of main thread once Main Thread execution complete after then only it start execute my thread (which is Gif processing image).
Using the async/await pattern will made this an easy task and every form will work on UI thread:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
// async show loading form dialog
var loadingForm = new LoadingForm();
var loadingDialogTask = this.InvokeAsync(loadingForm.ShowDialog);
// async loading data
var data = await LoadDataAsync();
listBox1.DataSource = data;
loadingForm.Close();
await loadingDialogTask;
}
private async Task<ICollection<string>> LoadDataAsync()
{
// fake work load
await Task.Delay(4000).ConfigureAwait(false);
return Enumerable.Range(1,20000).Select(e => e.ToString()).ToList();
}
}
Needed async extension for the controls:
public static class ControlAsyncExtensions
{
public static Task InvokeAsync(this Control control, Action action)
{
var tcs = new TaskCompletionSource<bool>();
control.BeginInvoke(new Action(() =>
{
try
{
action();
tcs.SetResult(true);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}
));
return tcs.Task;
}
public static Task<T> InvokeAsync<T>(this Control control, Func<T> action)
{
var tcs = new TaskCompletionSource<T>();
control.BeginInvoke(new Action(() =>
{
try
{
tcs.SetResult(action());
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}
));
return tcs.Task;
}
}

Object in use System.InvalidOperationException'

I have problems calling Form objects from another class.
In Form class I have this:
public void reloadMapOverlay(GMapOverlay overlay)
{
try
{
Invoke(new Action(() => this.map_Box.Overlays.Add(overlay)));
}
catch (Exception e)
{
Console.WriteLine("reloadMapOverlay: {0}", e);
this.setError("reloadMapOverlay: " + e);
}
}
And in another class (Map_custom.cs):
route.Points.Add(coords.get_position());
overlay.Routes.Add(route);
mainForm.reloadMapOverlay(overlay);
When mainForm.reloadMapOverlay executes, vb gives me and object in use an exception
System.InvalidOperationException
in overlay object.
What can I can do?
i try adding dispacher in mainForm, ->
public Dispatcher dispacher = Dispatcher.CurrentDispatcher;
public void reloadMapOverlay(GMapOverlay overlay)
{
try
{
dispacher.Invoke(new Action(() => this.map_Box.Overlays.Add(overlay)));
}
catch (Exception e)
{
Console.WriteLine("reloadMapOverlay: {0}", e);
this.setError("reloadMapOverlay: " + e);
}
}
and nothing.
i have
public void reloadMapOverlay(GMapOverlay overlay)
{
try
{
Invoke(new Action(() => this.map_Box.Overlays.Add(overlay)));
}
catch (Exception e)
{
Console.WriteLine("reloadMapOverlay: {0}", e);
this.setError("reloadMapOverlay: " + e);
}
} in mainForm,
then, in another class (Map_Custom.cs) i have a function
public void addRoute()
{
if (!coords.isWrongCoords())
{
Console.WriteLine("Route");
route.Points.Add(coords.get_position());
overlay.Routes.Add(route);
mainForm.reloadMapOverlay(overlay);
}
}
and in mainFrom i have a function (inside one thread) where i call
Thread addRoute = new Thread(new ThreadStart(map.addRoute));
addRoute.IsBackground = true;
addRoute.Start();

Passing custom object to RunWorkerCompleted event in case of an exception

My Console App uses System.ComponentModel.BackgroundWorker for threading purposes:
System.ComponentModel.BackgroundWorker backgroundWorker = new System.ComponentModel.BackgroundWorker();
backgroundWorker.DoWork += (sender, e) =>
ReportStatus(worker, status, result, e);
backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker.RunWorkerAsync(worker);
As you can see that I am passing "worker" as an argument inside RunWorkerAsync.
What I am trying to achieve is that if there is an exception inside ReportStatus method I need the same "worker" object so that I can perform some operation (Call a service to notify that workers exception)
private void ReportStatus(Worker worker, Status status, WorkResult result,System.ComponentModel.DoWorkEventArgs arg)
{
var proxy = new PreparationServiceProxy(new NetTcpBinding(), new EndpointAddress(PreparationEngineState.ServiceAddress));
try
{
proxy.ReportStatus(worker, status, result);
proxy.Close();
}
catch (Exception)
{
arg.Result = worker;
proxy.Abort();
throw;
}
}
In my exception block (I am not sure if this is the correct way!) I am assigning the worker to the Result so that I can get the same worker back when the RunWorkerCompleted method (backgroundWorker1_RunWorkerCompleted) is executed :
private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
Worker worker = e.Result as Worker; // At this point I get an exception!
}
}
It's because you re-threw the exception. BackgroundWorker sees that as an exception unhandled by the DoWork handler and re-throws it back on the other thread when you get the Result value.
If you don't want it to do that, remove the throw in your catch in the DoWork handler.
if you passed the worker object into the BackgroundWorker, why don't use just use what you passed in in an exception handler wrapping the call to Result or in the block that tests Error? e.g.:
if (e.Error != null)
{
worker.DoSomething(); // no access of Result
}
.NET does NOT consider that Async Operation might have some result if an Error happened. That's why you will have pass it some other way.
I would recommend to implement custom exception class:
public class WorkerException:ApplicationException
{
public WorkerException(Worker worker,Exception innerException):base(null,innerException)
{ Worker = worker; }
public Worker Worker
{
get;
set;
}
}
And wrap you exception accordingly:
private void ReportStatus(Worker worker, Status status, WorkResult result,System.ComponentModel.DoWorkEventArgs arg)
{
var proxy = new PreparationServiceProxy(new NetTcpBinding(), new EndpointAddress(PreparationEngineState.ServiceAddress));
try
{
proxy.ReportStatus(worker, status, result);
proxy.Close();
}
catch (Exception exception)
{
arg.Result = worker;
proxy.Abort();
throw new WorkerException(worker,exception);
}
}
In this case you will be able to retrieve Worker of exception, casting Error to WorkerException:
private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Error is WorkerException)
{
Worker worker = ((WorkerException)e.Error).Worker; // At this point I get an exception!
}
}
The RunWorkerCompletedEventArgs contains a UserState property which (I think) should be a reference to the same object that you passed to the RunWorkerAsync method. (It should also be in the DoWorkEventArgs as the Argument property.)
You'll need to experiment to confirm that this UserState is the right object (cast it as Worker) and that it is valid even when the DoWork handler threw an exception, but I think that could be what you're looking for.

Cross-thread operation not valid while listening to a COM port [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Getting Cross-thread operation not valid
Cross-thread operation not valid
I am trying to listen to COM port so that I create new handler for SerialPort.DataReceived event. The logic is simple - I write something to TextBox1, press Button1 and my text should show it self in Label1. But my application don't want to run, becouse it throws 'Cross thread operation not valid' error.
I did some searching and found Invoke object - how can I use it in my example? Why do I need to include Invoke logic?
namespace WindowsApplication1
{
public partial class Form1 : Form
{
SerialPort sp = new SerialPort();
public Form1()
{
InitializeComponent();
sp.DataReceived += MyDataReceivedHandler;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
try
{
//sp.PortName = "COM3";
//sp.Open();
Label1.Text = sp.ReadLine();
}
catch (Exception exception)
{
RichTextBox1.Text = exception.Message + "\n\n" + exception.Data;
}
finally
{
sp.Close();
}
}
private void button1_Click(object sender, EventArgs e)
{
try
{
sp.PortName = "COM3";
sp.Open();
sp.WriteLine(TextBox1.Text);
}
catch (Exception exception)
{
RichTextBox1.Text = exception.Message + "\n\n" + exception.Data;
}
finally
{
sp.Close();
}
}
}
}
My guess is that MyDataReceivedHandler is running on a different thread than the GUI. In order to fix that, you need to invoke the Text setters on the correct thread. This is a sample of doing so:
public void SetControlText(Control control, string text)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<Control,string>(SetControlText), new object[] { control, text });
}
else
{
control.Text = text;
}
}
private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
try
{
//sp.PortName = "COM3";
//sp.Open();
SetControlText(Label1, sp.ReadLine());
}
catch (Exception exception)
{
SetControlText(RichTextBox1, exception.Message + "\n\n" + exception.Data);
}
finally
{
sp.Close();
}
}
If you are using .NET Framework 2.0, the above Action<T1, T2> delegate is not available, so you will have to define your own one:
private delegate void SetControlTextHandler(Control control, string text);
public void SetControlText(Control control, string text)
{
if (this.InvokeRequired)
{
this.Invoke(new SetControlTextHandler(SetControlText), new object[] { control, text });
}
else
{
control.Text = text;
}
}
The SetControlText method can be made shorter (and even static) like this (this works in both 2.0 and 3.5):
public static void SetControlText(Control control, string text)
{
´control.Invoke((MethodInvoker)delegate { control.Text = text; });
}
Then you don't need to do the check of InvokeRequired each time, but you will on the other hand wrap the call in a delegate even if it is not needed. I think that in a GUI method like this any performance difference between those two is neglectible so I tend to use the shorter form, simply because it is less code to write.
You can also do the following whenever accessing a UI control from a different thread than the one it was created on:
(.NET 3.5)
myControl.BeginInvoke(new MethodInvoker( () => myControl.whatever = whatever; ));
or
(.NET 2.0)
myControl.BeginInvoke(new MethodInvoker( delegate { myControl.whatever = whatever; ));
edit> Sometimes using Invoke for a long running operation can/will still hang the ui, using BeginInvoke obviously performs that operation asynchronously, and the ui will not hang.

Categories