Этот пост разбит на 3 упражнения.
| Упражнение 1 | Добавьте новый класс с именем MySampleDataSource. Это заменит SampleDataSource.
|
| Упражнение 2 | Измените App.xaml.cs для вызова кода в MySampleDataSource. Этот код будет извлекать данные JSON через http.
|
| Упражнение 3 | Измените код пользовательского интерфейса (XAML) для правильной привязки к новому источнику данных.
|
Упражнение 1. Добавьте новый класс с именем MySampleDataSource.
Это заменит SampleDataSource. SampleDataSource имеет жесткие значения кода для предоставления данных в пользовательский интерфейс. MySampleDataSource будет выполнять асинхронные вызовы http и анализировать данные JSON для пользовательского интерфейса. Мы также добавим MyStack.cs, чтобы помочь с назначением элементов в группы.
Упражнение 1. Задача 1. Знакомство с MySampleDataSource.cs и MyStack.cs.
Следующим шагом является добавление модуля MySampleDataSource.cs ниже. Это перезапись SampleDataSource.cs (предоставлено VS 2012 RC). Не так много изменений. Я немного изменил некоторые базовые элементы данных. Я также добавил метод для выполнения асинхронного http-запроса на данные JSON.
Следующим шагом является добавление модуля MySampleDataSource.cs ниже.
Это перезапись SampleDataSource.cs (предоставлено VS 2012 RC). Не так много изменений. Я немного изменил некоторые базовые элементы данных. Я также добавил метод для выполнения асинхронного http-запроса на данные JSON.
Я также добавил модуль MyStack.cs . Это используется для группировки элементов в группы. Код ожидает, что данные JSON упорядочены правильно, когда они отправляются сервером (серверная часть Windows Azure).
Должны быть отсортированы по группам , а затем по пунктам .
Мы добавим два исходных файла.
| Файл 1 | MySampleDataSource.cs | Это позволит нашему приложению Metro Grid для Windows 8 отправлять запросы в формате JSON. |
| Файл 2 | MyStack.cs | Это позволит нам группировать предметы в группы. Например, добавление отдельных мотоциклов в такие группы, как спортивные велосипеды и экзотические велосипеды. |
- Вернитесь в Visual Studio 2012 RC.
- Если не видно, просмотрите «Solution Explorer».
- Нажмите на меню «Вид» и выберите «Обозреватель решений».
- Щелкните правой кнопкой мыши на папке «DataModel» и добавить в новый класс .
- Вызовите этот новый класс « MySampleDataSource.cs ».
- Вставьте код из фрагмента ниже. См. «Фрагмент кода 1 — MySampleDataSource.cs ».
- Щелкните правой кнопкой мыши папку « DataModel » и добавьте новый класс.
- Назовите этот новый класс » MyStack.cs .»
- Вставьте код из фрагмента ниже. См. « Фрагмент кода 2 — MyStack.cs ».
- Обозреватель решений должен выглядеть так:
- После того, как вы вставили код для MyStack.cs и MySampleDataSource.cs, вы можете попробовать скомпилировать ваше приложение.
- Обратите внимание, что 3 новых класса повторяют структуру и поведение встроенных типов.
- Вы можете увидеть эти объекты после добавления двух модулей.
- Затем вы перестроите свое решение и переключитесь на «Class View».
- Красный ящик , что мы добавим .
- MySampleDataCommon, MySampleDataGroup, MySampleDataItem
- Объекты в зеленом поле были предоставлены Visual Studio .
- SampleDataCommon, SampleDataGroup, SampleDataItem
- Красный ящик , что мы добавим .
- Мы добавим MySampleSource .cs.
- MySampleSource.cs будет включать следующие классы ключей, которые позволяют нам выполнять асинхронный вызов в хранилище данных JSON с использованием http.
- Мы также добавим MyStack.cs
- Этот класс поможет управлять отдельными предметами и их группами .
Некоторые дополнительные примечания относительно кода
- Мы добавили некоторые заявления об использовании .
- используя System.Net.Http;
- Поддерживает http веб-запросы
- используя Windows.Data.Json;
- Обеспечивает полезные функции синтаксического анализа
- использование System.Threading.Tasks;
- Поддержка потоков
- используя System.Net.Http;
- Я немного изменил название соглашения.
- От SampleData к MySampleData
- Не очень хорошая идея для производственного кода, но хорошая для быстрого взлома.
- От SampleData к MySampleData
- Вы должны легко увидеть параллели между новым MySampleDataSource.cs и старым SampleDataSource.cs.
- Изменения в базовом классе ( MySampleDataCommon )
- Мы анализируем JsonObjects в коде.
- Вы увидите код, подобный currGroup.TryGetValue («_ subTitle», out val).
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();
-
-
- Этот код позволяет извлекать строки из данных JSON
- Это то, как мы заполняем MySampleDataItem и MySampleDataGroup.
-
- Мы реализуем объект Stack, чтобы помочь нам сгруппировать элементы вместе.
- Элементы принадлежат группе, поэтому для отслеживания этой связи используется механизм стека.
Фрагмент кода 1 — MySampleDataSource.cs
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 "http://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("http://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.
}
}
}
Фрагмент кода 2 — MyStack.cs
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];
}
}
}
Упражнение 2. Изменение файла App.config.cs для вызова кода в MySampleDataSource
В MySampleDataSource есть код, который нужно вызвать. Этот код сделает асинхронный http-запрос для получения данных JSON.
- Обратите внимание, что мы добавили две строки кода здесь.
- Мы объявили объект типа MySampleDataSource
- We have called LoadRemoteDataAsync() on that object
Code Snippet 3 — App.xaml.cs
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 http://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
- We will modify GroupDetailPage.xaml
- We’d like to bind to the «Content» property for a group.
Modify the GroupDetailPage.xaml.cs
- Open GroupDetailPage.xaml.cs
- Navigate to the method LoadState()
- Change the following line:
- var group = SampleDataSource.GetGroup((String)navigationParameter);
- Change the following line:
- Navigate to the method ItemView_ItemClick()()
- Change the following line:
- var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
- Change the following line:
- See line 42 and 57 in the code below.
GroupDetailPage.xaml.cs
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 http://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
- Modify GroupedItemsPage.xaml.cs. You will add “My” to the appropriate lines. Take note of the following lines.
- Line 41
- Line 59
- Line 72
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 http://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.
- Open ItemDetailPage.xaml.
- We want to add a binding for Description
- Insert the following 3 lines on line 62, 63, 64, as seen in the code below.
<COMMON:LAYOUTAWAREPAGE mc:Ignorable="d" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:common="using:FastMotorcycle.Common" xmlns:data="using:FastMotorcycle" xmlns:local="using:FastMotorcycle" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" IsTabStop="false" DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}" x:Class="FastMotorcycle.ItemDetailPage" x:Name="pageRoot">
<?XML:NAMESPACE PREFIX = "[default] http://schemas.microsoft.com/winfx/2006/xaml/presentation" NS = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" /><Page.Resources>
<!-- 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
- Open ItemDetailPage.xaml.cs
- Modify the code on lines 50 and 64, similar to what we’ve been doing previously.
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 http://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.
- You will not be able to run this code yet.
- 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
- The key code happens in LoadRemoteDataAsync().
- Let’s reveiw this piece of code.
- It creates an HttpClient object
- This allows us to connect to an http endpoint using http
- The code then performs and asynchronous call to the cloud service
- Starts by navigating to the address:
- http://127.0.0.1:81/FastMotorcycleListService.svc/list/SampleData/1
- Later, when we migrate to Azure, this url will change
- Starts by navigating to the address:
- The next piece of code, loops through all the MySampleDataItem objects.
- We parse the JSON array and they extract all the attributes, one by one.
- 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.











