29 January 2012

Templating a XAML CheckBox to a thumbs-up/down control using Expression Blend

Preface
The checkbox has been been around in the Graphical User Interface for as long as I can remember doing GUI – since the early 90’s I guess. You know what, let’s make that “it’s been around for longer than I care to remember” ;). For my newest Windows Phone project I wanted something different. In stead of boring old 
checkbox 
 
I wanted something like this:
thumbsupdown
Turns out you can do this in pure XAML. And almost entirely in Expression Blend, too. I could just post the XAML and be done with it, but I like to document the track I took, not only to educate you, but also to remember myself how the hell I got here in the first place ;-).
Setting the stage
  • Open Visual Studio 2010
  • Create a new Windows Phone 7 (7.1 of course!) project,
  • Make a folder “icons”build actions
  • Download this image to your computer
  • Paste it in the “icons” folder in Visual Studio
  • Double check the image’s properties, they should be as showed to the right.
  • Save the project
Creating style and control template
  • Open the project in Expression blend
  • Put one (or more, for all I care) CheckBoxes on the phone page.
  • Select one of them, then click in the main Blend menu “Object/Edit Style/Create Empty”.
  • In the dialog that follows, enter “Thumbupdowncheckboxstyle” for a name and select “Application” under “Define in”
  • You will get a screen with a single CheckBox. Right click it, select “Edit template/Create Empty”.
  • In the dialog that follows, enter “Thumbupdowncheckboxtemplate” for a name and select “Application” under “Define in”
First thing you will notice is that the selected CheckBox completely disappears from your design surface. That’s because basically you have replaced the entire look for the CheckBox by an empty template, which is essentially well, pretty empty indeed. It only contains a grid, and even that’s gonna go.
  • Delete the Grid
  • Add a Rectangle to the template by double clicking on the Rectangle button from the Assets toolbar on the left
This is a good moment to turn on Split View in the designer, because that shows you what the designer is actually doing. Right-click the rectangle in the Objects and Timeline panel, and select “View XAML”. At this point you will see only this:
<Application.Resources>

  <ControlTemplate x:Key="Thumbupdowncheckboxtemplate" TargetType="CheckBox">
    <Rectangle Fill="#FFF4F4F5" Stroke="Black"/>
  </ControlTemplate>

  <Style x:Key="Thumbupdowncheckboxstyle" TargetType="CheckBox">
    <Setter Property="Template" Value="{StaticResource Thumbupdowncheckboxtemplate}"/>
  </Style>

</Application.Resources>
And the design surface will only show a horizontal white rectangle with a black border.
Default control size
ResourcesFirst and foremost, set the default size of your control. To get this done, look right top, select the Resources tab and expand App.Xaml. Then proceed as follows:
  • Right-click Thumbupdowncheckboxstyle
  • Select “Edit”
  • Select the “Properties” tab left of the “Resources” tab
  • Locate the “Width” and “Height” fields and enter 40 for both.
Your XAML now should look like this:
<Application.Resources>

  <ControlTemplate x:Key="Thumbupdowncheckboxtemplate" TargetType="CheckBox">
    <Rectangle Fill="#FFF4F4F5" Stroke="Black"/>
  </ControlTemplate>

  <Style x:Key="Thumbupdowncheckboxstyle" TargetType="CheckBox">
    <Setter Property="Template" Value="{StaticResource Thumbupdowncheckboxtemplate}"/>
    <Setter Property="Width" Value="40"/>
    <Setter Property="Height" Value="40"/>
  </Style>

</Application.Resources>
Propagate default size to template
Now go back to editing the Control template again:
  • Once again go to the “Resources” tab left top
  • Right-click Thumbupdowncheckboxtemplate
  • Select “Edit”.
  • Select the Rectangle and Click the tab “Properties” top left againwidth
  • Click the little square all the way to the right behind the field “Width”
  • This will popup a menu. Select “Template Binding/Width”
  • Click the little square behind “Height” and Select “Template Binding/Height”
This will set the width and height of the rectangle to the full width and height of the control. If the user does not set a specific width and height, the values in the default Setters (both 40) will be used. XAML at this point:
<Application.Resources>

  <ControlTemplate x:Key="Thumbupdowncheckboxtemplate" TargetType="CheckBox">
    <Rectangle Fill="#FFF4F4F5" Stroke="Black" Width="{TemplateBinding Width}" 
    Height="{TemplateBinding Height}"/>
  </ControlTemplate>

  <Style x:Key="Thumbupdowncheckboxstyle" TargetType="CheckBox">
    <Setter Property="Template" Value="{StaticResource Thumbupdowncheckboxtemplate}"/>
    <Setter Property="Width" Value="40"/>
    <Setter Property="Height" Value="40"/>
  </Style>

