My Windows Phone open source application

Though I know Windows Phone development in theory, I haven’t ever developed applications for this platform. That’s why I decided to create one application for one web site which provides json api. The site is an imageboard like 4chan, i.e all topics and messages are anonymous and you can attach images to your messages.

The source code is situated on my github account. I’ve already created an Android application for this web site and published it on Google Play, so it wasn’t so difficult to reimplement the application for Windows Phone. It is definitely much easier than to develop it from scratch.

The Windows Phone project is situated here.

So far the application looks like this:

wp1wp2wp3

At the moment the application has the following functionality:
1. Downloads and parses JSON files.
2. Displays the loading indicator during loading, displays the error message if an error has occured.
3. Loads and displays list images (thumbnails) asyncronously.
4. Opens full images in the WebBrowser.
5. Converts HTML posts to RichTextBox controls (the parser isn’t ideal, but it works at least for this website).
6. Makes HTTP POST requests.

By the way, the application is already published in the marketplace and available here:
http://www.windowsphone.com/en-us/store/app/2ch-browser/3f63fa2c-4b35-4b8a-b3c0-51b54b460d70

The certification process was without problems, but I had to wait 5 days.

Displaying progress bar while loading JSON from Web Services for Windows Phone 7

In one of my previous posts I explained how to download and parse JSON response from web services. I used a simple text to show that the content is loading, but it isn’t convenient because you don’t know how long it remains to wait. In this post I will add the ProgressBar control to the project which will constantly display the number of downloaded bytes in per cents.
Here is how it looks:
wp7_json_loading

At first, I added some code to the XAML view so that it displays the ProgressBar and its description:

<StackPanel Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}"
            VerticalAlignment="Center">
    <TextBlock Text="{Binding ProgressDescription}" HorizontalAlignment="Center" Style="{StaticResource PhoneTextNormalStyle}" />
    <ProgressBar Value="{Binding ProgressValue}" Maximum="1" IsIndeterminate="False" Height="50" />
</StackPanel>

After that I’ve tried to find some event of the HttpWebRequest class which can report progress, but I haven’t found it, so I implemented my own ProgressStream class which looks like a common stream but has the OnProgressChanged callback.

public class ProgressStream : Stream
{
    private readonly Stream _stream;
    private long _length;

    public ProgressStream(Stream stream, long length)
    {
        this._stream = stream;
        this._length = length;
    }

    public override bool CanRead
    {
        get { return this._stream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return this._stream.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return this._stream.CanWrite; }
    }

    public override void Flush()
    {
        this._stream.Flush();
    }

    public override long Length
    {
        get { return this._length; }
    }

    public override long Position
    {
        get
        {
            return this._stream.Position;
        }
        set
        {
            this._stream.Position = value;
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int readCount = this._stream.Read(buffer, offset, count);
        this.ReportProgress();

        return readCount;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        long seekCount = this._stream.Seek(offset, origin);
        this.ReportProgress();

        return seekCount;
    }

    public override void SetLength(long value)
    {
        this._length = value;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        this._stream.Write(buffer, offset, count);
        this.ReportProgress();
    }

    public Action<double> OnProgressChanged { get; set; }

    private void ReportProgress()
    {
        if (this.OnProgressChanged != null)
        {
            var percent = this.Position / (double)this.Length;
            // System.Threading.Thread.Sleep(500); // for test purposes
            this.OnProgressChanged(percent);
        }
    }
}

After that I changed the HttpGetTask class so that it uses the new ProgressStream class if it is possible. It is necessary to check the presence of the Content-Length header, because without this header and with unknown length it is impossible to calculate the progress.

public class HttpGetTask<T>
{
    //...
    
    private void OnGetResponseCompleted(IAsyncResult ar)
    {
        //...
        
        Stream stream;
        if (this.OnProgressChanged != null && response.Headers.AllKeys.Any(key => key == "Content-Length"))
        {
            var progressStream = new ProgressStream(response.GetResponseStream(), response.ContentLength);
            progressStream.OnProgressChanged = v => this.InvokeInUiThread(() => this.OnProgressChanged(v));
            stream = progressStream;
        }
        else
        {
            stream = response.GetResponseStream();
        }

        //...
    }

