When writing a demo for the Dutch Code-camp I ran into issues getting back on the UI thread after calling a webservice. The call to the webservice was made from the UI thread, but the callback was made on a different thread. The System.Threading.SynchronizationContext class held the solution.
The System.Threading.SynchronizationContext class is a base class that provides a thread-free context. It contains a read-only static property named Current, which gets the thread from which it is requested. When the current context is kept while the asynchronous call to a webservice is made, it can be used the call method in that context. The SynchronizationContext.Post can do this asynchronous, the SynchronizationContext.Send can do this synchronous.
Sample
Let me try to explain with a small sample which is taken from the code-camp demo.
The GetSearch(string searchText) method is called, which begins the call to the Bing API.
public class BingModel : IBingModel
{
const string BingRequestURL =
"http://api.bing.net/json.aspx?AppId={0}&Version=2.2&"
+"Market=en-US&Query={1}&Sources=web&Web.Count={2}";
private WebRequest request;
public void SearchBing(string searchText, string appId)
{
string requestString =
string.Format(BingRequestURL,
appId,
HttpUtility.UrlEncode(searchText),
20);
request = HttpWebRequest.Create(requestString);
state so it can be handled by the result.
request.BeginGetResponse(OnRequestCompleted,
SynchronizationContext.Current);
}
private void OnRequestCompleted(IAsyncResult ar)
{
var webResponse = (HttpWebResponse)request.EndGetResponse(ar);
var response = new StreamReader(webResponse.GetResponseStream());
var json = JsonObject.Parse(response.ReadToEnd());
IEnumerable<BingResult> ress = ExtractRestults(json);
InvokeSearchBingCompletedEvent(ar.AsyncState as SynchronizationContext, ress);
}
private void InvokeSearchBingCompletedEvent(SynchronizationContext context,
IEnumerable<BingResult> ress)
{
if (context != null)
context.Post((e) =>
{
if (SearchBingCompleted != null)
SearchBingCompleted(this, new SearchBingCompletedArgs()
{
SearchResults = ress.ToList()
}
);
}, null);
}
private static IEnumerable<BingResult> ExtractRestults(JsonValue json)
{
var results = json["SearchResponse"]["Web"]["Results"] as JsonArray;
return from res in results
select new BingResult()
{
Title = res["Title"],
Url = res["Url"]
};
}
public event EventHandler<SearchBingCompletedArgs> SearchBingCompleted;
}