How to provide cloud-based, JSON data to Windows 8 Metro Grid Applications – Part 3


Exercise 1: Modifying our Windows 8 Metro Grid application to consume JSON data across http

jzv2zzxa  

Download the Windows Azure SDK and Tools - To do these posts, absolutely necessary

How to provide cloud-based, JSON data to Windows 8 Metro Grid Applications - Part 1 https://blogs.msdn.com/b/brunoterkaly/archive/2012/06/12/how-to-provide-cloud-based-json-data-to-windows-8-metro-grid-applications.aspx
How to provide cloud-based, JSON data to Windows 8 Metro Grid Applications - Part 2 https://blogs.msdn.com/b/brunoterkaly/archive/2012/06/14/how-to-provide-cloud-based-json-data-to-windows-8-metro-grid-applications-part-2.aspx
How to provide cloud-based, JSON data to Windows 8 Metro Grid Applications - Part 3 https://blogs.msdn.com/b/brunoterkaly/archive/2012/06/15/how-to-provide-cloud-based-json-data-to-windows-8-metro-grid-applications-part-3.aspx
How to provide cloud-based, JSON data to Windows 8 Metro Grid Applications - Part 4 https://blogs.msdn.com/b/brunoterkaly/archive/2012/06/17/how-to-provide-cloud-based-json-data-to-windows-8-metro-grid-applications-part-3-windows-azure-cloud.aspx
Source Code

This post is broken down into 3 exercises.

Exercise 1 Add a new class, called MySampleDataSource. This will replace SampleDataSource.

  1. MySampleDataSource
    • Will provide JSON data obtained with an http web request
    • Is very similar to SampleDataSource (provided by default)
      • When you select "File/New/Metro Grid"
Exercise 2 Modify App.xaml.cs to invoke code in MySampleDataSource. This code will retrieve JSON data across http.

  1. App.xaml.cs
    • Contains startup code
    • This is where we want to make our JSON web request
    • Requires only two lines of code in App.xaml.cs
Exercise 3 Modify the user interface (XAML) code to bind correctly to the new data source.

  1. 3 xaml files
    • GroupDetailPage.xaml
    • GroupedItemsPage.xaml
    • ItemDetailPage.xaml
  2. 3 code-behind files
    • GroupDetailPage.xaml.cs
    • GroupedItemsPage.xaml.cs
    • ItemDetailPage.xaml.cs

Exercise 1: Add a new class, called MySampleDataSource.
This will replace SampleDataSource. SampleDataSource has hard-code values to supply data to the user interface. MySampleDataSource will make asynchronous http calls and parse JSON data for the user interface. We will also add MyStack.cs to help with assigning items to groups.


Exercise 1: Task 1 - Introducing MySampleDataSource.cs and MyStack.cs

The next step is to add MySampleDataSource.cs module below.

This is a re-write of SampleDataSource.cs (provided by VS 2012 RC). Not many changes. I changed some of the base data elements very slightly. I also added a method to do the asynchronous http request for JSON data.

I have also added a MyStack.cs module. This is used to group items into groups. The code expects that the JSON data is ordered correctly when it is sent by the server (Windows Azure Cloud back-end).

It should be sorted by group, then by item.

There are two source files we will add.

File 1 MySampleDataSource.cs It will allow our Windows 8 Metro Grid app make JSON requests
File 2 MyStack.cs It will allow us to group items into groups. For example, adding individual motorcycles into groups like Sport Bikes and Exotic Bikes.

 

  1. Go back to Visual Studio 2012 RC.
  2. If not visible, view "Solution Explorer."
    • Click on the "View" menu and select "Solution Explorer."
  3. Right mouse click on the "DataModel" folder and add a new class.
    hylj4vpk
    • Call that new class "MySampleDataSource.cs."
  4. Paste in the code from the snippet below. See "Code snippet 1 - MySampleDataSource.cs."
  5. Right mouse click on the "DataModel" folder and add a new class.
    • Call that new class "MyStack.cs."
  6. Paste in the code from the snippet below. See "Code snippet 2 - MyStack.cs."
  7. Solution Explorer should look like this:
    donar2c4
  8. After you’ve pasted the code in for MyStack.cs and MySampleDataSource.cs, you can try compiling your application.
    1. Go to the Build menu and choose Build Solution.
      nuiyayct
    2. Hopefully, you’ll receive 0 errors on the “output window.”
      sphrfudz

  1. Notice that the 3 new classes replicate the structure and behavior of the built in types.
  2. You can see these objects after you’ve added the two modules.
  3. You will then rebuild your solution and switch to “Class View.”
    • The red box is what we will add.
      • MySampleDataCommon, MySampleDataGroup, MySampleDataItem
    • The objects in the green box were provided by Visual Studio.
      • SampleDataCommon, SampleDataGroup, SampleDataItem
  4. We will add MySampleSource.cs.
    • MySampleSource.cs will include the follow key classes that allow us to asynchronous call into a JSON data store using http.
  5. We will also add MyStack.cs
    1. This class will help manage individual items and their groups.