</Application.Resources>
Initial colors
Next steps:
  • ColorClick the white white rectangle behind “Fill”
  • Don’t bother to select a color: simply type in “Green” in the text box and Blend will make a pretty hex string of it ;-)
  • After that, click the little white square behind “Stroke”
  • From the popup-menu, select “Reset”
The design surface now shows a green rectangle. Oh wow ;-) but bear with me, we will get there in the end. Your XAML should now look like this:
<Application.Resources>

  <ControlTemplate x:Key="Thumbupdowncheckboxtemplate" TargetType="CheckBox">
    <Rectangle Fill="Green" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/>
  </ControlTemplate>

  <Style x:Key="Thumbupdowncheckboxstyle" TargetType="CheckBox">
    <Setter Property="Template" Value="{StaticResource Thumbupdowncheckboxtemplate}"/>
    <Setter Property="Width" Value="40"/>
    <Setter Property="Height" Value="40"/>
  </Style>

</Application.Resources>
Including the image as an ‘opacity mask’
OpacityBlend’s greatest asset – it’s enormous capabilities – unfortunately is also sometimes its Achilles’ heel: there’s a bewildering set of options that’s not always easy to find your way in. Fortunately, at the top of the Properties there’s a Search box that helps you find that hard-to-find-options. Which is extremely helpful – if you happen to know what to look for ;-). In this case:
  • Enter “Opacity” in the Search box
  • Click “No Brush”
  • Select “Tile Brush” – that’s the 2nd icon from the left
  • That produces a drop-down where you can select “icons/thumbsup.png”
your XAML should now look like this:
<Application.Resources>

  <ControlTemplate x:Key="Thumbupdowncheckboxtemplate" TargetType="CheckBox">
  <Rectangle Fill="Green" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
    <Rectangle.OpacityMask>
    <ImageBrush Stretch="Fill" ImageSource="icons/thumbsup.png"/>
    </Rectangle.OpacityMask>
  </Rectangle>
  </ControlTemplate>

  <Style x:Key="Thumbupdowncheckboxstyle" TargetType="CheckBox">
  <Setter Property="Template" Value="{StaticResource Thumbupdowncheckboxtemplate}"/>
  <Setter Property="Width" Value="40"/>
  <Setter Property="Height" Value="40"/>
  </Style>

</Application.Resources>
desing2designYour design surface should now looks like to the image to the left, which is kinda crummy. You can use the little triangle on the right bottom, and the two little lines on the right and the bottom, to resize the design surface. This will have no effect on the control template itself, it will make just make it look better (see right image) .
You can also manually add the attributes d:DesignWidth="75" and d:DesignHeight="75" to the Rectangle.
Defining Visual States
statesA CheckBox has certain states, the most obvious being Checked and Unchecked. Actually there are quite a lot more, and you can see them by clicking on the “States”  tab. Now all these states are defined, but there are no visuals connected to it anymore, since you have replaced the control template by something empty and started filling in yourself. This next step will bring a bit of those visual states back. To prepare for that:
  • Make sure the Rectangle is selected in the “Objects and Timeline”  panel left
  • In the Properties panel to the right of the screen, scroll down to “Transform”
  • Expand the Transform panel if it’s collapsed
  • Selected the leftmost tab
  • Enter 0.5 in both X and Y boxes
transformXAML at this point:
<Application.Resources>

  <ControlTemplate x:Key="Thumbupdowncheckboxtemplate" TargetType="CheckBox">
    <Rectangle Fill="Green" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" 
       d:DesignWidth="75" d:DesignHeight="75" RenderTransformOrigin="0.5,0.5">
      <Rectangle.RenderTransform>
        <CompositeTransform TranslateX="0.5" TranslateY="0.5"/>
      </Rectangle.RenderTransform>
      <Rectangle.OpacityMask>
        <ImageBrush Stretch="Fill" ImageSource="icons/thumbsup.png"/>
      </Rectangle.OpacityMask>
    </Rectangle>
  </ControlTemplate>

  <Style x:Key="Thumbupdowncheckboxstyle" TargetType="CheckBox">
    <Setter Property="Template" Value="{StaticResource Thumbupdowncheckboxtemplate}"/>
    <Setter Property="Width" Value="40"/>
    <Setter Property="Height" Value="40"/>
  </Style>

