Reputation: 430
I have a class like that "ClientSocket.cs"
class ClientSocket {
public delegate void ConnectHandler(object sender, EventArgs e);
public event ConnectHandler ConnectEvent = delegate { };
protected void OnConnectEvent(EventArgs e) {
ConnectHandler ev = ConnectEvent;
ev(this, e);
}
}
And another class "myForm.cs"
public partial class myForm : Form {
private ClientSocket client;
private void button1_Click(object sender, EventArgs e) {
client = new ClientSocket();
client.ConnectEvent += myForm_OnConnectEvent;
client.connect();
}
// Handler for ConnectEvent
private void myForm_OnConnectEvent(object sender, EventArgs e) {
//this.BeginInvoke((MethodInvoker)delegate { writeLog("Connected"); });
writeLog("Connected");
}
// Function that write a log string to a TextBox
public writeLog(string log) {
guiTextBox.AppendText(log);
}
}
Here the question. I try to call writeLog with BeginInvoke or calling it directly. Sometimes I get an InvalidOperationException when writing to guiTextBox. I don't understand why I receive that message. The event is fired by ClientSocket object, but the event handler is relative to the main UI-thread (myForm).
Can I avoid to use BeginInvoke/Invoke for each EventHandler of my class?
EDIT: I understand what's the difference, now I'm try to understand the best practice for calling the event.
Should I put the BeginInvoke/Invoke method when RAISING the event in the BASE class (ClientSocket in that case)
protected void OnConnectEvent(EventArgs e) {
ConnectHandler ev = ConnectEvent;
this.BeginInvoke((MethodInvoker)delegate { ev(this, e);});
}
or should I put that WHEN I'm using that object and add a listeners to that handler
// Handler for ConnectEvent used in GUI (myForm)
private void myForm_OnConnectEvent(object sender, EventArgs e) {
this.BeginInvoke((MethodInvoker)delegate { writeLog("Connected"); });
}
Cheers
Upvotes: 2
Views: 1111
Reputation: 127593
the this.BeginInvoke
inside ClientSocket
does not exist. To be able to do the BeginInvoke
it must be called on a object that has that method (your form in your case).
If you wanted the invoking to happen inside your ClientSocket class you would need to pass in a Control
that has the BeginInvoke
function.
However if I where writing this I would not do this approach. It adds a unnecessary requirement to ClientSocket
that you must have a Control
passed in (this is called Tightly Coupling and you should try to avoid it in your programming). Personally I would let the event pass along in whatever thread it wants to be raised in and let the consumer worry about doing any special invoking (if they even need to at all).
Here is how I would write myForm_OnConnectEvent
, this pattern checks to see if we need to invoke and if we do it calls the function again with the same arguments but this time on the UI thread.
// Handler for ConnectEvent used in GUI (myForm)
private void myForm_OnConnectEvent(object sender, EventArgs e)
{
if(this.InvokeRequired)
{
this.BeginInvoke(new ConnectHandler(myForm_OnConnectEvent), new object[] {sender, e});
return;
}
writeLog("Connected");
}
As a side note, I don't know what writeLog
is doing (it should have a capital W by the way) but if it is not interacting with the UI you don't need to do any invoking at all. If it interacts with a TextBox
or something else on the UI, that is where I would do my invoking.
private void myForm_OnConnectEvent(object sender, EventArgs e)
{
writeLog("Connected");
}
private void writeLog(string logMessage)
{
if(logTextBox.InvokeRequired)
{
logTextBox.BeginInvoke(new Action<string>(writeLog), logMessage);
return;
}
var logLine = String.Format("{0:g}: {1}{2}", DateTime.Now, logMessage, Enviorment.NewLine);
logTextBox.AppendText(logLine);
}
Upvotes: 3
Reputation: 37780
The event handler is declared in myForm
, but the thread, which executes handler, is defined by the logic of ClientSocket
class. If this will be background thread, event handler will be raised from background thread, so, you'll need BeginInvoke
to avoid cross-thread access to controls.
In other words: belonging of any method of any type isn't related to the thread, which will ever execute this method. These things (types and threads) are parallel universes.
By the way, you can replace this:
public delegate void ConnectHandler(object sender, EventArgs e);
public event ConnectHandler ConnectEvent = delegate { };
with this:
public event EventHandler ConnectEvent;
There's no need to make yet another delegate type.
Upvotes: 3