GUI update when starting event handler class on separate thread? - c#

We have a DLL that monitors changes in status and looks for events from a separate, purchased product. Actually, it is a ScreenPop API from Siemens, for those of you who may know what that is. I am using C#.NET 3.5 as the platform.
This API takes a looong time to initialize, so we want to use a separate thread to initialize it. Currently, we have the functionality in a class called ScreenPop. The class monitors 2 events, a status change event and a screen pop event (data that tells us who the customer is that is calling).
The way this is currently implemented doesn't work, or at least doesn't work reliably. Within the ScreenPop class, there is an initialization method where all the long-running startup code is placed. This is called from the constructor of the class, like this:
public ScreenPop( string Address, int Ext, CallbackStatusType pStatusFunc,
CallbackScreenPopType pPopFunc
)
{
CallbackStatus = pStatusFunc;
CallbackPopup = pPopupFunc;
Thread t = new Thread( StartInBackground );
t.Start();
}
In the GUI code, the func at pStatusFunc updates a status label, and the func at pPopupFunc will fire off some other code to do the screen pop - right now it just displays the data from the event.
There is a lot of glue missing, but I hope you get the point. The problem with this approach is the GUI is not updated. I know the events fire and the event handlers run, and the callback functions are getting called and they seem like they should be running, but the GUI is never updated.
So, my question is, should I abandon this in favor of a BackgroundWorker approach? Or am I just missing something in getting the GUI to update?
More info on request...
Thanks,
Dave

You can never update the GUI from a different thread - only from the UI thread, which is the one that started the application. You need to use the Control.Invoke method to run code on the UI thread. Form instance, frmMain.Invoke.

You cannot use WinForms in a multithreaded apartment, to get around this, you have to marshal over to the UI thread to perform actions on it or get results. Since you are using C#3.5, you can make use of lambdas, generics, and extension methods to make a really clean and easy to use solution.
public static class ControlExtensions
{
public static TResult InvokeEx<TControl, TResult>(this TControl control,
Func<TControl, TResult> func)
where TControl : Control
{
if (control.InvokeRequired)
{
return (TResult)control.Invoke(func, control);
}
else
{
return func(control);
}
}
}
Now you can safely and easily make changes or get values.
this.InvokeEx(f => f.label1.Text = "Hello from another thread");
new Thread(() =>
{
string formTitle = this.InvokeEx(f => f.Text); // Safely get form title
}).Start();

Related

Writing a method that reports progress

I'm writing a class that performs a certain operation in a library. But the operation is tedious, and I want to be able to find out the progress of the method inside that class so that I can use it in a WinForms application to report the progress.
I'm planning to run my class on another thread in my WinForms application and I want the class to be separated from the concerns of the WinForms application, and I don't want to bind it to anything specific other than what it does.
What would be the best way to implement a progress reporting mechanism in a library class?
Would it be a good idea to somehow have a progress variable in the class, and add an event listener to it in my WinForms application? And if it is, how can I do it?
Edit: I have used the BackgroundWorker class before, but my problem is I don't want my library class to be concerned with any of the multithreading operations. So I don't want to invoke ReportProgress in the library class, I want to (maybe) have a variable in the class that contains the current progress and I want the UI thread to somehow "subscribe" to it. I don't know if that's a good way to design it though.
Look into the BackgroundWorker class. It supports automatic marshaling across threads for progress reporting, and it has a very simple event model for this kind of support.
Edit: Given your position on using BackgroundWorker directly, what you might do is create a simple wrapper:
// Error checking elided for expository purposes.
public interface IProgressReporter
{
void ReportProgress(int progress, object status);
}
public class BackgroundWorkerProgressReporter : IProgressReporter
{
private BackgroundWorker _worker;
public BackgroundWorkerProgressReporter(BackgroundWorker worker)
{
_worker = worker;
}
public void ReportProgress(int progress, object status)
{
_worker.ReportProgress(progress, status);
}
}
Then, alter the constructor (or add a property) of the class that you want to report progress to accept an IProgressReporter. This is a form of dependency injection, and should meaningfully allow your object to report progress whilst also avoiding specific dependencies on threading libraries.
Here you can do 2 different ways I will post both options below to help point you in the right direction
You should be doing this on another thread, and then updating your UI thread from that thread. You are blocking further processing by performing this work on the UI thread.
If you can't move this code to the UI thread, then you could always call Application.DoEvents, but I strongly suggest you explore these options first:
System.ComponentModel.BackgroundWorker
System.Threading.ThreadPool
System.Threading.Thread
System.Threading.Tasks namespace
Second Alternative you could do something like this:
You'll need to get your data from one thread to the other. This can be done a couple ways...
First, your "background" thread could update some kind of "CurrentStatus" string variable that it changes as it goes along. You could then put a timer on your form that would then grab the CurrentStatus variable and update the label with it.
Second, you could simply invoke the operation from the background thread to the UI thread with a delegate using the InvokeRequired property of the label control. So for example...
private delegate void UpdateStatusDelegate(string status);
private void UpdateStatus(string status)
{
if (this.label1.InvokeRequired)
{
this.Invoke(new UpdateStatusDelegate(this.UpdateStatus), new object[] { status });
return;
}
this.label1.Text = status;
}
You can call that UpdateStatus() method from any thread (UI or background) and it will detect whether or not it needs to invoke the operation on the main UI thread (and if so, does it).
Edit: To actually set up the thread, you can do so like this:
private void StartProcessing()
{
System.Threading.Thread procThread = new System.Threading.Thread(this.Process);
procThread.Start();
}
private void Process() // this is the actual method of the thread
{
foreach (System.IO.FileInfo f in dir.GetFiles("*.txt"))
{
// Do processing
// Show progress bar
// Update Label on Form, "f.Name is done processing, now processing..."
UpdateStatus("Processing " + f.Name + "...");
}
}
Then when the user clicks the "GO" button you'll simply call StartProcessing().

