PDA

Click to See Complete Forum and Search --> : [RESOLVED] Cross-Thread operation when accessing a Controll in an event raised by a Thread.


BramVandenbon
Nov 21st, 2006, 12:50 PM
In short:
I am getting an error because I am trying to access a control from an eventhandler raised by a Thread.

Longer explanation:
Hi, this is a small piece of code of a class that does some heavy calculations in a seperate thread and raises events each time new results are available.


public abstract class MyClass
{
public event EventHandler NewResults;
private int intResult;

public void DoStuff()
{
Thread th = new Thread(new ThreadStart(Calculate));
th.Priority = ThreadPriority.Lowest;
th.Start();
}

private void Calculate()
{
while(true){
//...
//calculations on intResult here
//...

OnNewResult(this);
}
}

public int Result
{
get { return intResult; }
}

protected void OnNewResults(object sender)
{
if (NewResults != null)
NewResults(sender, new EventArgs());
}
}

(My real code is a lot more complicated. It is actually an encryption algorithm I wrote and the event will be used to update a progressbar.)

Is the way I programmed the raising of the event inside the thread ok? I have the feeling I am doing something wrong here.

The exception however is not thrown in the above code though. I get it in the following part of code, where I handle the event.

/* somewhere in the constructor I put
*
* MyClass c = new MyClass();
* c.NewResults += new EventHandler(NewResults);
* c.DoStuff();
/*
void NewResults(object sender, EventArgs e)
{
MyClass c = (MyClass)sender;
txtTextBox.Text = c.Result.ToString();
}

This gives me a "cross-thread operation error because the txtTextBox was created in another thread".

I am sure that this error most be something that also other people were confronted with. What is the cleanest and usual way to solve this?

Some ideas
I read something about using the Control.Invoke(new InvokeMethod(delegate)) method. Is that the way to do it? If so, then which delegate should I use for it?

Either way, I do not really think this is the way to solve the problem. The problem should be solved inside the MyClass class. The client side should not care about the fact that a thread has been used inside another class.

I hope somebody has some experience with this.

Thank you in advance once again
BramGo

the182guy
Nov 21st, 2006, 08:02 PM
I have this same problem and cant fix it, my user control that I use must create a new thread, and this DLL raises events for the user control, and it basically wont allow me to access the forms controls from the event handler:(

jmcilhinney
Nov 21st, 2006, 10:58 PM
This is an oft-asked and oft-answered question. A search of the forum would yield numerous examples.

If you don't need to pass any arguments then you can use the existing MethodInvoker delegate. If you do need to pass arguments then you need to declare your own delegate, e.g.Private Delegate Sub SetTextBoxTextDelegate(ByVal text As String)

Private Sub SetTextBoxText(ByVal text As String)
If Me.TextBox1.InvokeRequired Then
'This is a worker thread so create a delegate to cross the thread boundary.
Me.TextBox1.Invoke(New SetTextBoxTextDelegate(AddressOf SetTextBoxText), text)
Else
'This is the UI thread so access the control's members directly.
Me.TextBox1.Text = text
End If
End Sub

BramVandenbon
Nov 22nd, 2006, 04:54 PM
Thank you for answering my question jmcilhinney,

I did indeed search on the forum, on google and on codetalk. I found a lot of results and indeed it resembled the code you are showing here.

However I am assured there must be another way to solve this problem. I think from an architectural point of view you can not expect the user of a class to care about technical details of the class (such as in this case: the use of a thread). The problem should be fixed from inside the class so that the user can just program the Eventhandling method without caring about cross-thread operations.

Does anybody know a way to solve this problem from inside the class which starts the thread?

BramVandenbon
Nov 22nd, 2006, 05:51 PM
I solved it by using the System.ComponentModel.BackgroundWorker class instead of implementing threads. It's a very clean way of working and I was able to reuse most of my code.

Thank you for all your efforts, ideas and help !