C#: Events & Thread Safe GUI Updates - c#

I am in the process of writing an application that communicates with several devices through GPIB commands, running a test on some equipment. I've set up a class, TestProcedure, which will start a new thread, and run the testing. Throughout testing, I've set up several custom events to send information back to the GUI. Here is an example of a simple event:
public event InformationEventHandler<string> TestInfoEvent;
/// <summary>
/// Event raised when information should be passed back to the main testing form.
/// </summary>
/// <param name="s">Information to send back to form.</param>
private void OnInfo(string s)
{
if (TestInfoEvent != null)
TestInfoEvent(this, s);
}
Which would be handled through the GUI, updating a text box like this:
TheTestProcedure.TestInfoEvent += new TestProcedure.InformationEventHandler<string>
(InfoOccurred);
....
private void InfoOccurred(Object sender, string s)
{
this.textBox1.Text = s + Environment.NewLine + this.textBox1.Text;
if (this.textBox1.Text.Length > 10000)
this.textBox1.Text = this.textBox1.Text.Remove(1000);
}
This event handling seems to be working fine. I haven't received any cross threading issues, and overall it's been working as expected. However, on another form I just added a similar event handler, which throws a cross-thread exception. The event fires, sending a simple class with a bit of information that I display in an InputTextBox (A custom ComponentOne control). The particular control does not have a .Invoke method, so I'm looking for alternative solutions to access it asynchronously.
So my question is, are event handlers safe to access controls on a form? If not, how do event handlers fire, and could somebody help educate me, or provide some linked information, as to how an event handler communicates with form controls? Do I need to lock the event?

Controls on the UI thread may only be accessed from the UI thread - any access from other threads is bound to cause issues. You need to use InvokeRequired and BeginInvoke() to marshal an event to the right thread if it's not already there.
Example

You'll want to create a delegate callback and Invoke() execute that after testing the InvokeRequired property. The following code will handle the addition in a thread safe manner.
TheTestProcedure.TestInfoEvent += new TestProcedure.InformationEventHandler<string>
(InfoOccurred);
private void InfoOccurred(Object sender, string s)
{
LogMessage(s);
}
delegate void LogMessageCallback(string text);
void LogMessage(String message)
{
if (this.textBox1.InvokeRequired)
this.Invoke(new LogMessageCallback(LogMessage), message);
else
{
this.textBox1.Text = s + Environment.NewLine + this.textBox1.Text;
if (this.textBox1.Text.Length > 10000)
this.textBox1.Text = this.textBox1.Text.Remove(1000);
}
}

Related

Crossthread Communication in modular C# WinForms App

I am currently working on a modular application using multiple dynamically loaded plugins. The main window also works as an output for feedback messages from the various plugins. This is a central requirement so the plugins can't have their own output.
The main program consists of 3 classes:
Main class that creates the GUI and handles the plugin calls
Second class that collects the plugins from a specified folder using MEF
Export class that can be accessed from a Plugin to send a message (string[]) to show in the main window
The Export class uses an event for receiving messages, to which the main class subscribes and writes the message into a DataGridView.
This works fine as long as I start a plugin without putting it into a separate thread. But as expected the mainform is frozen while the plugin is working. Now I tried to create separate threads for the plugins so I can have multiple ones running in the background simultaneously. Unfortunately this renders the message receiving a cross-thread operation.
With the use of an event I intended to make it thread-safe because the mainform subscribes to the Event and handles the messages in the "main-thread". Obviously I was wrong… I still don't quite get how the event-triggered method in the main window suddenly switches into the separate task...
Some additional Information:
Invoking is not an option because the output window consists of a lot more controls than just the DataGridView and these controls are constantly being modified whenever a message is received.
The mainform has a public static string[] for transferring the message's content from the export class to the main class
Is there any way to put the message writing method into the main thread but still able to subscribe to the event that is fired from another thread?
Or maybe is there another approach to the task?
[Export]
public class Exportklasse : Interfaces.IMain
{
public static event EventHandler MeldungEintragen;
// Diese Methode aufrufen, um von Modulen aus Textmeldungen einzutragen
public void MeldungEmpfangen(string[] MeldungInput, EventArgs e)
{
EventHandler EintragenEvent = MeldungEintragen;
if (EintragenEvent != null) {
MainForm.MeldungText = MeldungInput;
EintragenEvent(this, e);
}
}
}
Would something like this work?:
private readonly IBackendWorker worker;
private SynchronizationContext uiContext;
public MainForm(IBackendWorker worker)
{
InitializeComponent();
this.worker = worker;
uiContext = SynchronizationContext.Current;
worker.BackendEvent += OnWorkerBackEvent;
...
}
and the eventhandler:
private void OnWorkerBackEvent(int count)
{
// schedule the action on the UI synchronisation context.
uiContext.Post((c) =>
{
// put your code to interact with UI here
outputTextBox.Text = count.ToString();
}, null);
// or: uiContext.Post((c) => outputTextBox.Text = c.ToString(), count);
}
The eventhandler is executed in whatever thread the event is triggered from, but executes the action in the UI-thread.

