Windows Phone select and upload image to a website over HTTP POST

You have often seen photo sharing websites where you can attach images and after clicking the submit button the local images will be uploaded to the server. It is implemented by using HTML forms which send HTTP requests automatically. But if your application is not a web application written in HTML, you should write HTTP requests by yourself. You should do that in Windows Phone too.

So the full sequence consists of 2 actions: 1 – select an image, 2 – upload it to the server.
At first, you should select an image from the gallery.
ChoosePhotoPage
The code is simple, just use the built-in PhotoChooserTask class:

public void AttachFile()
{
    var chooseTask = new PhotoChooserTask();
    chooseTask.ShowCamera = true;
    chooseTask.Completed += (s, e) => this.OnChoosePhotoCompleted(e);
    chooseTask.Show();
}

private void OnChoosePhotoCompleted(PhotoResult result)
{
    if (result.TaskResult == TaskResult.OK)
    {
        this.HasPhoto = true;
        this.PhotoPath = new Uri(result.OriginalFileName);
        this.PhotoName = Path.GetFileName(result.OriginalFileName);

        using (var fileBytes = new MemoryStream())
        {
            result.ChosenPhoto.CopyTo(fileBytes);
            this._photoBytes = fileBytes.GetBuffer();
        }
    }
}

Then you should use the HttpWebRequest class to post the selected image to the server. This class alone is not enough, so you should write lots of extra code to make it work. I have implemented a special class for this purpose, the code is based on this answer on stackoverflow.com, you can just copy it to your project.

public class HttpPostTask
{
    private readonly string _boundary = "----------" + DateTime.Now.Ticks.ToString();

    private bool _isCancelled;
    private bool _isExecuted;

    public HttpPostTask(string url, Dictionary<string, object> parameters, Action<string> onCompleted)
    {
        this.Url = url;
        this.Parameters = parameters;
        this.OnCompleted = onCompleted;
    }

    public string Url { get; set; }

    public Dictionary<string, object> Parameters { get; set; }

    public Action<string> OnCompleted { get; private set; }

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

    public void Execute()
    {
        if (this._isExecuted)
        {
            throw new NotSupportedException();
        }

        this._isExecuted = true;

        HttpWebRequest httpWebRequest = WebRequest.CreateHttp(this.Url);
        httpWebRequest.Method = "POST";
        httpWebRequest.ContentType = string.Format("multipart/form-data; boundary={0}", this._boundary);

        httpWebRequest.BeginGetRequestStream(new AsyncCallback(this.GetRequestStreamCallback), httpWebRequest);
    }
    
    public void Cancel()
    {
        this._isCancelled = true;
    }

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

    protected void InvokeInUiThread(Action action)
    {
        if (!this._isCancelled)
        {
            Deployment.Current.Dispatcher.BeginInvoke(action);
        }
    }


    private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
    {
        HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
        Stream postStream = request.EndGetRequestStream(asynchronousResult);

        this.WriteMultipartObject(postStream, this.Parameters);
        postStream.Close();

        request.BeginGetResponse(new AsyncCallback(this.GetResponseCallback), request);
    }

    private void GetResponseCallback(IAsyncResult asynchronousResult)
    {
        HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;

        // get the response
        HttpWebResponse response;
        try
        {
            response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
        }
        catch (Exception e)
        {
            if (e.InnerException != null && e.InnerException.Message.StartsWith("[net_WebHeaderInvalidControlChars]"))
            {
                // not an exception, everything is ok
                this.InvokeInUiThread(() => this.OnCompleted(null));
            }
            else
            {
                this.InvokeOnErrorHandler("Unable to post data.");
            }

            return;
        }

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

        // response stream
        using (Stream stream = response.GetResponseStream())
        {
            using (StreamReader reader = new StreamReader(stream))
            {
                string str = reader.ReadToEnd();
                this.InvokeInUiThread(() => this.OnCompleted(str));
            }
        }
    }

