I have a Windows Phone 7 application that is using Silverlight with C#. This application has a method that fires off multiple web service requests. At another point in my code, I have some code that looks like the following:
myProgressBar.Visibility = Visibility.Visible;
while (AreWebServiceCallsDone() == false)
{
// Need "waiting" code here
}
// Proceed
How do I make my UI "wait" a small amount of time, without locking up the UI, and then check again to see if my web service calls are done?
Thank you!
The answer is to not make the UI wait at all but to "go with the (asynchronous) flow", as it were.
Ultimately, the async stuff in C# 5 will solve this particular problem, but there's no timeline for it's release for the desktop CLR let alone Silverlight or WP7.
I'd personally recommend looking into Microsoft.Phone.Reactive, which is the WP7 version of the Reactive Extensions (Rx) and ships with the SDK. It's a pretty big subject that takes a fair amount of time to get your head around, but can really simplify how you deal with asynchronous scenarios.
Assuming each of your web services return different types of data, I would:
Do to "peek" at the message and perform your side effects (like assigning the value locally)Select to "normalize" the types so that they are all of the same type (required for the next step)ForkJoin to execute each of the requests in parallel and to handle when each has completed1 Creating an IObservable for your request really depends on how asynchronous pattern you are using. Assuming you're using WebClient, here's an extension method that creates an Observable from DownloadStringAsync as a sample (it may look complex but it's just handling errors and cancellation):
public static class ObservableWebClient
{
public static IObservable<string> DownloadStringObservable(
this WebClient webClient, Uri uri)
{
return Observable.Create(observer =>
{
var disposable = new CompositeDisposable();
var completedObservable = Observable.FromEvent<
DownloadStringCompletedEventHandler,
DownloadStringCompletedEventArgs
>(
h => new DownloadStringCompletedEventHandler(h),
h => webClient.DownloadStringCompleted += h,
h => webClient.DownloadStringCompleted h= h
);
disposable.Add(completedObservable
.SelectMany(ev =>
{
return (ev.EventArgs.Error != null)
? Observable.Throw<string>(ev.EventArgs.Error)
: Observable.Return(ev.EventArgs.Result);
})
.Subscribe(observer));
disposable.Add(Disposable.Create(
() => webClient.CancelAsync()));
return disposable;
});
}
}
You can then use it like so:
Note that I've skipped the Do + "normalizing" steps because my data types are all the same (String). As such, I can subscribe to them all as an array (it's a subtlety of how ForkJoin works, if you were wondering)
var webClientA = new WebClient();
var webClientB = new WebClient();
var webClientC = new WebClient();
Observable.ForkJoin(
webClientA.DownloadStringObservable(uriA),
webClientB.DownloadStringObservable(uriB),
webClientC.DownloadStringObservable(uriC),
)
.ObserveOnDispatcher()
.Subscribe(dataArray =>
{
// All three have completed
this.DataA = dataArray[0];
this.DataB = dataArray[1];
this.DataC = dataArray[2];
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With