C# difference between button events and custom class

I am trying to understand events and delegates and after 2 days of studying, it looks like I am still lost in basic understanding.
I wrote following code - class UserControl contains event definition. It works well, although the program is stucked in Start() method.
How for example buttonClick event is implemented? Does button object running in some kind of different thread - on order to be able to call a method whenever the button is clicked?
Thanks
class UserControl
{
public delegate void methodsControlDelegate();
public event methodsControlDelegate methods;
public void Start()
{
while (true)
{
if (methods != null)
{
Thread.Sleep(1000);
this.methods();
}
}
}
}
class Program
{
static void Main()
{
UserControl uc = new UserControl();
uc.methods += eventMethod;
uc.Start();
}
public static void eventMethod()
{
Console.WriteLine("EVENT METHOD");
}
}
EDIT:
I have modified the code for Windows Forms.
class Writer
{
public string Text { get; set; }
public void writeMessage(object sender, EventArgs e)
{
MessageBox.Show(Text);
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
UserControl userControl = new UserControl();
Writer writer = new Writer();
userControl.WriteMessages += writer.writeMessage;
writer.Text = "HELLO, HOW ARE YOU";
}
}
class UserControl
{
public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler WriteMessages;
}
I am trying to understand events and delegates and after 2 days of studying, it looks like I am still lost in basic understanding.
Take a step back.
class Customer
{
private string name;
public string Name { get { return this.name; } }
public Customer(string name)
{
this.name = name;
}
}
A property is logically a property of the class of things. Customers have a name, so Customers have a property Name.
A field is an implementation detail. It is a variable that can store a value.
A type is an implementation detail of a field or property; it gives you a restriction on what kind of data can be used as a value for this property.
The value -- say "Barbara Smith" -- is the value associated with that property for a particular customer: new Customer("Barbara Smith").
If that is not all clear then you need to take a step back and understand that. You won't get events and delegates if you haven't got properties, fields and values down.
An event is like a property. It is a logical feature of a class of things. Buttons can tell you that they are clicked, so Click is an event of Button. The button will call you when it is clicked.
A delegate type is a restriction on how the button may call you when it is clicked.
A delegate instance is a delegate to a particular function that will be called when the button is clicked.
Is that now clear?
How for example buttonClick event is implemented?
Understanding that requires you to understand how Windows works.
Every Windows program has a message queue which receives messages from the operating system. When the user clicks a button, Windows puts a message in the queue for that process that says the button was clicked. When the process handles that message, taking it out of the queue, it invokes the delegate associated with the click handler for the button.
Does button object running in some kind of different thread - on order to be able to call a method whenever the button is clicked?
Nope. If you hang the UI thread of your program so that it cannot remove the message from the queue then the button click handler is never invoked.
In fact it is illegal to call code in the button from any thread other than the UI thread.
Every time you've seen a Windows app hang, it's almost certainly because some badly-written code did not allow the message loop to take a message out of the queue in a timely manner.
You then go on to post some code with no explanation and no question. I don't know why you posted that code. Try asking a more clear question.
That said, looking at the code you seem to be trying to do event handling from a console application. Don't do that. Console applications are not event-driven. Write a WinForms or WPF application if you want to make an event-driven application.
delegate is an encapsulation on top of a method. It allows you to pass a method around, as a reference and execute it whenever you decide. The delegate defines a signature of a method and any method which is of the same signature can be used as that delegate.
events are one more level of encapsulation, this time on top of delegates. It allows adding and removing (subscribing and unsubscribing) methods to it. And when the event fires, it will invoke each one of the added to it methods (delegates). This encapsulation is necessary, so that one 'client' of the event cannot override another client to the same event.

