private void cmbPlatform_SelectedIndexChanged(object sender, EventArgs e)
{
string platform = cmbPlatform.Text;
UpcomingGameFinder gameFinder = new UpcomingGameFinder();
Task.Factory.StartNew<IEnumerable<Game>>(() => gameFinder.FindUpcomingGamesByPlatform(platform))
.ContinueWith((i) => PlaceGameItemsInPanel(i.Result));
}
private void PlaceGameItemsInPanel(IEnumerable<Game> games)
{
int yPosition = 0;
foreach (var game in games)
{
GameItem item = new GameItem(game);
item.Location = new Point(0, yPosition);
panelGameItemsHolder.Controls.Add(item);
yPosition += 125;
}
}
Obviously, I'm doing something wrong here, my mind if pretty new to Tasks, so I'm not sure how it would work in this context.
Basically, FindUpcomingGames() takes a long time to finish, I want to grab the returned collectio开发者_开发技巧n from that method, and create N amount of GameItem objects (which are user controls) and place them in a panel I have in my Windows Forms.
I get this error message:
Cross-thread operation not valid: Control 'panelGameItemsHolder' accessed from a thread other than the thread it was created on.
Any suggestions?
The PlaceGameItemsInPanel method needs to switch over the UI to interact with UI items. The easiest way to do that is to make your ContinueWith happen on the UI thread. i.e.
private void cmbPlatform_SelectedIndexChanged(object sender, EventArgs e)
{
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
string platform = cmbPlatform.Text;
UpcomingGameFinder gameFinder = new UpcomingGameFinder();
Task.Factory.StartNew<IEnumerable<Game>>(() => gameFinder.FindUpcomingGamesByPlatform(platform))
.ContinueWith((i) => PlaceGameItemsInPanel(i.Result), uiContext);
}
This assumes that cmbPlatform_SelectedIndexChanged is happening on the UI thread (which it will be if it's handling an event from a combo box, as it seems to be here). So you can get a reference to current SynchronizationContext, then use that in the ContinueWith to make sure that runs on the correct SynchronizationContext.
You can read more about this over at http://blogs.msdn.com/b/pfxteam/archive/2009/09/22/9898090.aspx
You would do your work in the .ContinueWith
delegate ... after you create your object, add it to some collection (ideally an observable collection), which would automatically update the UI. of course if you don't use a bound observable collection, you'd have to update the UI controls yourself (just be careful with cross-thread communication)
You're using the tasks fine, but I think you might need to rethink your workflow. If FindUpcomingGames() takes a long time, you either want to start it in the background as early as possible and cache the results, or refactor it so its faster. As you are using tasks now, you aren't really working asynchronously. You are just starting a task then waiting for it to finish.
精彩评论