</Application.Resources>
In the “States”  panel, click on the “Checked” state. This will add quite some XAML code: you will see a “VisualStateManager.VisualStateGroups” tag appear, defining all three states of the “CheckStates”  group, i.e. “Checked”, “Unchecked”  and “Indeterminate”.
  • Now Select the “Unchecked”  state
  • Make sure the “Rectangle” still is selected in the “Objects and Timeline”  panel
  • Select the “Properties” tab on the top right again
  • Select the (now green) “Fill” Rectangle again.
  • In the box where you previously typed “Green” (which will say ”#FF008000” now), type “Red”. The thumbs-up image will now turn red
  • flipScroll down to “Transform” again
  • Select the right most tab
  • Select the “Flip Y-axis” button, in the middle. The thumbs-up image will now flip vertically and turn into a thumbs-down picture.
  • recordingLocate the little red button on top of the design pane that says “Unchecked state recording is on”. Click it and the text should change into “Unchecked state recording is off”.
If you press F5, the project will compile and run (yes, that works from Blend as well), and you will see that the checkbox shows a green thumbs-up image when selected, and a red thumbs-down image when unselected.
Now, for a finale to make things a little more visually appealing:
  • AnimateGo back to the “States” tab again
  • Select the Textbox with “0 s” in the “Default transition” panel above state “Unchecked”
  • Type 0.5 in the text box
  • And press F5 again.
You will now see the thumbs not simply flip: now it rotates in half a second and change color from red via orange to green. By simply specifying a time you tell the application to actually infer an animation. And there you are. A completely customized, animated, thumb-up-thumbs-down control with just some clicking around. Code-wise it behaves just like a normal checkbox. And if you want to make more of these checkboxes, just select a standard CheckBox, right click it, Select “Edit template/Apply Resources/Thumbupdowncheckboxstyle” and boom – yet another Thumbup-thumbsdown control.
Final XAML:
<Application.Resources>

  <ControlTemplate x:Key="Thumbupdowncheckboxtemplate" TargetType="CheckBox">
    <Rectangle x:Name="rectangle" Fill="Green" Width="{TemplateBinding Width}" 
               Height="{TemplateBinding Height}" 
               d:DesignWidth="75" d:DesignHeight="75" RenderTransformOrigin="0.5,0.5">
      <Rectangle.RenderTransform>
        <CompositeTransform TranslateX="0.5" TranslateY="0.5"/>
      </Rectangle.RenderTransform>
      <Rectangle.OpacityMask>
        <ImageBrush Stretch="Fill" ImageSource="icons/thumbsup.png"/>
      </Rectangle.OpacityMask>
      <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CheckStates">
          <VisualStateGroup.Transitions>
            <VisualTransition GeneratedDuration="0:0:0.5"/>
          </VisualStateGroup.Transitions>
          <VisualState x:Name="Indeterminate"/>
          <VisualState x:Name="Unchecked">
            <Storyboard>
              <DoubleAnimation Duration="0" To="-1" 
                    Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)" 
                    Storyboard.TargetName="rectangle" d:IsOptimized="True"/>
              <ColorAnimation Duration="0" To="Red" 
                   Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" 
                   Storyboard.TargetName="rectangle" d:IsOptimized="True"/>
            </Storyboard>
          </VisualState>
          <VisualState x:Name="Checked"/>
        </VisualStateGroup>
      </VisualStateManager.VisualStateGroups>
    </Rectangle>
  </ControlTemplate>

  <Style x:Key="Thumbupdowncheckboxstyle" TargetType="CheckBox">
    <Setter Property="Template" Value="{StaticResource Thumbupdowncheckboxtemplate}"/>
    <Setter Property="Width" Value="40"/>
    <Setter Property="Height" Value="40"/>
  </Style>
</Application.Resources>
You could of course go on and define all other states, but this already works pretty well IMHO ;-). Oh and by the way: this should work on any XAML platform, not just on Windows Phone. And for those who don’t like typing or copy-and-pasting, here is, as always, the complete demo solution.
Thanks to Willem Meints for helping me out via twitter on default setters for styles.

22 January 2012

JSON deserialization with JSON.net: basics

I’ve been contemplating an article about handling JSON for some time now, but it turned out to be a rather long article. So I’d thought to try something new, and write a short series in three parts.

  • Part 1 handles the basics
  • Part 2 handles advanced deserialization with class hierarchies
  • Part 3 handles a caching-and-updating scenarios.

And this is part 1 ;-)

This whole article actually boils down to one line of code, but I need to go trough some hooplah to show you how to use it. It all begins with the data. Consider this piece of quite readable JSON, describing a few recent Windows Phone models.

[
  {
    "Brand": "Nokia","Type" : "Lumia 800",
    "Specs":{"Storage" : "16GB", "Memory": "512MB","Screensize" : "3.7"}
  },
  {
    "Brand": "Nokia", "Type" : "Lumia 710",
    "Specs":{"Storage" : "8GB","Memory": "512MB","Screensize" : "3.7"}
  },  
  { "Brand": "Nokia","Type" : "Lumia 900",
    "Specs":{"Storage" : "8GB", "Memory": "512MB","Screensize" : "4.3" }
  },
  { "Brand": "HTC ","Type" : "Titan II",
    "Specs":{"Storage" : "16GB", "Memory": "512MB","Screensize" : "4.7" }
  },
  { "Brand": "HTC ","Type" : "Radar",
    "Specs":{"Storage" : "8GB", "Memory": "512MB","Screensize" : "3.8" }
  }
]