Need help setting up threads/background worker in GUI

I'm using C# and Winforms in Visual Studio 2010
I have a program with which I am trying to read output through a serial port and print it to the screen. It originally started as a Console program but has now evolved to where we would like to have the output be in a field on a form. I have the code that parses out the output I'm looking for off the serial port written and working, I just need to change the Console.WriteLine to label.text = "";, basically. I have merged the function that listens to the serial port into the GUI code so everything is in the same file.
I'm getting hung up on how to get the function to write to the label, though. It is STATIC so I cant just say 'label.text ='. I tried creating a new form object inside the function to use, and that allowed me to access the control on the form, but doesnt update the form I see at runtime (I'm guessing because I've created a new instance of the form rather than accessed the existing instance?)
I need to have the serial listener run at the same time as the GUI as well, so the GUI label will update with the results it gets from running the function in close to real-time, so Ive tried to set it up to be threaded, with the GUI being one thread that is started by main() and the serial listener being another thread which is started when i click the button to start it. However, I run into the same issue with not being able to access the label in the serial listener thread because it has to be static to be initialized using system.threading.
I'm thinking maybe I need to use a background worker for the serial listener but I have absolutely zero experience with those. Would a background worker be able to update the label on the GUI in real time?
I cant post specific code but heres the general idea:
Main() starts GUIthread
GUI has button to start serial listener
OnClick button starts ListenerThread
ListenerThread outputs to console, want to output to a form label instead
Cant access GUI.Label because Listener is static out of necessity to be threaded
Creating new GUI instance inside Listener allows me to call the controls for that instance, but they dont update the GUI at runtime
have ensured label is public.
The BackgroundWorker class was essentially made just for this.
Just have the DoWork method do your actual work, and ensure that ReportProgess is called while working as needed. You can pass any data as a string (or whatever else, if you want) and then use that value in the ProgressChanged event handler, which the form can handle to update it's UI.
Note that the BackgroundWorker will automatically ensure that the ProgressChanged and RunWorkerCompleted events run in the UI thread, so you don't need to bother with that.
Here's a sample worker:
public class MyWorker//TODO give better name
{
public void DoWork(BackgroundWorker worker)//TODO give better name
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);//to mimic real work
worker.ReportProgress(0, i.ToString());
}
}
}
And here's an example of configuring the background worker. Here I use lambdas both because it's convenient to be able to close over variables (i.e. use variables across each of these anonymous methods) but if you wanted to you could refactor each of the event handlers out into methods.
private void button1_Click(object sender, EventArgs e)
{
var bgw = new BackgroundWorker();
MyWorker worker = new MyWorker();
bgw.WorkerReportsProgress = true;
bgw.DoWork += (s, args) => { worker.DoWork(bgw); };
bgw.ProgressChanged += (s, data) =>
{
label1.Text = data.UserState.ToString();
};
bgw.RunWorkerCompleted += (s, args) =>
{
label1.Text = "All Done!";
};
bgw.RunWorkerAsync();//actually start the worker
}
Note here that none of the controls in the form are public, none of them are static, and I'm not passing any references to my form outside of the class. It's considered best form each Form to be responsible for updating it's own Controls. You shouldn't be allowing anyone else to directly access them. Rather than allowing some other worker class to directly access the label or modify it's text, what's happening is that the worker is simply telling the form, "Hey, I've got some data, you can go update yourself accordingly based on these values." It is then the form that is responsible for updating itself. events are what you use to allow these workers, or other types of child elements (such as other forms you create, for example) to inform the "parent" form that it needs to update itself.
To write to any windows control, you must be on the UI thread. If you have a serial listener running on a different thread, then you need to switch threads before changing the windows control. The BeginInvoke can be handy, http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx.
What I would do, is add a Action to the serial listener that is called whenever the listener wants to display something. And then this Action would call BeginInvoke.
Something like:
static class SerialListner
{
public Action<string> SomethingToDisplay;
void GotSomethingToDisplay(string s)
{
SomethingToDisplay(s);
}
And then somewhere in your windows form
SerialListern.SomethingToDisplay = (s) =>
label.BeginInvoke((Action) () => label.Text = s);
I think you can use a background worker, and they are really easy to use.
In order to use a BackgroundWorker, you'll have to implement at least two events:
backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
there you read your input. It's triggered calling backgroundWorker1.RunWorkerAsync(...)
backgroundWorker1_ProgressChanged(....)
there you update your label. Maybe you'll have to create a delegate to update it.
you can also implement:
backgroundWorker1_RunWorkerCompleted(....)
to let you know when it stop...
Going on what you said about a static listener method and that it used to be a console application, I think a relatively minor modification might be the following:
class Program
{
static void Main(string[] args)
{
// Create a main window GUI
Form1 form1 = new Form1();
// Create a thread to listen concurrently to the GUI thread
Thread listenerThread = new Thread(new ParameterizedThreadStart(Listener));
listenerThread.IsBackground = true;
listenerThread.Start(form1);
// Run the form
System.Windows.Forms.Application.Run(form1);
}
static void Listener(object formObject)
{
Form1 form = (Form1)formObject;
// Do whatever we need to do
while (true)
{
Thread.Sleep(1000);
form.AddLineToTextBox("Hello");
}
}
}
In this case, Form1 is obviously the form class, and Listener is the listening method. The key here is that I'm passing the form object as an argument to the Listen method (via Thread.Start), so that the listener can access the non-static members of the GUI. Note that I've defined Form1.AddLineToTextBox as:
public void AddLineToTextBox(string line)
{
if (textBox1.InvokeRequired)
textBox1.Invoke(new Action(() => { textBox1.Text += line + Environment.NewLine; }));
else
textBox1.Text += line + Environment.NewLine;
}
Note especially that since now the Listener method is running in a separate thread, you need to use the Invoke method on the GUI control to make a change. I've used a lambda expression here, but if you're targeting an earlier version of .net you could use a full method just as easily. Note that my textBox1 is a TextBox with Multiline set to true and ReadOnly set to false (to be similar to a label).
An alternative architecture which may require more work but would probably be more elegant would be to do the opposite dependence relationship: you create the form with a reference to a Listener object. The listener will then raise events which the GUI would be subscribed to in order to update its display.

Invoking with textbox c#

Part of my program uses an event handler for the receive data of my serial port. The idea is when data is received that the text received is then added to the textbox (rx). I did not used to have this problem but something has changed and I can't figure out what. So now I am re-examining the way this is handled.
During the form load of my winform the last thing I do is
if (!serialPort1.IsOpen)
{
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
Then I have the event handler
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string indata1 = serialPort1.ReadExisting();
// rx.Text = " "; accidentally posted this. it was from trial and error.
rx.AppendText(Environment.NewLine + indata1);
}
When I run the program it stops at the rx.AppendText(Environment.NewLine + indata1); and gives the error
invalidoperationexception was unhandled: Control "accessed from a
thread other than the thread it was created on.
From what I have been able to read suggests that I need to use invoke or BeginInvoke.
I have never had problems appending the text before so now I can't understand why it's a problem. Also from what I have been reading on invoking i just don't understand it.
Can someone help me understand how to use the invoke instance for my situation? or perhaps show me another way of appending the text box?
Usually the exception you're seeing occurs when you run in debug mode, and if you run your application in release mode, you're unlikely to see the exception.
However, it is best to use invoke, as you have read. Something like this:
private delegate void RefreshTextBox();
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
//this event is raised in an event separate from UI thread,
//so InvokeRequired must be checked and Invoke called to update UI controls.
if (this.InvokeRequired) {
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
} else {
RefreshTextBoxResults();
}
}
private void RefreshTextBoxResults() {
string indata1 = serialPort1.ReadExisting();
rx.Text = " ";
rx.AppendText(Environment.NewLine + indata1);
}
The first time you see this invoke stuff, it's nearly impossible to follow, but take a close look and give it some time and it will make sense. Promise. :)
Updates in GUI applications should only be done on the GUI thread. Another thread attempting to update GUI components directly will result in either the error you described or in seemingly random behavior.
The role of Invoke & friends is to enable a secondary thread to safely forward GUI updates to the GUI thread, which will then process them from a queue.
In your case (assuming WinForms here):
rx.BeginInvoke(
(Action)(() =>
{
rx.AppendText(Environment.NewLine + indata1);
}));
BeginInvoke is asynchronous, so the thread calling it will not wait for the actual updates to be processed before moving on, while Invoke is synchronous.