xwjsoroz 

Some additional notes regarding the code

  1. We added some using statements.
    • using System.Net.Http;
      • Supports http web requests
    • using Windows.Data.Json;
      • Provides useful parsing functions
    • using System.Threading.Tasks;
      • Support for threads
  2. I slightly changed the name conventions.
    • From SampleData to MySampleData
      • Not a good idea for production code, but good for a quick hack.
  3. You should easily be able to see parallels between the new MySampleDataSource.cs versus the old SampleDataSource.cs.
    • Changes to the base class (MySampleDataCommon)
  4. We parse JsonObjects in the code.
    • You will see code like currGroup.TryGetValue("_subTitle", out val).
      ?

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      public MySampleDataGroup(JsonObject currGroup)
      {
          //
          //  Extract attributes from the JsonObject
          //
          IJsonValue val;
          if (currGroup.TryGetValue("_uniqueId", out val))
              this.UniqueId = val.GetString();
        
          if (currGroup.TryGetValue("_title", out val))
              this.Title = val.GetString();
        
          if (currGroup.TryGetValue("_subTitle", out val))
              this.Subtitle = val.GetString();
        
          if (currGroup.TryGetValue("_imagePath", out val))
              this.SetImage(val.GetString());
        
          if (currGroup.TryGetValue("_description", out val))
              this.Description = val.GetString();
        
          if (currGroup.TryGetValue("_content", out val))
              this.Content = val.GetString();
      • This code allows you to extract strings from JSON data
      • It is how we populate MySampleDataItem, and MySampleDataGroup.
  5. We implement a Stack object to help us group items together.
  6. Items belong to a group so a Stack mechanism is used to track this relationship.

Code snippet 1 - MySampleDataSource.cs

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.ApplicationModel.Resources.Core;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using System.Net.Http;
using Windows.Data.Json;
using System.Threading.Tasks;
  
// The data model defined by this file serves as a representative example of a strongly-typed
// model that supports notification when members are added, removed, or modified.  The property
// names chosen coincide with data bindings in the standard item templates.
//
// Applications may use this model as a starting point and build on it, or discard it entirely and
// replace it with something appropriate to their needs.
  
namespace FastMotorcycle
{
    /// <SUMMARY>
    /// 
    /// This is the base class to MySampleDataGroup and MySampleDataGroup
    /// It provides some common data structures and some eventing mechanisms
    /// 
    /// </SUMMARY>
    [Windows.Foundation.Metadata.WebHostHidden]
    public abstract class MySampleDataCommon : FastMotorcycle.Common.BindableBase
    {
        private static Uri _baseUri = new Uri("ms-appx:///");
  
        public MySampleDataCommon()
        {
        }
        // Constructor for the core elements of the base class.
        public MySampleDataCommon(String uniqueId, string title, string subTitle, string imagePath, string description, string content)
        {
            this._uniqueId = uniqueId;
            this._title = title;
            this._subTitle = subTitle;
            this._description = description;
            this._content = content;
            this.SetImage(imagePath);
  
        }
  
        private string _uniqueId = string.Empty;
        public string UniqueId
        {
            get { return this._uniqueId; }
            set { this.SetProperty(ref this._uniqueId, value); }
        }
  
        private string _title = string.Empty;
        public string Title
        {
            get { return this._title; }
            set { this.SetProperty(ref this._title, value); }
        }
  
        private string _subTitle = string.Empty;
        public string Subtitle
        {
            get { return this._subTitle; }
            set { this.SetProperty(ref this._subTitle, value); }
        }
  
        private string _description = string.Empty;
        public string Description
        {
            get { return this._description; }
            set { this.SetProperty(ref this._description, value); }
        }
  
        private string _content = string.Empty;
        public string Content
        {
            get { return this._content; }
            set { this.SetProperty(ref this._content, value); }
        }
  
  
        private ImageSource _image = null;
  
        private string _imagePath = null;
  
        public string ImagePath
        {
            get { return _imagePath; }
            set { _imagePath = value; }
        }
  
  
        public ImageSource Image
        {
            get
            {
                if (this._image == null && this._imagePath != null)
                {
                    this._image = new BitmapImage(new Uri(MySampleDataCommon._baseUri, this._imagePath));
                }
                return this._image;
            }
  
            set
            {
                this._imagePath = null;
                this.SetProperty(ref this._image, value);
            }
        }
        // This method makes sure the image is viewable as an ImageSource
        public void SetImage(String path)
        {
            this._image = null;
            this._imagePath = path;
            this.OnPropertyChanged("Image");
        }
    }
  
    /// <SUMMARY>
    /// 
    /// This is the core MySampleDataItem. It will hold information about motorcycles and the 
    /// group to which they belong
    /// 
    /// </SUMMARY>
    public class MySampleDataItem : MySampleDataCommon
    {
        public MySampleDataItem()
        {
        }
        public MySampleDataItem(String uniqueId, string title, string subTitle, string description, string imagePath, string content, MySampleDataGroup group)
            : base(uniqueId, title, subTitle, description, imagePath, content)
        {
            this._group = group;
        }
  
        // The main constructor for an item. The code here searches for attributes
        // in JSON objects. It then populate relevant properties.
        // In this implementation, it will really hold "Fast Motorcycles"
        public MySampleDataItem(JsonObject currItem, MySampleDataGroup currGroup)
        {
            string uniqueId = string.Empty, title = string.Empty, subTitle = string.Empty,
                    description = string.Empty, imagePath = string.Empty, content = string.Empty;
  
            IJsonValue val;
  
            if (currItem.TryGetValue("_uniqueId", out val))
                uniqueId = val.GetString();
  
            if (currItem.TryGetValue("_title", out val))
                title = val.GetString();
  
            if (currItem.TryGetValue("_subTitle", out val))
                subTitle = val.GetString();
  
            if (currItem.TryGetValue("_imagePath", out val))
                imagePath = val.GetString();
  
            if (currItem.TryGetValue("_description", out val))
                description = val.GetString();
  
            if (currItem.TryGetValue("_content", out val))
                content = val.GetString();
  
  
            // Inherited members
            this.UniqueId = uniqueId;
            this.Title = title;
            this.Subtitle = subTitle;
            this.SetImage(imagePath);
            this.Description = description;
            this.Content = content;
            // Additional data member (items point to their parent)
            this.Group = currGroup;
  
  
        }
        private MySampleDataGroup _group;
  
        public MySampleDataGroup Group
        {
            get { return this._group; }
            set { this.SetProperty(ref this._group, value); }
        }
    }
  
    /// <SUMMARY>
    /// 
    /// This is the fundamental type for groups. It will be either Sport Bikes or Exotic Bikes. It will hold a 
    /// collection of individual MySampleDataItem objects.
    /// 
    /// </SUMMARY>
    public class MySampleDataGroup : MySampleDataCommon
    {
        public MySampleDataGroup()
        {
        }
  
        public MySampleDataGroup(String uniqueId, string title, string subTitle, string imagePath, string description, string content)
            : base(uniqueId, title, subTitle, imagePath, description, content)
        {
        }
  
        public MySampleDataGroup(JsonObject currGroup)
        {
            //
            //  Extract attributes from the JsonObject
            //
            IJsonValue val;
            if (currGroup.TryGetValue("_uniqueId", out val))
                this.UniqueId = val.GetString();
  
            if (currGroup.TryGetValue("_title", out val))
                this.Title = val.GetString();
  
            if (currGroup.TryGetValue("_subTitle", out val))
                this.Subtitle = val.GetString();
  
            if (currGroup.TryGetValue("_imagePath", out val))
                this.SetImage(val.GetString());
  
            if (currGroup.TryGetValue("_description", out val))
                this.Description = val.GetString();
  
            if (currGroup.TryGetValue("_content", out val))
                this.Content = val.GetString();
  
  
        }
  
        //
        // MySampleDataGroup has a collection of MySampleDataItem
        //
        private ObservableCollection<MYSAMPLEDATAITEM> _items = new ObservableCollection<MYSAMPLEDATAITEM>();
        public ObservableCollection<MYSAMPLEDATAITEM> Items
        {
            get { return this._items; }
        }
  
        public IEnumerable<MYSAMPLEDATAITEM> TopItems
        {
            // Provides a subset of the full items collection to bind to from a GroupedItemsPage
            // for two reasons: GridView will not virtualize large items collections, and it
            // improves the user experience when browsing through groups with large numbers of
            // items.
            //
            // A maximum of 12 items are displayed because it results in filled grid columns
            // whether there are 1, 2, 3, 4, or 6 rows displayed
            get { return this._items.Take(12); }
        }
    }
  
    /// <SUMMARY>
    /// 
    /// This is the main container class for MySampleDataItem and MySampleDataGroup objects
    /// Creates a collection of groups and items with data from a an JSON/http web request.
    /// 
    /// </SUMMARY>
    public sealed class MySampleDataSource
    {
        private static MySampleDataSource _sampleDataSource = new MySampleDataSource();
        private static MyStack mystack = new MyStack();
  
        //
        // This is the main entry point for all objects into the system.
        // Essentially, we add MySampleDataGroup objects to _allGroups. Each
        // MySampleDataGroup object will have a collection of MySampleDataItem objects.
        //
        private ObservableCollection<MYSAMPLEDATAGROUP> _allGroups = new ObservableCollection<MYSAMPLEDATAGROUP>();
  
        public ObservableCollection<MYSAMPLEDATAGROUP> AllGroups
        {
            get { return this._allGroups; }
        }
  
        public static IEnumerable<MYSAMPLEDATAGROUP> GetGroups(string uniqueId)
        {
            if (!uniqueId.Equals("AllGroups")) throw new ArgumentException("Only 'AllGroups' is supported as a collection of groups");
  
            return _sampleDataSource.AllGroups;
        }
  
        public static MySampleDataGroup GetGroup(string uniqueId)
        {
            // Simple linear search is acceptable for small data sets
            var matches = _sampleDataSource.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));
            if (matches.Count() == 1) return matches.First();
            return null;
        }
  
        public static MySampleDataItem GetItem(string uniqueId)
        {
            // Simple linear search is acceptable for small data sets
            var matches = _sampleDataSource.AllGroups.SelectMany(group => group.Items).Where((item) => item.UniqueId.Equals(uniqueId));
            if (matches.Count() == 1) return matches.First();
            return null;
        }
  
        //
        // This is the main method that retrieves JSON data by making an asynchronous web requet.
        // Currently, it is calling a local emulator instance of Windows Azure.
        // Note the web address of "https://127.0.0.1/FastMotorcycleListService...."
        //
        public static async Task<JSONARRAY> LoadRemoteDataAsync()
        {
            //
            // Retrieve fastmotorcycle data from Azure
            //
            var client = new HttpClient();
            client.MaxResponseContentBufferSize = 1024 * 1024; // Read up to 1 MB of data
            var response = await client.GetAsync(new Uri("https://127.0.0.1:81/FastMotorcycleListService.svc/list/SampleData/1"));
            var result = await response.Content.ReadAsStringAsync();
  
            //
            // Parse the JSON fastmotorcycle data
            //
            var fastmotorcycles = JsonArray.Parse(result);
  
            //
            // Convert the JSON objects into MySampleDataItems and MySampleDataGroups
            // Assume that we are ordered by Group, then by Item
            //
            JsonArray array = fastmotorcycles;
            IJsonValue groupKey;
            foreach (var item in array)
            {
                var obj = item.GetObject();
  
                if (!obj.TryGetValue("_group", out groupKey))
                {
                    continue// must have _group, skip if not
                }
                else
                {
                    // 
                    // This code tracks "groups"
                    // If a group already exists, and we have a new item to add, then add item to group
                    // If we do find a new group, add it to our stack. The stack object is used
                    // to add new items to a group.
                    //
                    var currGroup = groupKey.GetObject();
                    MySampleDataGroup newGroup = new MySampleDataGroup(currGroup);
                    if (!mystack.Exists(newGroup))
                    {
                        mystack.Add(newGroup);
                    }
  
                }
                //
                // Add item to latest group
                // Make sure child points to its own group.
                // Children items point to parent (group)
                //
                MySampleDataItem newItem = new MySampleDataItem(obj, mystack.GetLatestGroup());
                mystack.AddChildToLatestGroup(newItem);
            }
            //
            // Loop through all groups and a group to _sampleDataSource.AllGroups
            // This is one of the main entry points for data into the user interface.
            //
            MySampleDataGroup loadedGroup = null;
            for (int i = 0; i < mystack.stackCount; i++)
            {
  
                loadedGroup = mystack.GetEntry(i); // Retrieve group from bottom of stack
                _sampleDataSource.AllGroups.Add(loadedGroup);
  
            }
            return array;
        }
        public MySampleDataSource()
        {
            // Empty. Data retrieved from JSON call.
        }
    }
}

