Этот пост разбит на 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.