JSON is rather compact, which is a great feature when you are developing for mobile devices. It has also a few downsides as far as client programming is concerned:

  • generating client code for it that does all the parsing and calling, as for SOAP, is not a standard feature of Visual Studio,
  • it’s almost impossible to read for an ordinary human being,
  • deciphering it into classes is a lot of work,
  • hand coding a parser for it is not fun.

Which is why you don’t. There are several ways of generating classes from JSON, the simplest way is this website: json2csharp by Jonathan Keith. You copy a JSON result into the upper textbox, hit the “Generate” button and out come your classes:Json2Csharp

There are more sites that do the same, by the way, but this is what I use. Next steps:

  • Fire up Visual Studio
  • Create a new Windows Phone project (for instance JsonDemo)
  • Plonk the classes generated above in the project. Bonus cookies if you split them in separate files and add namespaces to them. Bonus donut if you, like me, think “RootObject” is actually a pretty ugly name for an object - so change it to "Phone".
  • Click Tools/Library Package Manager/Manage NuGet Packages for Solution (you do have the NuGet Package Manager installed, don’t you? If not, stop whatever you are doing now and get it right this instance, you hear me ;)? )
  • Search for JSON.Net
  • Click install. This will add a reference to NewtonSoft.Json.dll to your product.
  • Add references to Microsoft.Phone.Reactive and System.Observable because they are going to be needed in the next step.

To make the result visible, add some XAML to the default content panel in Mainpage.Xaml – just a button and a templated ListBox, no rocket science here:

<StackPanel>
  <Button Name="Load"
      VerticalAlignment="Top"
      Content="Load phones" Click="Load_Click" />
  <ListBox x:Name="PhoneList" Height="532">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Brand}" 
                 Margin="0,0,12,0" />
          <TextBlock Text="{Binding Type}"/>
        </StackPanel>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</StackPanel>

Finally, open MainPage.Xaml.cs and add the method Load_Click as displayed below.

using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Reactive;
using Newtonsoft.Json;

namespace JsonDemo
{
  public partial class MainPage : PhoneApplicationPage
  {
    // Constructor
    public MainPage()
    {
      InitializeComponent();
    }

    private void Load_Click(object sender, RoutedEventArgs e)
    {
      var w = new WebClient();
      Observable
        .FromEvent<DownloadStringCompletedEventArgs>(w, "DownloadStringCompleted")
        .Subscribe(r =>
        {
          var deserialized = 
            JsonConvert.DeserializeObject<List<Phone>>(r.EventArgs.Result);
          PhoneList.ItemsSource = deserialized;
        });
      w.DownloadStringAsync(
        new Uri("http://www.schaikweb.net/dotnetbyexample/JSONPhones1.txt"));
    }
  }
}

JsonSerializerAnd there it is, the one line of code that this is all about. Call the DeserializeObject method, template it with the return type you want, and stuff the JSON string in it. Result: a list of objects with their properties filled, even if there are things like nested objects (specs in these case) and arrays in there.

If you run the demo solution you get the result displayed in the image on the right. Keep in mind this code is by no means Windows Phone specific. There are JSON.Net implementations for virtually all frameworks available. So should you feel the need to use this from Silverlight or full .NET: it’s there.

You should, by the way, pay attention to the structure of the JSON. The code I show works for a list. A list in JSON starts with a square bracket: [. If your JSON starts with a curly brace: { then you get returned a single object - a so called root object. In that case, your deserialization should code return a single object in stead of a list as well, i.e. something like

var deserialized = 
     JsonConvert.DeserializeObject<Phone>(r.EventArgs.Result);

Finally, a ninja tip:

  • Click Tools/Library Package Manager/Manage NuGet Packages for Solution  again
  • Search for SharpGIS.GZipWebClient
  • Click install
  • Change “WebClient” in the Load_Click method to SharpGIS.GZipWebClient

This plug-in replacement for WebClient by Morten Nielsen adds support for GZIP compressed web requests – this reduces network traffic even further, apparently boosting load performance significantly. You won’t really notice the difference on such a small data files as used in this sample, but as your JSON return values get larger, so will be the impact of using this library.

For the record: I am not ill nor do I have forsaken MVVM, but I tried to make the example as simple as possible so yes, I used a little code behind, as to not to cloud the solution in architectural frills. ;-)

Thanks to Matthijs Hoekstra for putting me on track to this, and to fellow #wp7nl developer Leon Zandman for correcting some annoying typos.