    private void WriteMultipartObject(Stream stream, Dictionary<string, object> data)
    {
        StreamWriter writer = new StreamWriter(stream);
        if (data != null)
        {
            foreach (var entry in data)
            {
                this.WriteEntry(writer, entry.Key, entry.Value);
            }
        }

        writer.Write("--");
        writer.Write(this._boundary);
        writer.WriteLine("--");
        writer.Flush();
    }

    private void WriteEntry(StreamWriter writer, string key, object value)
    {
        if (value != null)
        {
            writer.Write("--");
            writer.WriteLine(this._boundary);
            if (value is byte[])
            {
                byte[] ba = value as byte[];

                writer.WriteLine(@"Content-Disposition: form-data; name=""{0}""; filename=""{1}""", key, "image.png");
                writer.WriteLine(@"Content-Type: application/octet-stream");
                writer.WriteLine(@"Content-Length: " + ba.Length);
                writer.WriteLine();
                writer.Flush();
                Stream output = writer.BaseStream;

                output.Write(ba, 0, ba.Length);
                output.Flush();
                writer.WriteLine();
            }
            else
            {
                writer.WriteLine(@"Content-Disposition: form-data; name=""{0}""", key);
                writer.WriteLine();
                writer.WriteLine(value.ToString());
            }
        }
    }
}

The usage of this class is simple. I use the http://posttestserver.com website to which I post images, but you can use a more appropriate site like Google Picasa or Imgur.

string url = "http://posttestserver.com/post.php?dir=wp7posttest";
var parameters = new Dictionary<string, object>();
parameters.Add("name", this.PhotoName);
parameters.Add("photo", this._photoBytes);

this._currentPostTask = new HttpPostTask(url, parameters, this.OnPostCompleted);
this._currentPostTask.OnError = this.OnPostError;

this._currentPostTask.Execute();

UploadedPhotoPage

The website http://posttestserver.com returns a string with a link to the posted data, other website will return different responses.

The working sample application which I used in this post you can download here: HttpPostSample.zip

Windows Phone Marketplace app submitting

In this article I will show how the windows phone marketplace looks and how to publish apps there.

When you click the Submit App link, you will see the following page:
mainpage

Read more of this post

Windows Phone display images from URL

The built-in Image element is quite good for loading and displaying images from the web, because it loads them asynchronously in the background thread and doesn’t freeze UI.

In this example I will show how to display a loading indicator while the image is loading and an error message if the image loading has failed.

wp7_imageloading_loadingviewwp7_imageloading_imageview

You shouldn’t use the Source property in the XAML markup, instead you should set the Source property in the code-behind.

The XAML markup looks so:

<Grid x:Name="LayoutRoot" Background="Transparent">
    <TextBlock x:Name="loadingView" Visibility="Collapsed" Text="Loading..." VerticalAlignment="Center" HorizontalAlignment="Center" />
    <TextBlock x:Name="errorView" Visibility="Collapsed" VerticalAlignment="Center" HorizontalAlignment="Center" />
    <Image x:Name="imageView" Visibility="Collapsed" />
</Grid>

The code-behind:

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();

        var bitmap = new BitmapImage(new Uri("http://upload.wikimedia.org/wikipedia/commons/6/63/Wikipedia-logo.png"));
        bitmap.ImageFailed += (s, e) => this.ShowError("Error while loading the image.");
        bitmap.ImageOpened += (s, e) => this.ShowImage();

        this.ShowLoading();
        this.imageView.Source = bitmap;
    }

    private void ShowLoading()
    {
        this.loadingView.Visibility = Visibility.Visible;
        this.imageView.Visibility = Visibility.Collapsed;
        this.errorView.Visibility = Visibility.Collapsed;
    }

    private void ShowError(string message)
    {
        this.loadingView.Visibility = Visibility.Collapsed;
        this.imageView.Visibility = Visibility.Collapsed;
        this.errorView.Visibility = Visibility.Visible;

        this.errorView.Text = message;
    }

    private void ShowImage()
    {
        this.loadingView.Visibility = Visibility.Collapsed;
        this.imageView.Visibility = Visibility.Visible;
        this.errorView.Visibility = Visibility.Collapsed;
    }
}