Code snippet 2 - MyStack.cs

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
  
namespace FastMotorcycle
{
  
    public class MyStack
    {
        public int stackCount;
        public MySampleDataGroup[] stack;
  
  
        public MyStack()
        {
            stackCount = 0;
            stack = new MySampleDataGroup[1000];
        }
  
  
        internal void Add(MySampleDataGroup myNode)
        {
            stack[stackCount++] = myNode;
        }
  
        internal bool IsChild(int i)
        {
            return false// stack[i].Depth > stack[i - 1].Depth;
        }
  
        internal bool IsEqual(int k)
        {
            return false; // stack[k].Depth == stack[k - 1].Depth;
        }
  
        internal bool IsLast(int k)
        {
            return k == stackCount;
        }
  
  
  
        public bool Exists(MySampleDataGroup newGroup)
        {
            if (stackCount == 0)
                return false;
            else
            {
                return stack[stackCount - 1].UniqueId == newGroup.UniqueId;
            }
        }
  
        public void AddChildToLatestGroup(MySampleDataItem newItem)
        {
            newItem.Group = stack[stackCount - 1];
            stack[stackCount - 1].Items.Add(newItem);
        }
  
        public  MySampleDataGroup GetEntry(int i)
        {
            return stack[i];
        }
  
        public  MySampleDataGroup GetLatestGroup()
        {
            return stack[stackCount - 1];
        }
    }
}

