Infinite wait loading remote image into BitmapImage() in Background Agent - windows-phone-8

I have a valid URL for a remote JPEG which I'm trying to load in the background. But I find I never get control back after invoking the BitmapImage() constructor. My question is, should this approach work, or should I pitch it all, load up BcpAsync project from NuGet and start working with WebClient asynch methods?
A sample URL for which it fails is
http://image.weather.com/images/maps/current/garden_june_720x486.jpg
It is valid. .UpdateAsync() references it from AppViewModel.Instance, it's not explicitly referenced here.
Here's the background agent:
protected override async void OnInvoke(ScheduledTask task)
{
AppViewModel.LoadData();
await AppViewModel.Instance.RemoteImageProxy.UpdateAsync();
AppViewModel.Instance.ImageUrl = AppViewModel.Instance.RemoteImageProxy.LocalFileUri;
AppViewModel.Instance.UpdateCount++;
PinnedTile.Update();
}
AppViewModel.SaveData();
#if DEBUG
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(AppViewModel.Instance.BgAgentInterval));
#endif
NotifyComplete();
}
Here's the invoked method:
public Task<double> UpdateAsync() {
LastCheckedTime = DateTime.UtcNow;
CompletionTask = new TaskCompletionSource<double>();
// Not usually called on UI thread, not worth optimizing for that case here.
Deployment.Current.Dispatcher.BeginInvoke(() => { //todo determine whether System.Windows.Deployment.Dispatcher can be called from main app, or just bgAgent.
HelperImageControl = new Image();
HelperImageControl.Loaded += im_Loaded;
HelperImageControl.ImageFailed += im_ImageFailed;
HelperImageControl.ImageOpened += im_ImageOpened;
// breakpoint here
HelperImageControl.Source = new BitmapImage(SourceUri);
// stepping over the function, control does not return here. Nor are any of the above events fired.
});
return CompletionTask.Task; // this will be completed in one of the subsequent control events...
}

You need to call CompletionTask.SetResult(); to return control back to the caller method.
This works (I'm returning 100 in case of successful download because you set the task to return double).
TaskCompletionSource<double> CompletionTask;
public Task<double> UpdateAsync()
{
CompletionTask = new TaskCompletionSource<double>();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var HelperImageControl = new Image();
var bmp = new BitmapImage();
bmp.ImageOpened += bmp_ImageOpened;
bmp.ImageFailed += bmp_ImageFailed;
bmp.CreateOptions = BitmapCreateOptions.None;
bmp.UriSource = new Uri("http://image.weather.com/images/maps/current/garden_june_720x486.jpg", UriKind.Absolute);
HelperImageControl.Source = bmp;
});
return CompletionTask.Task;
}
void bmp_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
CompletionTask.SetException(e.ErrorException);
}
void bmp_ImageOpened(object sender, RoutedEventArgs e)
{
CompletionTask.SetResult(100);
}

Related

FirstRun info in JSON (WP)

i have a problem with the info of a JSON in Windows Phone.
I want to show if the app is running for the first time, and if not, don't show anything.
This is my function to show info on the JSON:
async void NavigationService_Navigated(object sender, NavigationEventArgs e)
{
if (e.IsNavigationInitiator
|| !e.IsNavigationInitiator && e.NavigationMode != NavigationMode.Back)
{
var navigationInfo = new
{
Mode = e.NavigationMode.ToString(),
From = this.BackStack.Any() ? this.BackStack.Last().Source.ToString() : string.Empty,
Current = e.Uri.ToString(),
};
var jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(navigationInfo);
await this.currentApplication.Client.PageView(jsonData);
}
}
I want to add one more thing where is Mode, From and Current. I want to add IsFirstRun that give as True if it's the first time i open the app.
I've seen this for firstRun function, but i don't know how to put it in my code.
public static bool IsFirstRun()
{
if (!settings.Contains(FIRST_RUN_FLAG)) //First time running
{
settings.Add(FIRST_RUN_FLAG, false);
return true;
}
return false;
}
I need help... thanks!
It is pretty simple if you want to create a flag for First Run,
In App.xaml.cs
Look for a function named
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}
What we want to do is create a flag inside this function and only set it to true if it doesn't exist. Like so.
using System.IO.IsolatedStorage; // include this namespace in App.xaml.cs
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
if (!IsolatedStorageSettings.ApplicationSettings.Contains("first_run"))
{
IsolatedStorageSettings.ApplicationSettings.Add("first_run", true);
}
else
{
// set the flag to flase
IsolatedStorageSettings.ApplicationSettings["first_run"] = false;
}
// save
IsolatedStorageSettings.ApplicationSettings.Save();
}
Now if you want to do something on first run all you have to do is check the settings again like so:
bool first_run = (bool) IsolatedStorageSettings.ApplicationSettings["first_run"];
For debugging cases you will probably want to remove the flag so it will hit first_run again by doing this
// remove the flag and save
IsolatedStorageSettings.ApplicationSettings.Remove("first_run");
IsolatedStorageSettings.ApplicationSettings.Save();