At first I create the BitmapImage instance, after the creation of this object I subscribe to its events ImageFailed and ImageOpened. Then I display the loading indicator (the ShowLoading method). And as soon as the line ‘this.imageView.Source = bitmap’ is called, the image loading process starts. After some time either ShowError or ShowImage will be called.

The sample application you can download here: https://dl.dropbox.com/u/8047386/WordPress/PhoneImageLoadingSample.zip

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

Silverlight and WP7 chart with data point labels

Recently I found the question on stackoverflow in which one user asked how to add labels to data points on WPF/Silverlight/Windows Phone Toolkit charts. Whereas almost every javascript chart can do this, this functionality is absent in Silverlight charts and none of developers cares.

So I wrote a simple class which extended the LinearSeries class and now line charts can be displayed like this:

silverlight_chart_datapointlabels

wp7_chart_datapointlabels

I added labels only for line charts, but if someone needs them for column charts, I can try to implement it as well.
In the current implementation I created the class which inherits the LineSeries class. It has the following properties:
DisplayLabels – You should set it to true explicitly so that labels are displayed. It is false by default.
LabelBindingPath – Optional. You can specify a custom property of your model which will be displayed instead of the value from the Y-axis.
LabelStyle – Optional. You can change foreground, font weight, font size and other properties of labels.

The example of usage:

<chart:Chart Width="600" Height="300">
    <local:ExtendedLineSeries
        ItemsSource="{Binding Items}"
        DependentValuePath="ItemValue"
        IndependentValuePath="Title" 
        DisplayLabels="True"
        LabelBindingPath="ItemValue" />
</chart:Chart>

The complete source code of the class:

public class ExtendedLineSeries : LineSeries
{
    private Canvas _labelsCanvas;
    private Dictionary<DataPoint, TextBlock> _currentLabels = new Dictionary<DataPoint, TextBlock>();

    /// <summary>
    /// Gets or sets a value indicating whether labels should be displayed. 
    /// </summary>
    public bool DisplayLabels { get; set; }

    /// <summary>
    /// Gets or sets the binding path of the label.
    /// </summary>
    public string LabelBindingPath { get; set; }

    /// <summary>
    /// Gets or sets the style of each label.
    /// </summary>
    public Style LabelStyle { get; set; }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        // get a canvas to which the labels will be added
        this._labelsCanvas = (Canvas)this.GetTemplateChild("PlotArea");
        // clear the clip property so that labels are visible even if they exceed the bounds of the chart
        this.Clip = null;
    }

    protected override void UpdateDataPoint(DataPoint dataPoint)
    {
        base.UpdateDataPoint(dataPoint);

        // after the data point is created and added to the chart, we can add a label near it
        if (this.DisplayLabels && dataPoint.Visibility == System.Windows.Visibility.Visible)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() => this.CreateLabel(dataPoint));
        }
    }

    private void CreateLabel(DataPoint dataPoint)
    {
        // this method is also called with the SizeChanged event, so I create the label only one time
        TextBlock label;
        if (this._currentLabels.ContainsKey(dataPoint))
        {
            label = this._currentLabels[dataPoint];
        }
        else
        {
            label = new TextBlock();
            this._labelsCanvas.Children.Add(label);
            this._currentLabels.Add(dataPoint, label);

            label.Style = this.LabelStyle;

            // bind the label text to the specified path, or to dataPoint.DependantValue by default
            Binding binding = this.LabelBindingPath != null
                        ? new Binding(this.LabelBindingPath) { Source = dataPoint.DataContext }
                        : new Binding("DependentValue") { Source = dataPoint };
            BindingOperations.SetBinding(label, TextBlock.TextProperty, binding);
        }

        // calculate a position of the label
        double coordinateY = Canvas.GetTop(dataPoint) - label.ActualHeight; // position the label above the data point
        double coordinateX = Canvas.GetLeft(dataPoint) + dataPoint.ActualHeight / 2 - label.ActualWidth / 2; // center horizontally
        Canvas.SetTop(label, coordinateY);
        Canvas.SetLeft(label, coordinateX);
    }
}