Exercise 2: Modify App.config.cs to invoke code in MySampleDataSource

There is code in MySampleDataSource that needs to be called. This code will make an asynchronous http request to retrieve JSON data.

  1. Open the file App.xaml.cs
    g2tp4ltv
  2. Add the code highlighted below
    454u4szh
    • Take note that we've added two lines of code here.
      • We declared an object of type MySampleDataSource
      • We have called LoadRemoteDataAsync() on that object

Code Snippet 3 - App.xaml.cs

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
using FastMotorcycle.Common;
  
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
  
// The Grid App template is documented at https://go.microsoft.com/fwlink/?LinkId=234226
  
namespace FastMotorcycle
{
    /// <SUMMARY>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </SUMMARY>
    sealed partial class App : Application
    {
  
        public static MySampleDataSource sampleData = new MySampleDataSource();
  
        /// <SUMMARY>
        /// Initializes the singleton Application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </SUMMARY>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }
  
        /// <SUMMARY>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </SUMMARY>
        /// <PARAM name="args">Details about the launch request and process.</PARAM>
        protected override async void OnLaunched(LaunchActivatedEventArgs args)
        {
            // Do not repeat app initialization when already running, just ensure that
            // the window is active
            if (args.PreviousExecutionState == ApplicationExecutionState.Running)
            {
                Window.Current.Activate();
                return;
            }
  
            await MySampleDataSource.LoadRemoteDataAsync();
  
            // Create a Frame to act as the navigation context and associate it with
            // a SuspensionManager key
            var rootFrame = new Frame();
            SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
  
            if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                // Restore the saved session state only when appropriate
                await SuspensionManager.RestoreAsync();
            }
  
            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups"))
                {
                    throw new Exception("Failed to create initial page");
                }
            }
  