calling event that implements at other thread in C#

I have a class with a Socket, listens to clients to receive data. When receive new data I want to call an event (if implemented) but as you know every connection has its own thread so the event will run at that thread and you know the rest. you can not work with form controls.
How do I call the event (or invoke it). I'm really new to thread and network programing so I appreciate any example.
public class HVremotechooser
{
public delegate void NewOrder(Order order);
public event NewOrder nOrder;
//... (code elided)
public void ReceiveCallback(IAsyncResult AsyncCall) // new connection of client
{
//... (code elided)
if (nOrder != null)
nOrder(Order); // calling the event "nOrder"
//... (code elided)
}
}
thank you.
If you want to update your form from a non-UI thread, you have to invoke the action. What I normally do is the following:
private void LongRunningBackgroundThread() {
// lots of work
...
// Update my form
InvokeIfRequired(() => {
...update form...
}
}
private static void InvokeIfRequired(Action a) {
if (control.InvokeRequired) {
control.Invoke(a);
} else {
a();
}
}
See here and here
I ran into a similar problem with a Silverlight application that I was working on last week, and used the Dispatcher.BeginInvoke method. With Windows forms, it looks like it might be easier to use Control.BeginInvoke instead (although I believe that either should work): http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx
You can use the typical marshling operations like Invoke or BeginInvoke to inject the execution of a delegate onto the UI thread. Just pass an instance of ISynchronizeInvoke or SynchronizationContext to your class to facilitate the marshaling.
However, I would not do that in your case. Since, presumably anyway, these callbacks are occuring because of socket events it is possible likely they are coming in hard and heavy. You definitely do not want to slam your UI thread with all of that activity. Instead, package up all of the pertinent data and put into a collection that the UI thread can then poll for using a System.Windows.Forms.Timer on a more reasonable interval.
I rip on these marshling operations all of the time. They are way overrated and overused. Remember, there are two general methods of sharing data and signaling between UI and worker threads.
Push method via Invoke or BeginInvoke in the worker thread
Pull method via System.Windows.Forms.Timer in the UI thread
The pull method can be, and often is, more elegant.
UI Threading is a UI concern. In my opinion you shouldn't worry about invoking to the ui thread in this code. Rather the consumer of the event should do the Invoke or whatever other threading stuff they happen to need to do. That way if the UI person needs to change their strategy (e.g. by using a timer) your non-UI related code wont need to change.

Custom Events and Threading

I have two different projects and in one, I have a class that defines a custom event to fire under certain conditions:
public delegate void ButtonHandler(object myObject,
GuitarArgs myargs);
public event ButtonHandler OnButtonPress;
... other stuff ...
GuitarArgs myArgs = new GuitarArgs( guitarState );
if(OnButtonPress!= null)
OnButtonPress(this, myArgs);
Then in another project I create an instance of this class and subscribe to this event:
Guitar.OnButtonPress += Guitar_OnButtonPress;
The event then fires properly, but it seems to be on a different thread, because when I try to access some UI elements, I get an error : The calling thread cannot access this object because a different thread owns it.
The code throwing the error is:
void Guitar_OnButtonPress(object myObject, Guitar.GuitarArgs myargs)
{
GuitarCheck(myargs.State);
}
private void GuitarCheck(GuitarState getState)
{
if (getState.green)
{
VisualTreeHelper.HitTest(guitarCanvas, null,
MyHitTestResult,
new GeometryHitTestParameters(
new RectangleGeometry(new Rect(1, 411, 88, 78))));
}
}
What can I do to make this run?
Thank you.
You need to check if your event handler is running on a non-UI thread and, if so, invoke it on the UI thread:
How to update the GUI from another thread in C#?
I would like to highlight the less-upvoted answer, which provides a very elegant approach using an extension method. I use this in all of my WinForms projects (and use a variant in WPF/Silverlight projects)
https://stackoverflow.com/a/3588137/141172
If you are using WinForms
this.Invoke(() => GuitarCheck(myargs.State));
(http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx)
If you are using WPF
this.Dispatcher.Invoke(() => GuitarCheck(myargs.State));
(http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.invoke.aspx)
So you have an event called OnButtonPress whos event handler is obviously executing on a thread that is not hosting VisualTreeHelper. The big question for me is...why? With a name like OnButtonPress it makes me think it should already be on the UI thread. Afterall, it is a button press right? So why is it executing on another thread to begin with?
Using a marshaling operation like Invoke or BeginInvoke is fine as long as you are aware of why it is needed and what it accomplishes for you. But, you really need to take a step back and identify what all is going on that would cause you to need to use a marshaling operation in the first place. There may be a bigger issue here that you need to address.

Framework like thread marshalling

I always thought I could use SynchronizationContext to marshal a call to another thread. Apparently I was wrong as SyncCtx.Send() doesn't do anything but invoking the given delegate (stays on the same thread). Do I really need to derive from SynchronizationContext and do work on the thread context? I feel like missing something.
What I want to achive: Imagine a little API for executing commands within an app. You can also execute a command on a background thread, as you can assign a delegate to run when the command has finished execution. This "Call-me-when-done"-Delegate gets one single paramter (State) containing success/failure flag, optional exception information, etc. I want to call this delegate on the original calling thread so that devs using the lib do not need to handle invoke required etc. I would just like to take this away and let them do simple non-thread-aware programming. WindowsFormsSynchronizationContext doesn't seem to help either if you don't give it some control as a target.
Thanks for any help!
When you are in winforms and you use the synchronizationcontext the call will be marshalled to the GUI thread.
for your specific case I guess something like this should work, probably it will be a good idea to create a class that represents a Command
public class CommandManager
{
private readonly SynchronizationContex _synchronizationContex;
public CommandManager(SynchronizationContext synchronizationContex)
{
_synchronizationContex = synchronizationContex;
}
public void ExecuteAsync(Func<State> action, Action<State> callback)
{
ThreadPool.QueueUserWorkItem(o => {
state = action();
_synchronizationContex.Send(oo => callback(state));
});
}
}
you'd create like this (in the GUI thread, so for example in your main form)
var commandManager = new CommandManager(SynchronizationContext.Current);
you'd use it like this:
commandManager.ExecuteAsync(() => new State() { Success = true },
c => MessageBox.Show("success in the GUI thread"));

C#: How to force "calling" a method from the main thread by signaling in some way from another thread

Sorry for long title, I don't know even the way on how to express the question
I'm using a library which run a callback from a different context from the main thread (is a C Library), I created the callback in C# and when gets called I would like to just raise an event.
However because I don't know what will be inside the event, I would like to find a way to invoke the method without the problem of locks and so on (otherwise the third party user will have to handle this inside the event, very ugly)
Are there any way to do this?
I can be totally on the wrong way but I'm thinking about winforms way to handle different threads (the .Invoke thing)
Otherwise I can send a message to the message loop of the window, but I don't know a lot about message passing and if I can send "custom" messages like this
Example:
private uint lgLcdOnConfigureCB(int connection, System.IntPtr pContext)
{
OnConfigure(EventArgs.Empty);
return 0U;
}
this callback is called from another program which I don't have control over, I would like to run OnConfigure method in the main thread (the one that handles my winform), how to do it?
Or in other words, I would like to run OnConfigure without the need of thinking about locks
Edit 1:
I have a problem with this exception:
CallbackOnCollectedDelegate retrived
Message: Callback run on delegate 'G19dotNet!G19dotNet.LgLcd+lgLcdOnSoftButtonsCB::Invoke' collected in GarbageCollector. During unmanaged code delegates should be ensured will never be deleted until you are sure they will never be called
Edit 2:
Issue resolved by myself, thanks to Stackoverflow which always helps me!
For future reference: Defining a delegate as a function pointer
If you're using WinForms and you want to execute something on the UI thread, you need to call either Invoke or BeginInvoke on some control (be it a Button or a Form or whatever) that was created on that thread. You'll need a reference to it in order to do this.
For example, with your code and assuming that you have a reference to a form called form:
private uint lgLcdOnConfigureCB(int connection, System.IntPtr pContext)
{
form.Invoke(new MethodInvoker(() => OnConfigure(EventArgs.Empty)));
return 0U;
}
Before you call the 3rd party function, get a reference to Dispatcher.CurrentDispatcher. In the callback function, use dispatcher.Invoke.
What you end up with will look something like this:
class MyClass
{
private Dispatcher dispatcher;
public void runThirdParty()
{
this.dispatcher = Dispatcher.CurrentDispatcher;
callThirdPartyFunction(myCallBack);
}
public void myCallBack()
{
this.dispatcher.Invoke(new Action(() =>
{
//code to run here.
}));
}
}
There is a pattern for this called Event-based Asynchronous Pattern. The article linked is a great overview on how to use it. The AsyncOperation class is the key to this pattern.
This pattern might not fit perfectly with your problem that you are trying to solve, but it might give you some insights into the problem.
Thanks to Adam Robinson answer, I added a nice little utility function on my form:
private void runOnUIThread(Action function)
{
this.Invoke(new MethodInvoker(function));
}
And I'm using it like this:
runOnUIThread(() =>
{
example_lbl_status.Text = "Active";
});

Categories