In this code I made an override of the UpdateDataPoint method and each time a DataPoint is updated I update its label.
The code work with Silverlight as well as with Windows Phone 7. For WP7 I used the recompiled library which I published in one of my previous posts.

Source code of the sample application: ChartPointLabelsSample.zip
Show case: ChartPointLabelsSampleTestPage.html

Highlight found text in ListBox for Silverlight and WP7

You probably saw an implementation of search where all matches of the entered query were highlighted in the text or list, or table. Here are 2 screenshots which illustrate the described functionality:

It is very easy to implement in Javascript, just 4 lines:

var html = element.html();
html = html.replace(new RegExp("<span class=\"highlighted\">(.*?)<\/span>", "i"), "$1"); // clear the previous highlight
html = html.replace(new RegExp("(" + query + ")", "i"), "<span class=\"highlighted\">$1</span>"); // highlight the current search query
element.html(html);

But it is slightly more difficult in Silverlight. However the approach is the same:
1. Get the text value.
2. Replace some places in the text by a span with some user-defined style.
3. Parse the result text and display it.

I have created a new project by using the Windows Phone Databound Application template in Visual Studio, so that I don’t need to create base mark-up and add default data, Visual Studio creates everything by itself.

At first, we need to write methods which replace values of ListBox by formatted values. The corresponding javascript code is `html.replace(new Regexp(…))`. The C# code looks so:

public void Search()
{
    this.ClearHighlights();

    if(string.IsNullOrEmpty(SearchQuery))
    {
        return;
    }

    this.AddHighlights();
}

private void ClearHighlights()
{
    var highlightRegex = new Regex("<Run Foreground='Yellow'>(.*?)</Run>");
    foreach (var item in this.Items)
    {
        item.LineOne = highlightRegex.Replace(item.LineOne, "$1");
        item.LineTwo = highlightRegex.Replace(item.LineTwo, "$1");
    }
}

private void AddHighlights()
{
    var searchRegex = new Regex(string.Format("({0})", this.SearchQuery), RegexOptions.IgnoreCase);
    foreach (var item in this.Items)
    {
        item.LineOne = searchRegex.Replace(item.LineOne, "<Run Foreground='Yellow'>$1</Run>");
        item.LineTwo = searchRegex.Replace(item.LineTwo, "<Run Foreground='Yellow'>$1</Run>");
    }
}

The `LineOne` and `LineTwo` properties contain some strings which are displayed in each item of the ListBox.

Finally, we need to write a binding which correctly displays a text with the `Run` tags. The corresponding javascript code is `elem.html(html)`. I used attached property for this task:

public static class TextBlockProperties
{
    public static string GetStyledText(DependencyObject obj)
    {
        return (string)obj.GetValue(StyledTextProperty);
    }

    public static void SetStyledText(DependencyObject obj, string value)
    {
        obj.SetValue(StyledTextProperty, value);
    }

    public static readonly DependencyProperty StyledTextProperty =
        DependencyProperty.RegisterAttached("StyledText", typeof(string), typeof(TextBlock), new PropertyMetadata(null, StyledText_Changed));