            // Place the frame in the current Window and ensure that it is active
            Window.Current.Content = rootFrame;
            Window.Current.Activate();
        }
  
        /// <SUMMARY>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </SUMMARY>
        /// <PARAM name="sender">The source of the suspend request.</PARAM>
        /// <PARAM name="e">Details about the suspend request.</PARAM>
        private async void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            await SuspensionManager.SaveAsync();
            deferral.Complete();
        }
    }
}

 


Exercise 3: Modify the user interface (XAML) code to bind correctly to the new data source.
Remember, we have 3 files for the user interface:

GroupDetailPage.xaml Gives details about a group. In this case, the group can be Sport Bikes or Exotic Bikes
GroupedItemsPage.xaml Shows Groups and Items. This is the initial page seen by users. You can navigate to the group or to an individual item from here.
ItemDetailPage.xaml Provides details about and individual item. In this case, it is about an individual motorcycle.

Modify the GroupDetailPage.xaml

  1. We will modify GroupDetailPage.xaml
  2. We'd like to bind to the "Content" property for a group.
    • That is just an extra data element that we've added.
    • We will add 2 lines of XAML Code
      image
    • The updated file should look like this:
      srvhkdbd

Modify the GroupDetailPage.xaml.cs

  1. Open GroupDetailPage.xaml.cs
  2. Navigate to the method LoadState()
    • Change the following line:
      • var group = SampleDataSource.GetGroup((String)navigationParameter);
  3. Navigate to the method ItemView_ItemClick()()
    • Change the following line:
      • var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
  4. See line 42 and 57 in the code below.

GroupDetailPage.xaml.cs

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
  
// The Group Detail Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234229
  
namespace FastMotorcycle
{
    /// <SUMMARY>
    /// A page that displays an overview of a single group, including a preview of the items
    /// within the group.
    /// </SUMMARY>
    public sealed partial class GroupDetailPage : FastMotorcycle.Common.LayoutAwarePage
    {
        public GroupDetailPage()
        {
            this.InitializeComponent();
        }
  
        /// <SUMMARY>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </SUMMARY>
        /// <PARAM name="navigationParameter">The parameter value passed to
        /// <SEE cref="Frame.Navigate(Type, Object)" /> when this page wtas initially requested.
        /// </PARAM>
        /// <PARAM name="pageState">A dictionary of state preserved by this page during an earlier
        /// session.  This will be null the first time a page is visited.</PARAM>
        protected override void LoadState(Object navigationParameter, Dictionary<STRING, Object> pageState)
        {
            // TODO: Create an appropriate data model for your problem domain to replace the sample data
            var group = MySampleDataSource.GetGroup((String)navigationParameter);
            this.DefaultViewModel["Group"] = group;
            this.DefaultViewModel["Items"] = group.Items;
        }
  