Wait until async operation ends Windows Phone

I am trying to parse some pois from a xml download from a server and I saw that it is done after the program continues in the main thread. I haven't found a way to solve it because I need it.
using System.Threading;
namespace XML_Parser
{
class XMLParserPOI_Wiki
{
private static XMLParserPOI_Wiki objSingle = new XMLParserPOI_Wiki();
public static XMLParserPOI_Wiki ObjSingle
{
get { return objSingle; }
set { objSingle = value; }
}
private List<POI> places;
public List<POI> Places
{
get { return places; }
}
private XMLParserPOI_Wiki()
{
}
public void parseWikitude(string url)
{
places = new List<POI>();
WebClient wc = new WebClient();
wc.DownloadStringCompleted += HttpsCompleted;
wc.DownloadStringAsync(new Uri(url));
}
private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
XDocument xdoc = XDocument.Parse(e.Result, LoadOptions.None);
XNamespace ns = "http://www.opengis.net/kml/2.2";
XNamespace ns2 = "http://www.openarml.org/wikitude/1.0";
var placemarkers = xdoc.Root.Descendants(ns + "Placemark");
places =
(from query in xdoc.Root.Descendants(ns + "Placemark")
select new POI
(
...
)).ToList();
System.Diagnostics.Debug.WriteLine("Lista");
System.Diagnostics.Debug.WriteLine(places.Count);
}
}
}
}
In my main class:
XMLParserPOI_Wiki parserXML = XMLParserPOI_Wiki.ObjSingle;
parserXML.parseWikitude("http://myurl.php");
System.Diagnostics.Debug.WriteLine("Lista de pois");
System.Diagnostics.Debug.WriteLine(parserXML.Places.Count);
for (int i = 0; i < parserXML.Places.Count; i++)
{
System.Diagnostics.Debug.WriteLine(parserXML.Places[i].getName());
}
It prints Lista de POis and 0, before Lista and X (number of pois)
I guess I should freeze main thread but I tried a couple of times with some examples and they didn't work.
Can you point me to any tutorial about this? More than get an answer I want to understand how to deal with this kind of operations
First of all, you don't want to block (freeze) the UI thread EVER!
This is called asynchronous programming. There are two things you can do to solve your problem (I recommend option 2!):
Use the classic callback model. You basically call some long operation on a background thread and give a function to it, to execute when the long operation is done. Here's how to do it in your case.
At the end of the HttpsCompleted method, invoke what you need on the UI Thread using:
Deployment.Current.Dispatcher.BeginInvoke(delegate() {
//The code here will be invoked on the UI thread
});
If you want to make the parseWikitude method reusable, you should pass an Action to it. This way you can call it from multiple places and tell it what to do on the UI thread when the parsing is done. Something like this:
public void parseWikitude(string url, Action callback) {
places = new List<POI>();
WebClient wc = new WebClient();
wc.DownloadStringCompleted += HttpsCompleted;
wc.DownloadStringAsync(new Uri(url), callback);
}
private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
...
var callback = (Action)e.UserState;
Deployment.Current.Dispatcher.BeginInvoke(callback);
}
}
//And then when you use it, you do it like that
parserXML.parseWikitude("http://myurl.php", delegate() {
//The code here will be executed on the UI thread, after the parsing is done
});
Use the (rather) new asnyc pattern in .NET. You should read about this, as it is one of the best features of .NET if you ask me. :) It basically does the callback thing automatically and makes the code a lot easier to read/maintain/work-with. Once you get used to it, that is.
Here's an example:
public Task<List<POI>> parseWikitude(string url) {
TaskCompletionSource<List<POI>> resultTaskSource = new TaskCompletionSource<List<POI>>();
WebClient wc = new WebClient();
wc.DownloadStringCompleted += HttpsCompleted;
wc.DownloadStringAsync(new Uri(url), resultTaskSource);
return resultTaskSource.Task;
}
private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
//If needed, run the code here in a background thread
//...
var resultTaskSource = (TaskCompletionSource<List<POI>>)e.UserState;
resultTaskSource.SetResult(places);
}
}
//And when you need to use it, do it like that (note, this must be invoked in an async method!)
var places = await parser.parseWikitude("http://myurl.php");
//The code here will be executed on the same thread when the parsing is done, but the thread will not be blocked while the download is happening.
So, these are the two ways you can handle it. Option one is old-school, classic and easy. Option two is the new and cool way of doing async stuff. It really is a must-know. Simplifies a lot of things once you get used to it.
P.S. Sorry if I got carried away. :D