    private static void StyledText_Changed(DependencyObject d, DependencyPropertyChangedEventArgs args)
    {
        var tb = (TextBlock) d;
        var text = (string) args.NewValue;

        if(string.IsNullOrEmpty(text) || !Regex.IsMatch(text, "<Run.*?>.*?</Run>"))
        {
            tb.Text = text;
            return;
        }

        var formattedTextBlockXaml = "<TextBlock xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" + text + "</TextBlock>";
        var formattedTextBlock = (TextBlock) XamlReader.Load(formattedTextBlockXaml);

        // detach parsed inlines from the view tree
        var inlines = formattedTextBlock.Inlines.ToList();
        formattedTextBlock.Inlines.Clear();

        // add inlines to the specified text block
        tb.Inlines.Clear();
        foreach (var inline in inlines)
        {
            tb.Inlines.Add(inline);
        }
    }
}

So now all that you need is to use the StyledText property instead of Text:

<TextBlock ext:TextBlockProperties.StyledText="{Binding LineOne}" />

In my example I used the yellow color for highlighting, but you can change any property of the `Run` class, like FontWeight (Bold), FontStyle (Italic) and others, the list of properties you can find here: Run Class.

The sample application which I used in order to make the 2 screenshots above you can download here: Wp7ListSearchSample.zip

Windows Phone 7 chart libraries overview. AmCharts

After the previous post I decided to explore the internet and find some other chart libraries for WP7. Unfortunately, there are only two free chart libraries: the previously mentioned Silverlight Toolkit and AmCharts. You can download the last charts here: http://wpf.amcharts.com/download.

This is a very small library and the last time it was updated was more than 1 year ago. It contains only 4 charts: Line, Column, Area, Pie. Here are screenshots of them:

The code for a chart item is quite simple:

<amq:SerialChart Style="{StaticResource chartStyle}" 
		DataSource="{Binding Charts[0].Items}" 
		CategoryValueMemberPath="Month">
	<amq:SerialChart.Graphs>
		<amq:LineGraph ValueMemberPath="Value" Title="Test Chart" />
	</amq:SerialChart.Graphs>
</amq:SerialChart>

For some unknown reason, the pie chart should be created by the different XAML code:

<amq:PieChart DataSource="{Binding Charts[3].Items}"
		TitleMemberPath="Month" 
		ValueMemberPath="Value" />

Advantages of these charts:
1. Free and open source (source code: https://github.com/ailon/amCharts-Quick-Charts);
2. Display the tooltip if you tap a data series;
3. Look better on the phone than the Toolkit charts with the default template.
4. Have small size and must work faster than Toolkit.

Disadvantages:
1. Only 4 kinds of charts;
2. Almost no customization, difficult to apply your own design;
3. Annoying behavior: if you tap the legend or the y-axis they will disappear and you will have to tap once again to display them;

As for me, I would use the Toolkit library and would customize their theme so that it looks more like metro style controls. But this library is good as well if you want just to display a chart without any customization.

The source code of the sample application with AmCharts you can download here: Wp7ChartsSamples.Amcharts.zip

Windows Phone 7 charts. Silverlight Toolkit.

Earlier I thought that WP7 Toolkit had chart controls. But it was surprising for me that charts weren’t ported to the WP7 library and if you want to use them, you have to use the Silverlight 3 toolkit with the S3 controls library as it is described in this post. The thing that I don’t like is that I have to add an extra library and also there is the annoying warning message that the library isn’t compatible with WP7. So I created the WP7 library, downloaded the source code of the toolkit and rewrote some code so that it didn’t use the extra reference.

Now you can use this library, which is more suitable for WP7: System.Windows.Controls.DataVisualization.Toolkit.dll.
For some reason the size of this library is 400kb, slightly larger than the silverlight library, I think it is because I haven’t removed *.xaml files.

As it was already said, the xaml code is the same as in WPF and Silverlight versions:

<charting:Chart>
	<charting:PieSeries Title="Test items" ItemsSource="{Binding Items}" DependentValuePath="ItemValue" IndependentValuePath="Title" />
</charting:Chart>

I’m going to examine some other chart libraries for Windows Phone 7 and I will write about their advantages and disadvantages in future posts.

The source code of the tooolkit charts and the sample application you can download here: WP7ToolkitCharts.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.