        /// <SUMMARY>
        /// Invoked when an item is clicked.
        /// </SUMMARY>
        /// <PARAM name="sender">The GridView (or ListView when the application is snapped)
        /// displaying the item clicked.</PARAM>
        /// <PARAM name="e">Event data that describes the item clicked.</PARAM>
        void ItemView_ItemClick(object sender, ItemClickEventArgs e)
        {
            // Navigate to the appropriate destination page, configuring the new page
            // by passing required information as a navigation parameter
            var itemId = ((MySampleDataItem)e.ClickedItem).UniqueId;
            this.Frame.Navigate(typeof(ItemDetailPage), itemId);
        }
    }
}

GroupedItemsPage.xaml.cs

  1. Modify GroupedItemsPage.xaml.cs. You will add “My” to the appropriate lines. Take note of the following lines.
    1. Line 41
    2. Line 59
    3. Line 72

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
  
// The Grouped Items Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234231
  
namespace FastMotorcycle
{
    /// <SUMMARY>
    /// A page that displays a grouped collection of items.
    /// </SUMMARY>
    public sealed partial class GroupedItemsPage : FastMotorcycle.Common.LayoutAwarePage
    {
        public GroupedItemsPage()
        {
            this.InitializeComponent();
        }
  
        /// <SUMMARY>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </SUMMARY>
        /// <PARAM name="navigationParameter">The parameter value passed to
        /// <SEE cref="Frame.Navigate(Type, Object)" /> when this page was initially requested.
        /// </PARAM>
        /// <PARAM name="pageState">A dictionary of state preserved by this page during an earlier
        /// session.  This will be null the first time a page is visited.</PARAM>
        protected override void LoadState(Object navigationParameter, Dictionary<STRING, Object> pageState)
        {
            // TODO: Create an appropriate data model for your problem domain to replace the sample data
            var sampleDataGroups = MySampleDataSource.GetGroups((String)navigationParameter);
              
  
            this.DefaultViewModel["Groups"] = sampleDataGroups;
        }
  
        /// <SUMMARY>
        /// Invoked when a group header is clicked.
        /// </SUMMARY>
        /// <PARAM name="sender">The Button used as a group header for the selected group.</PARAM>
        /// <PARAM name="e">Event data that describes how the click was initiated.</PARAM>
        void Header_Click(object sender, RoutedEventArgs e)
        {
            // Determine what group the Button instance represents
            var group = (sender as FrameworkElement).DataContext;
  
            // Navigate to the appropriate destination page, configuring the new page
            // by passing required information as a navigation parameter
            this.Frame.Navigate(typeof(GroupDetailPage), ((MySampleDataGroup)group).UniqueId);
        }
  
        /// <SUMMARY>
        /// Invoked when an item within a group is clicked.
        /// </SUMMARY>
        /// <PARAM name="sender">The GridView (or ListView when the application is snapped)
        /// displaying the item clicked.</PARAM>
        /// <PARAM name="e">Event data that describes the item clicked.</PARAM>
        void ItemView_ItemClick(object sender, ItemClickEventArgs e)
        {
            // Navigate to the appropriate destination page, configuring the new page
            // by passing required information as a navigation parameter
            var itemId = ((MySampleDataItem)e.ClickedItem).UniqueId;
            this.Frame.Navigate(typeof(ItemDetailPage), itemId);
        }
    }
}