    //...
    
    public Action<double> OnProgressChanged { get; set; }
}

Then you should update the view model. I added the OnProgressChanged method which updates the ProgressDescription and ProgressValue properties.
Also I changed the url of the sample data, because the website geonames.org which I used earlier doesn’t send the Content-Length header (it sends the Transfer-Encoding header with value ‘chunked’ and unknown length), so I downloaded a JSON response and uploaded it on my dropbox account here.
The ViewModel looks so:

public class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        Cities = new ObservableCollection<CityViewModel>();

        string url = "https://dl.dropbox.com/u/8047386/WordPress/testcities.json";
        
        var task = new HttpGetTask<CitiesList>(url, this.OnPostExecute);
        task.OnPreExecute = this.OnPreExecute;
        task.OnError = this.OnError;
        task.OnProgressChanged = this.OnProgressChanged;

        task.Execute();
    }
    
    //...

    private void OnProgressChanged(double value)
    {
        this.ProgressValue = value;
        this.ProgressDescription = string.Format("Loading {0:F0}%", value * 100);
    }

    //...
        
    private double _progressValue;

    public double ProgressValue
    {
        get { return _progressValue; }
        set
        {
            _progressValue = value;
            RaisePropertyChanged("ProgressValue");
        }
    }

    private string _progressDescription;

    public string ProgressDescription
    {
        get { return _progressDescription; }
        set
        {
            _progressDescription = value;
            RaisePropertyChanged("ProgressDescription");
        }
    }
}

Though the loading indicator is difficult to reproduce on the Windows Phone Emulator because it downloads the file very fast, it will be certainly displayed if the phone has a slow internet connection.
I didn’t add all code to the post, but as usual you can download the full project here:
PhoneJsonProgressBar.zip

Working with JSON web services in Silverlight and Windows Phone 7

In this post I’ll explain how to retrieve and display data from JSON web services.

As an example I’ll use the REST service from geonames.org.

JSON response of the example looks so:

At first, you should create C# classes (models) which can be mapped to JSON entities.
The first curly braсe indicates that it is an object, and it has 1 property `geonames`. After this property you can see the square bracket indicating an array. This array consists of many objects, and each object has 12 properties.
So we should create a model for the root object, which looks so:

// { geonames: [{}, {}, {}] }
[DataContract]
public class CitiesList
{
	[DataMember(Name = "geonames")]
	public List<City> Cities { get; set; }
}

It is important to add DataContract and DataMember attributes to the class and all its properties. Also you should specify the name of the corresponding JSON property (geonames) inside the DataMember attribute.

Then we should create the model for inner objects (cities). As you already saw, each JSON object has 12 properties, but we don’t need all of them. So we can specify only those proeprties which we need, and .Net deserializer will ignore all other properties.

// { name: "", countrycode: "", population: -1 }
[DataContract]
public class City
{
	[DataMember(Name = "name")]
	public string Name { get; set; }

	[DataMember(Name = "countrycode")]
	public string CountryCode { get; set; }

	[DataMember(Name = "population")]
	public int Population { get; set; }
}

So now all that you need is to create an instance of the HttpWebRequest class, get the response and deserialize it by using the DataContractJsonSerializer class. The deserializer requires the System.Servicemodel.Web and System.Runtime.Serialization references.

You must use the two following methods to perform loading and parsing of JSON.

private void BeginDownloadCitiesRequest()
{
	// create the http request
	HttpWebRequest httpWebRequest = WebRequest.CreateHttp("http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=en&username=vortexwolf");
	httpWebRequest.Method = "GET";
	httpWebRequest.Accept = "application/json";

	// get the response asynchronously
	httpWebRequest.BeginGetResponse(OnGetResponseCompleted, httpWebRequest);
}

private void OnGetResponseCompleted(IAsyncResult ar)
{
	var httpWebRequest = (HttpWebRequest)ar.AsyncState;

	// get the response
	var response = httpWebRequest.EndGetResponse(ar);

	// deserialize json
	var jsonSerializer = new DataContractJsonSerializer(typeof(CitiesList));
	var responseObject = (CitiesList)jsonSerializer.ReadObject(response.GetResponseStream());

	// display on the view
	Deployment.Current.Dispatcher.BeginInvoke(() => OnCitiesDownloaded(responseObject));
}

