Martin Suchan – BloQ Random #WPdev stuff

25Mar/140

Using Json Design-time data in Windows Phone 8 apps

In my previous article I've shown a new way, how can we use Json file as a data source for design-time data in new Windows 8.1 apps. Luckily, it's possible to use similar approach in Windows Phone 8 apps as well!

So how does it work in Windows Phone 8 apps? First of all, the approach is a bit different. We cannot define the reference to the json file directly in our XAML just like we used in Windows 8.1 app. Instead we have to load the data in constructor of our ViewModel class.

In my sample I'm going to use this simple model, that should be filled with real data in design time:

public class EpisodeModel
{
    public string ShowName { get; set; }
    public string ImageUrl { get; set; }
    public string EpisodeName { get; set; }
}
 
public class TimelineModel
{
    public EpisodeModel[] Rows { get; set; }
}

Json file with data for this model:

{
    "Rows": [{
        "ShowName": "MENU dom\u016f",
        "ImageUrl": "https:\/\/im.stream.cz\/episode\/533154b8ad72d58bcb310000\/80\/80\/120.jpg",
        "EpisodeName": "Bor\u0161\u010d",
    },
    {
        "ShowName": "A DOST!",
        "ImageUrl": "https:\/\/im.stream.cz\/episode\/53313698204916e7ccee0300\/80\/80\/120.jpg",
        "EpisodeName": "Anglick\u00e1 slanina speci\u00e1l",
    }]
}

Ok, now let's say we want to display on our MainPage ListBox of Episodes. In Design-time we want to display data from our json file and when the app is running, we want to load real data from our web server. One way, how to set specific class as a design-time data source is using the d:DataContext property like this in MainPage.xaml, in the root PhoneApplicationPage object:

d:DataContext="{d:DesignInstance Type=json:MainViewModel, IsDesignTimeCreatable=True}"

The MainViewModel class is then created by Visual Studio or Blend in background, when the page preview is loaded, including all data in it. And this is also the right place where to load our json data.

The MainViewModel constructor uses method for opening the json file from resources, reading it as a string and parsing it into our model class using Json.NET library, downloaded on NuGet:

public MainViewModel()
{
    TimelineModel timeline = GetObjectSync<TimelineModel>("timeline");
    LatestEpisodes = new ObservableCollection<EpisodeModel>(timeline.Rows);
}
 
private static T GetObjectSync<T>(string filename)
{
    // design time data must not use async code!
    // we cannot use StorageFile classes, but old WP7 API works here
    // note the Json file must be added as 'Resource', otherwise it won't work
    Uri fileUri = new Uri(string.Format("/WP.DesignTime.Json;component/DesignTime/{0}.json", filename), UriKind.Relative);
    StreamResourceInfo json = Application.GetResourceStream(fileUri);
    using (StreamReader sr = new StreamReader(json.Stream))
    {
        string fileString = sr.ReadToEnd();
        return JsonConvert.DeserializeObject<T>(fileString);
    }
}

json

Let's go through this code step by step. The constructor of MainViewModel just uses the GetObjectSync main method. This method is called with name of a file and target class type, that we want.

In GetObjectSync we first create full file Uri referencing our "timeline.json" file in app resources. Note our project is called "WP.DesignTime.Json" and the file is in "DesignTime" folder. Here's the first important note, the file must be added as a Resource into our project. I've not found other way, how to load Content file in my app synchonously. That's also another important note - all design-time data loading code must not use async/await code! It just won't work even when calling async methods synchronously. Luckily Application.GetResourceStream method works fine with Resource files and it's fully synchronous, so that's exactly what we need.

The rest of the GetObjectSync is quite simple. Once we got the full file Uri, we receive the StreamResourceInfo about our file, than we got the Stream object representing our file, we read that stream using StreamReader and then we just deserialize the string using JsonConvert into plain EpisodeModel object. And that's all. Note, just like in Windows 8.1, the target model we're deserializing must be really simple, nothing fancy. The Visual Studio engine for loading design time data is really sensitive when any complicated API is used in the loading process and in the result it just does not load anything, so be sure to not use anything non-trivial there.

Note the sample I've provided and described is really simplified model how to load the data, stripped down of all MVVM code to make it short and easy to read.

The sample of this solution can be downloaded here from GitHub.

And as always, let me know what do you think about this approach, or if there is anything not clear, just let me know in discussion below or using Twitter, thanks!