Code Snippet - ItemDetailPage.xaml.

  1. Open ItemDetailPage.xaml.
  2. We want to add a binding for Description
  3. Insert the following 3 lines on line 62, 63, 64, as seen in the code below.
    lo4vq1pd

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<COMMON:LAYOUTAWAREPAGE mc:Ignorable="d" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:common="using:FastMotorcycle.Common" xmlns:data="using:FastMotorcycle" xmlns:local="using:FastMotorcycle" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" IsTabStop="false" DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}" x:Class="FastMotorcycle.ItemDetailPage" x:Name="pageRoot">
  
  
        <!-- Collection of items displayed by this page -->
        <CollectionViewSource x:Name="itemsViewSource" d:Source="{Binding AllGroups[0].Items, Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}" Source="{Binding Items}"></CollectionViewSource>
    </Page.Resources>
  
    <!--
        This grid acts as a root panel for the page that defines two rows:
        * Row 0 contains the back button and page title
        * Row 1 contains the rest of the page layout
    -->
    <Grid DataContext="{Binding Group}" d:DataContext="{Binding AllGroups[0], Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}">
  
        <Grid.RowDefinitions>
            <RowDefinition Height="140"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
  
        <!-- Back button and page title -->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <BUTTON x:Name="backButton" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Click="GoBack">
            <TextBlock Text="{Binding Title}" x:Name="pageTitle" Grid.Column="1"></TextBlock>
        </Grid>
  
        <!--
            The remainder of the page is one large FlipView that displays details for
            one item at a time, allowing the user to flip through all items in the chosen
            group
        -->
        <FlipView tabIndex=1 Margin="0,-3,0,0" x:Name="flipView" ItemsSource="{Binding Source={StaticResource itemsViewSource}}" Grid.Row="1" AutomationProperties.Name="Item Details" AutomationProperties.AutomationId="ItemsFlipView">
  
            <FlipView.ItemTemplate>
                <DataTemplate>
  
                    <!--
                        UserControl chosen as the templated item because it supports visual state management
                        Loaded/unloaded events explicitly subscribe to view state updates from the page
                    -->
                    <UserControl Unloaded="StopLayoutUpdates" Loaded="StartLayoutUpdates">
                        <ScrollViewer x:Name="scrollViewer" Grid.Row="1">
  
                            <!-- Content is allowed to flow across as many columns as needed -->
                            <COMMON:RICHTEXTCOLUMNS Margin="117,0,117,47" x:Name="richTextColumns">
                                <RichTextBlock x:Name="richTextBlock" Width="560">
                                    <Paragraph>
                                        <Run Text="{Binding Title}" FontWeight="Light" FontSize="26.667"></Run>
                                        <LineBreak></LineBreak>
                                        <LineBreak></LineBreak>
                                        <Run Text="{Binding Subtitle}" FontWeight="SemiBold"></Run>
                                    </Paragraph>
                                    <Paragraph LineStackingStrategy="MaxHeight">
                                        <InlineUIContainer>
                                            <IMG Margin="0,20,0,10" x:Name="image" Source="{Binding Image}" Stretch="Uniform" MaxHeight="480">
                                        </InlineUIContainer>
                                    </Paragraph>
                                    <Paragraph>
                                        <Run Text="{Binding Description}" FontWeight="SemiLight"></Run>
                                    </Paragraph>
                                    <Paragraph>
                                        <Run Text="{Binding Content}" FontWeight="SemiLight"></Run>
                                    </Paragraph>
                                </RichTextBlock>
  
                                <!-- Additional columns are created from this template -->
                                <COMMON:RICHTEXTCOLUMNS.COLUMNTEMPLATE>
                                    <DataTemplate>
                                        <RichTextBlockOverflow Margin="80,0,0,0" Width="560">
                                            <RichTextBlockOverflow.RenderTransform>
                                                <TranslateTransform Y="4" X="-1"></TranslateTransform>
                                            </RichTextBlockOverflow.RenderTransform>
                                        </RichTextBlockOverflow>
                                    </DataTemplate>
                                </COMMON:RICHTEXTCOLUMNS.COLUMNTEMPLATE>
                            </COMMON:RICHTEXTCOLUMNS>
  
                            <VisualStateManager.VisualStateGroups>
  
                                <!-- Visual states reflect the application's view state inside the FlipView -->
                                <VisualStateGroup x:Name="ApplicationViewStates">
                                    <VisualState x:Name="FullScreenLandscape"></VisualState>
                                    <VisualState x:Name="Filled"></VisualState>
  
                                    <!-- Respect the narrower 100-pixel margin convention for portrait -->
                                    <VisualState x:Name="FullScreenPortrait">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="richTextColumns">
                                                <DiscreteObjectKeyFrame Value="97,0,87,57" KeyTime="0"></DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="MaxHeight" Storyboard.TargetName="image">
                                                <DiscreteObjectKeyFrame Value="400" KeyTime="0"></DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
  
                                    <!-- When snapped, the content is reformatted and scrolls vertically -->
                                    <VisualState x:Name="Snapped">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="richTextColumns">
                                                <DiscreteObjectKeyFrame Value="17,0,17,57" KeyTime="0"></DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Style" Storyboard.TargetName="scrollViewer">
                                                <DiscreteObjectKeyFrame Value="{StaticResource VerticalScrollViewerStyle}" KeyTime="0"></DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Width" Storyboard.TargetName="richTextBlock">
                                                <DiscreteObjectKeyFrame Value="280" KeyTime="0"></DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="MaxHeight" Storyboard.TargetName="image">
                                                <DiscreteObjectKeyFrame Value="160" KeyTime="0"></DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                        </ScrollViewer>
                    </UserControl>
                </DataTemplate>
            </FlipView.ItemTemplate>
        </FlipView>
  
        <VisualStateManager.VisualStateGroups>
  
            <!-- Visual states reflect the application's view state -->
            <VisualStateGroup x:Name="ApplicationViewStates">
                <VisualState x:Name="FullScreenLandscape"></VisualState>
                <VisualState x:Name="Filled"></VisualState>
  
                <!-- The back button respects the narrower 100-pixel margin convention for portrait -->
                <VisualState x:Name="FullScreenPortrait">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Style" Storyboard.TargetName="backButton">
                            <DiscreteObjectKeyFrame Value="{StaticResource PortraitBackButtonStyle}" KeyTime="0"></DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
  
                <!-- The back button and title have different styles when snapped -->
                <VisualState x:Name="Snapped">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Style" Storyboard.TargetName="backButton">
                            <DiscreteObjectKeyFrame Value="{StaticResource SnappedBackButtonStyle}" KeyTime="0"></DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Style" Storyboard.TargetName="pageTitle">
                            <DiscreteObjectKeyFrame Value="{StaticResource SnappedPageHeaderTextStyle}" KeyTime="0"></DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Grid>