C# windows forms custom controls cross-thread operation

I have one main windows form and within that form I have custom controls that represents different screens in application. I want to access this control's child controls. There's something I'm not getting here...sometimes I get this error:
Cross-thread operation not valid:
Control 'lblText' accessed from a thread
other than the thread it was created on.
but sometimes everything works OK. I don't completelly understand why the error...probably something with external device (MEI BillAcceptor) which has an event (inside Form1 class) that does the changes to the control... so let me write a simple code...
//user control
public partial class Screen2 : UserControl
{
public void changeValue(string txt)
{
lblText.Text = txt;
}
}
and the method changeValue is called from a form1 when particular event is rised...
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BillAcceptor.SomeBillAcceptorEvent +=
new SomeBillAcceptorEventHandler(changeText);
}
private void changeText(object sender, EventArgs args)
{
_screen2.changeValue("some text");
}
}
So the most annoying thing is that sometimes everything actually works... So my question is "do I have to use Invoke here?" or how do I solve this with less changes to the application...
In your handler. do something like this.
if (this.InvokeRequired)
{
Invoke(new MethodInvoker(() =>
{
_screen2.changeValue("some text");
}));
}
else
{
_screen2.changeValue("some text");
}
I would guess that the event is being raised on a seperate thread other that the main UI thread.
Yes you need to use Invoke if there is a possibility of that method being called from a different thread.
You can check this.InvokeRequired(), if true, then use invoke, if false do a normal call.
This occurs due to thread unsafe call
You should make only thread safe calls in program
Check this link.
The short answer is yes, you must use Invoke. See this question and its accepted answer if you need details.
The reason the exception is only thrown some of the time, by the way, comes down to timing. You currently have a race condition in which sometimes you get lucky and sometimes you don't.
By the way, here is pretty handy pattern for this sort of thing.
Refactor any code that sets form values into its own private void method(s).
In this new method, call InvokeRequired. If it returns true, call Invoke, passing the current method so as to recurse back into it. If it returns false, go ahead and make the change.
Call this new method from the event handler.
For example:
private void ChangeScreen2() {
if (this.InvokeRequired) {
this.Invoke(new MethodInvoker(ChangeScreen2));
}
else {
_screen2.changeValue("some text");
}
}
private void changeText(object sender, EventArgs args)
{
ChangeScreen2();
}
The idea being that you sequester all code that modifies the form into these methods that always begin with a check of InvokeRequired and always Invoke themselves if so required. This pattern works with .NET 1.0 onward. For even neater approach, see the accepted answer to this question, which works with .NET 3.0 and later.

Categories