Ali Tor Ali Tor - 4 years ago 131
C# Question

Control text update in event throws illegal cross thread exception

I am working on a Youtube search by keyword programmatically and using Youtube API to do this. I want to fire an event when a search progress is completed and return the result in

YoutubeSearchCompletedEventArgs
sent by
YoutubeSearchCompleted
.

But the code in
YoutubeSearchCompleted
in
Form.cs
throws cross thread illegal operation exception. Normally, using
AsyncOperation.Post
method it must not throw
InvalidOperationException
. Because I used the same method in a download manager project before and it worked well. So I can't understand why this happens.


Youtube search class


class YouTubeManager
{
public delegate void YoutubeSearchCompletedEventHandler(object sender, YoutubeSearchCompletedEventArgs e);
public event YoutubeSearchCompletedEventHandler YoutubeSearchCompleted;
AsyncOperation aop = AsyncOperationManager.CreateOperation(null);

List<YoutubeVideo> SearchByKeyword(string keyword)
{
List<YoutubeVideo> videos = new List<YoutubeVideo>();

//.......
//...Youtube data api search codes....
//.......

return videos;
}
public void Search(string keyword)
{
Task.Run(() =>
{
List<YoutubeVideo> list = SearchByKeyword(keyword);
aop.Post(new System.Threading.SendOrPostCallback(delegate
{
if (YoutubeSearchCompleted != null)
YoutubeSearchCompleted(this,
new YoutubeSearchCompletedEventArgs(keyword, list);
}), null);
});
}
}



Form.cs


public partial class Form1 : Form
{
YouTubeManager yam = new YouTubeManager();
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
}

void Form1_Load(object sender, EventArgs e)
{
yam.YoutubeSearchCompleted += yam_YoutubeSearchCompleted;
yam.Search("Blues");
}

void yam_YoutubeSearchCompleted(object sender, YoutubeSearchCompletedEventArgs e)
{
if (e.Videos.Count < 1) return;

textBox1.Text = e.Videos[0].Title();
}
}


In this code the
textBox1.Text = e.Videos[0].Title();
line throws
InvalidOperationException
. How can I fix this problem?

Note: I don't want
Invoke
method, just
AsyncOperation
.

Answer Source

Most likely the issue is caused by AsyncOperation created too early. You can check that with the following:

if (!(aop.SynchronizationContext is WindowsFormsSynchronizationContext))
{
    // Oops - we have an issue
}

Why is that? The AsyncOperation stores the SynchronizationContext.Current at the construction time, and normally all Control derived classes (including the Form) install WindowsFormsSynchronizationContext from inside the Control class constructor.

But imagine the Forgm1 is your startup form (e.g. the typical Application.Run(new Form1()); call from Main). Since Any instance variable initializers in the derived class are executed before the base class constructor, at the time aop variable is initialized (through yam field initializer), the Control class constructor has not been ran yet, hence the WindowsFormsSynchronizationContext is not installed, so the AsynOperation is initialized with the default SynchronozationContext, which implements Post by simply executing it on separate thread.

The fix is simple - don't use initializer, just define the field

YouTubeManager yam;

and move the initialization

yam = new YouTubeManager();

inside the form constructor or load event.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download