</COMMON:LAYOUTAWAREPAGE>
  
  
<PRE></PRE><P></P><DIV></DIV><P></P><DIV></DIV>
<DIV></DIV>
  
  
  
</BUTTON>

Code Snippet ItemDetailPage.xaml.cs

  1. Open ItemDetailPage.xaml.cs
  2. Modify the code on lines 50 and 64, similar to what we've been doing previously.

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using FastMotorcycle.Data;
  
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
  
// The Item Detail Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234232
  
namespace FastMotorcycle
{
    /// <SUMMARY>
    /// A page that displays details for a single item within a group while allowing gestures to
    /// flip through other items belonging to the same group.
    /// </SUMMARY>
    public sealed partial class ItemDetailPage : FastMotorcycle.Common.LayoutAwarePage
    {
        public ItemDetailPage()
        {
            this.InitializeComponent();
        }
  
        /// <SUMMARY>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </SUMMARY>
        /// <PARAM name="navigationParameter">The parameter value passed to
        /// <SEE cref="Frame.Navigate(Type, Object)" /> when this page was initially requested.
        /// </PARAM>
        /// <PARAM name="pageState">A dictionary of state preserved by this page during an earlier
        /// session.  This will be null the first time a page is visited.</PARAM>
        protected override void LoadState(Object navigationParameter, Dictionary<STRING, Object> pageState)
        {
            // Allow saved page state to override the initial item to display
            if (pageState != null && pageState.ContainsKey("SelectedItem"))
            {
                navigationParameter = pageState["SelectedItem"];
            }
  
            // TODO: Create an appropriate data model for your problem domain to replace the sample data
            var item = MySampleDataSource.GetItem((String)navigationParameter);
            this.DefaultViewModel["Group"] = item.Group;
            this.DefaultViewModel["Items"] = item.Group.Items;
            this.flipView.SelectedItem = item;
        }
  
        /// <SUMMARY>
        /// Preserves state associated with this page in case the application is suspended or the
        /// page is discarded from the navigation cache.  Values must conform to the serialization
        /// requirements of <SEE cref="SuspensionManager.SessionState" />.
        /// </SUMMARY>
        /// <PARAM name="pageState">An empty dictionary to be populated with serializable state.</PARAM>
        protected override void SaveState(Dictionary<STRING, Object> pageState)
        {
            var selectedItem = (MySampleDataItem)this.flipView.SelectedItem;
            pageState["SelectedItem"] = selectedItem.UniqueId;
        }
    }
}

Conclusions – Next Steps
Note a few closing facts. First, we are not done. We need to create the cloud back end.

  1. You will not be able to run this code yet.
  2. We still need to create the Windows Azure Clod-based back end that will serve up JSON data.
    • That is the next post - creating the cloud project using Windows Azure
  3. The key code happens in LoadRemoteDataAsync().
    jv1bezws
  4. Let's reveiw this piece of code.
  5. It creates an HttpClient object
    • This allows us to connect to an http endpoint using http
  6. The code then performs and asynchronous call to the cloud service
  7. The next piece of code, loops through all the MySampleDataItem objects.
  8. We parse the JSON array and they extract all the attributes, one by one.
    d1vjqol5
  9. The remainder of the code, as previously explained, uses a stack to manage the relationship between groups and items. We essentially add all the items to a group. Next, the groups are added to the application.
    2h0ktexl
  10. Next stop, the cloud, we will code up the Azure side of the equation.

 


The Next Post is about the Cloud
Although I haven't shown you how to build the cloud project, here is what it looks like from a few angles.

  1. Here is the running web server, ready to take http requests and return JSON data.
    ghvg2deg
  2. Here is what the Visual Studio 2010 Azure project looks like. In the next post, we will build it from scratch. 
    ' ekn01kln
    • ** Important Note **
      Just to be clear, we cannot run the cloud project yet. So trying to run our Windows 8 Metro Grid client yet isn’t possible.

      Step 1 – Create the cloud project (not done)
      Step 2 – Run the cloud project (not done)
      Step 3 – Run the Windows 8 Metro Data Grid Client Application (pending Step 1 & 2)
    • Once you start your Windows Azure Cloud Project, you are ready to run the Windows 8 Metro Grid client.
      twyq0ark
      If we are lucky, here is what we will see.
      ixhhmuvg

See you soon !


Download for Azure SDK