I am attempting to bind a TextBox for error logging in Windows Forms.
I bind the TextBox like this:
this.operationEventHistoryTextbox.DataBindings.Add("Text", this.Logger, "OperationLog")
The Logger object is an instance of the Logger class, which contains the following property.
public string OperationLog
{
get
{
return this.operationLog;
}
set
{
if (this.operationLog != value)
{
this.operationLog = value;
System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new System.ComponentModel.PropertyChangedEventArgs("OperationLog"));
}
}
}
}
And I get the error when calling this.Logger.LogEvent("message")
. My LogEvent
method contains the following:
public void LogEvent(string msg)
{
this.OperationLog += System.DateTime.Now + ": " + msg + "\r\n";
}
The InvalidOperationException
says that there was an invalid Cross-thread operation, and that the Control operationEventHistoryTextBox
was accessed from a thread other than the thread it was created on.
I understand this to mean that I've written parts of my code in a way that wasn't thread-safe, but I don't really understand why, or what to fix.
I could just go around setting all of these functions to invoke rather than be directly called, but I'd like really understand what isn't working.
Update: I've attempted to use System.Threading.ScynchronizationContext
to raise the PropertyChanged Event on the correct thread, however, I continue to get the error. Here is the new setter for the property:
set
{
if (this.operationLog != value)
{
System.Threading.SynchronizationContext context = System.Threading.SynchronizationContext.Current
?? new System.Threading.SynchronizationContext();
this.operationLog = value;
context.Send(
(s) =>
{
System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new System.ComponentModel.PropertyChangedEventArgs("OperationLog"));
}
},
null);
}
}
Am I not correctly creating the SynchronizationContext
? Or is there something else at work here?
Update 2:
If I replace the call of handler(this, ... )
with handler(null, ... )
or handler(this.OperationLog)
, the setter will run without errors, but does not actually update the text.
For now I'm using a workaround where I will, instead of using a DataBinding to link the text, just manually do that by adding my own handler to the PropertyChanged Event.