The code of creating a http request and getting a response will often repeat in different places of the application, so it is preferrable to move this code to a separate class.
I did it so:

public class HttpGetTask<T>
{
    public HttpGetTask(string url, Action<T> onPostExecute)
    {
        this.Url = url;
        this.OnPostExecute = onPostExecute;
    }

    public void Execute()
    {
        if (this.OnPreExecute != null)
        {
            this.OnPreExecute();
        }

        // create the http request
        HttpWebRequest httpWebRequest = WebRequest.CreateHttp(this.Url);
        httpWebRequest.Method = "GET";
        httpWebRequest.Accept = "application/json";

        // get the response asynchronously
        httpWebRequest.BeginGetResponse(OnGetResponseCompleted, httpWebRequest);
    }

    private void OnGetResponseCompleted(IAsyncResult ar)
    {
        var httpWebRequest = (HttpWebRequest)ar.AsyncState;

        // get the response
        HttpWebResponse response;
        try
        {
            response = (HttpWebResponse)httpWebRequest.EndGetResponse(ar);
        }
        catch (WebException)
        {
            this.InvokeOnErrorHandler("Unable to connect to the web page.");
            return;
        }
        catch (Exception e)
        {
            this.InvokeOnErrorHandler(e.Message);
            return;
        }

        if (response.StatusCode != HttpStatusCode.OK)
        {
            this.InvokeOnErrorHandler((int)response.StatusCode + " " + response.StatusDescription);
            return;
        }

        // response stream
        var stream = response.GetResponseStream();

        // deserialize json
        var jsonSerializer = new DataContractJsonSerializer(typeof(T));
        var responseObject = (T)jsonSerializer.ReadObject(stream);

        // call the virtual method
        this.InvokeInUiThread(() => this.OnPostExecute(responseObject));
    }

    private void InvokeOnErrorHandler(string message)
    {
        if (this.OnError != null)
        {
            this.InvokeInUiThread(() => this.OnError(message));
        }
    }

    private void InvokeInUiThread(Action action)
    {
        Deployment.Current.Dispatcher.BeginInvoke(action);
    }

    public string Url { get; private set; }

    public Action<T> OnPostExecute { get; private set; }

    public Action OnPreExecute { get; set; }

    public Action<string> OnError { get; set; }
}

You can execute this task by specifying Url and the OnPostExecute callback. Also you can set other properties. Example:

public class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        Cities = new ObservableCollection<CityViewModel>();

        string url = "http://api.geonames.orgzz/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=en&username=vortexwolf";
        
        var task = new HttpGetTask<CitiesList>(url, this.OnPostExecute);
        task.OnPreExecute = this.OnPreExecute;
        task.OnError = this.OnError;

        task.Execute();
    }

    private void OnPreExecute()
    {
        this.IsLoading = true;
    }

    private void OnPostExecute(CitiesList responseObject)
    {
        this.OnCitiesDownloaded(responseObject);
        this.IsLoading = false;
    }

    private void OnError(string message)
    {
        MessageBox.Show(message);
        this.IsLoading = false;
    }

    public ObservableCollection<CityViewModel> Cities { get; set; }

    private bool _isLoading;

    public bool IsLoading
    {
        get { return _isLoading; }
        set
        {
            _isLoading = value;
            RaisePropertyChanged("IsLoading");
        }
    }

    private void OnCitiesDownloaded(CitiesList citiesList)
    {
        var cityModels = citiesList.Cities
            .Select(c =>
                    new CityViewModel
                    {
                        Name = c.Name,
                        CountryCode = c.CountryCode,
                        Population = c.Population
                    })
            .ToList();

        cityModels.ForEach(this.Cities.Add);
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

I created the sample application for Windows Phone 7 which retrieves JSON data and displays it on the phone.

The source code of the example you can download here:
https://dl.dropbox.com/u/8047386/WordPress/PhoneJsonTest.zip

Follow

Get every new post delivered to your Inbox.