windows phone 8: how to download xml file from web and save it to local?

I would like to download a xml file from web, then save it to the local storage but I do not know how to do that. Please to help me clearly or give me an example. Thank you.
Downloading a file is a huge subject and can be done in many ways. I assume that you know the Uri of the file you want to download, and want you mean by local is IsolatedStorage.
I'll show three examples how it can be done (there are also other ways).
1. The simpliest example will dowload string via WebClient:
public static void DownloadFileVerySimle(Uri fileAdress, string fileName)
{
WebClient client = new WebClient();
client.DownloadStringCompleted += (s, ev) =>
{
using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
using (StreamWriter writeToFile = new StreamWriter(ISF.CreateFile(fileName)))
writeToFile.Write(ev.Result);
};
client.DownloadStringAsync(fileAdress);
}
As you can see I'm directly downloading string (ev.Result is a string - that is a disadventage of this method) to IsolatedStorage.
And usage - for example after Button click:
private void Download_Click(object sender, RoutedEventArgs e)
{
DownloadFileVerySimle(new Uri(#"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
}
2. In the second method (simple but more complicated) I'll use again WebClient and I'll need to do it asynchronously (if you are new to this I would suggest to read MSDN, async-await on Stephen Cleary blog and maybe some tutorials).
First I need Task which will download a Stream from web:
public static Task<Stream> DownloadStream(Uri url)
{
TaskCompletionSource<Stream> tcs = new TaskCompletionSource<Stream>();
WebClient wbc = new WebClient();
wbc.OpenReadCompleted += (s, e) =>
{
if (e.Error != null) tcs.TrySetException(e.Error);
else if (e.Cancelled) tcs.TrySetCanceled();
else tcs.TrySetResult(e.Result);
};
wbc.OpenReadAsync(url);
return tcs.Task;
}
Then I'll write my method downloading a file - it also need to be async as I'll use await DownloadStream:
public enum DownloadStatus { Ok, Error };
public static async Task<DownloadStatus> DownloadFileSimle(Uri fileAdress, string fileName)
{
try
{
using (Stream resopnse = await DownloadStream(new Uri(#"http://filedress/myfile.txt", UriKind.Absolute)))
using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
{
if (ISF.FileExists(fileName)) return DownloadStatus.Error;
using (IsolatedStorageFileStream file = ISF.CreateFile(fileName))
resopnse.CopyTo(file, 1024);
return DownloadStatus.Ok;
}
}
catch { return DownloadStatus.Error; }
}
And usage of my method for example after Button click:
private async void Downlaod_Click(object sender, RoutedEventArgs e)
{
DownloadStatus fileDownloaded = await DownloadFileSimle(new Uri(#"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
switch (fileDownloaded)
{
case DownloadStatus.Ok:
MessageBox.Show("File downloaded!");
break;
case DownloadStatus.Error:
default:
MessageBox.Show("There was an error while downloading.");
break;
}
}
This method can have problems for example if you try to download very big file (example 150 Mb).
3. The third method - uses WebRequest with again async-await, but this method can be changed to download files via buffer, and therefore not to use too much memory:
First I'll need to extend my Webrequest by a method that will asynchronously return a Stream:
public static class Extensions
{
public static Task<Stream> GetRequestStreamAsync(this WebRequest webRequest)
{
TaskCompletionSource<Stream> taskComplete = new TaskCompletionSource<Stream>();
webRequest.BeginGetRequestStream(arg =>
{
try
{
Stream requestStream = webRequest.EndGetRequestStream(arg);
taskComplete.TrySetResult(requestStream);
}
catch (Exception ex) { taskComplete.SetException(ex); }
}, webRequest);
return taskComplete.Task;
}
}
Then I can get to work and write my Downloading method:
public static async Task<DownloadStatus> DownloadFile(Uri fileAdress, string fileName)
{
try
{
WebRequest request = WebRequest.Create(fileAdress);
if (request != null)
{
using (Stream resopnse = await request.GetRequestStreamAsync())
{
using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
{
if (ISF.FileExists(fileName)) return DownloadStatus.Error;
using (IsolatedStorageFileStream file = ISF.CreateFile(fileName))
{
const int BUFFER_SIZE = 10 * 1024;
byte[] buf = new byte[BUFFER_SIZE];
int bytesread = 0;
while ((bytesread = await resopnse.ReadAsync(buf, 0, BUFFER_SIZE)) > 0)
file.Write(buf, 0, bytesread);
}
}
return DownloadStatus.Ok;
}
}
return DownloadStatus.Error;
}
catch { return DownloadStatus.Error; }
}
Again usage:
private async void Downlaod_Click(object sender, RoutedEventArgs e)
{
DownloadStatus fileDownloaded = await DownloadFile(new Uri(#"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
switch (fileDownloaded)
{
case DownloadStatus.Ok:
MessageBox.Show("File downloaded!");
break;
case DownloadStatus.Error:
default:
MessageBox.Show("There was an error while downloading.");
break;
}
}
Those methods of course can be improved but I think this can give you an overview how it can look like. The main disadvantage of these methods may be that they work in foreground, which means that when you exit your App or hit start button, downloading stops. If you need to download in background you can use Background File Transfers - but that is other story.
As you can see you can reach your goal in many ways. You can read more about those methods on many pages, tutorials and blogs, compare an choose the most suitable.
Hope this helps. Happy coding and good luck.

Windows Phone link from Tile error

I have a list of theaters and I created a secondary tile from my application to navigate directly to specific theater. I pass the id of the theater in query string :
I load the theaters from a WCF service in the file "MainViewModel.cs"
In my home page, I have a list of theaters and I can navigate to a details page.
But when I want to navigate from the tile, I have an error...
The Tile :
ShellTile.Create(new Uri("/TheaterDetails.xaml?selectedItem=" + theater.idTheater, UriKind.Relative), tile, false);
My TheaterDetails page :
public partial class TheaterDetails : PhoneApplicationPage
{
theater theater = new theater();
public TheaterDetails()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
if (DataContext == null)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
theater = (from t in App.ViewModel.Theaters
where t.idTheater == index
select t).SingleOrDefault();
DataContext = theater;
....
....
....
The error :
https://dl.dropboxusercontent.com/u/9197067/error.png
Like if the data were not loaded...
Do you have an idea where the problem come from ?
The solution could be easy but I am a beginner... Maybe it's because I load the data asynchronously and the application doesn't wait until it's done...
Thanks
EDIT :
My LoadData() method :
public void LoadData()
{
client.GetTheatersCompleted += new EventHandler<ServiceReference1.GetTheatersCompletedEventArgs>(client_GetTheatersCompleted);
client.GetTheatersAsync();
// Other get methods...
this.IsDataLoaded = true;
}
private void client_GetTheatersCompleted(object sender, ServiceReference1.GetTheatersCompletedEventArgs e)
{
Theaters = e.Result;
}
You should check to see which variable is actually null. In this case it looks to be Theaters (otherwise the error would have thrown earlier).
Since Theaters is populated from a web call it is most likely being called asynchronously, in other words when you return from LoadData() the data is not yet there (it's still waiting for the web call to come back), and is waiting for the web service to return its values.
Possible solutions:
Make LoadData() an async function and then use await LoadData(). This might require a bit of rewriting / refactoring to fit into the async pattern (general introduction to async here, and specific to web calls on Windows Phone here)
A neat way of doing this that doesn't involve hacks (like looping until the data is there) is to raise a custom event when the data is actually populated and then do your Tile navigation processing in that event. There's a basic example here.
So the solution that I found, thanks to Servy in this post : Using async/await with void method
I managed to use async/await to load the data.
I replaced my LoadData() method by :
public static Task<ObservableCollection<theater>> WhenGetTheaters(ServiceClient client)
{
var tcs = new TaskCompletionSource<ObservableCollection<theater>>();
EventHandler<ServiceReference1.GetTheatersCompletedEventArgs> handler = null;
handler = (obj, args) =>
{
tcs.SetResult(args.Result);
client.GetTheatersCompleted -= handler;
};
client.GetTheatersCompleted += handler;
client.GetTheatersAsync();
return tcs.Task;
}
public async Task LoadData()
{
var theatersTask = WhenGetTheaters(client);
Theaters = await theatersTask;
IsDataLoaded = true;
}
And in my page :
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
await App.ViewModel.LoadData();
}

ASync JSON REST call problem with MVVM

I am trying to implement the MVVM patter for my WP7 Silverlight app and I am running into a problem with the async JSON Rest call. I moved into my ViewModel class the following two methods that were on my WP7 app Page.
public void FetchGames()
{
ObservableCollection<Game> G = new ObservableCollection<Game>();
//REST call in here
var webClient = new WebClient();
Uri uri = new Uri("http://www.somewebsite.com/get/games/league/" + league);
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedGames);
webClient.OpenReadAsync(uri);
}
private void OpenReadCompletedGames(object sender, OpenReadCompletedEventArgs e)
{
DataContractJsonSerializer ser = null;
ser = new DataContractJsonSerializer(typeof(ObservableCollection<Game>));
Games = ser.ReadObject(e.Result) as ObservableCollection<Game>;
this.IsDataLoaded = true;
}
Now the problem is that because it is an async call the following code does not work. The following code is on my app Page.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigationContext.QueryString.TryGetValue("league", out league))
{
try
{
App.gViewModel.league = league;
App.gViewModel.FetchGames();
if(App.gViewModel.IsDataLoaded)
{
lbTeams.ItemsSource = App.gViewModel.Games;
}
}
catch ()
{
//error logging in here
}
}
}
Stepping thru the code shows that FetchGames is called then hits the next line ( if(App.gViewModel.IsDataLoaded)
) before the async call is finished. So IsDataLoaded is always false and I cant bind the listbox on the page.
Doing a lot of googleing I have some possible solutions but I am unable convert them to my particular problem. One is like this and it has to do with continuation passing style'. I couldn't get it to work tho and would greatly appreciate some help.
Thanks!
void DoSomethingAsync( Action<string> callback ) {
HttpWebRequest req; // TODO: build your request
req.BeginGetResponse( result => {
// This anonymous function is a closure and has access
// to the containing (or enclosing) function.
var response = req.EndGetResponse( result );
// Get the result string and call the callback
string resultString = null; // TODO: read from the stream
callback(resultString);
}, null );
}
This can be resolved by moving
lbTeams.ItemsSource = App.gViewModel.Games;
to the end of the OpenReadCompletedGames method. You'll need to use the Dispatcher to update the UI from here.
Dispatcher.BeginInvoke( () => { lbTeams.ItemsSource = App.gViewModel.Games; } );