Статьи

Как предоставить облачные данные JSON для Windows 8 Metro Grid Apps

Этот пост разбит на 3 упражнения.

Упражнение 1 Добавьте новый класс с именем MySampleDataSource. Это заменит SampleDataSource.

  1. MySampleDataSource
    • Предоставит данные JSON, полученные с помощью веб-запроса http
    • Очень похоже на SampleDataSource (предоставляется по умолчанию)

      • При выборе «Файл / Новый / Метро Сетка»
Упражнение 2 Измените App.xaml.cs для вызова кода в MySampleDataSource. Этот код будет извлекать данные JSON через http.

  1. App.xaml.cs
    • Содержит код запуска
    • Здесь мы хотим сделать наш веб-запрос JSON
    • Требуется только две строки кода в App.xaml.cs
Упражнение 3 Измените код пользовательского интерфейса (XAML) для правильной привязки к новому источнику данных.

  1. 3 файла xaml

    • GroupDetailPage.xaml
    • GroupedItemsPage.xaml
    • ItemDetailPage.xaml
  2. 3 файла с выделенным кодом

    • GroupDetailPage.xaml.cs
    • GroupedItemsPage.xaml.cs
    • ItemDetailPage.xaml.cs

 

Упражнение 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 Это позволит нам группировать предметы в группы. Например, добавление отдельных мотоциклов в такие группы, как спортивные велосипеды и экзотические велосипеды.

 

  1. Вернитесь в Visual Studio 2012 RC.
  2. Если не видно, просмотрите «Solution Explorer».

    • Нажмите на меню «Вид» и выберите «Обозреватель решений».
  3. Щелкните правой кнопкой мыши на папке «DataModel» и добавить в новый класс .

    hylj4vpk

    • Вызовите этот новый класс « MySampleDataSource.cs ».
  4. Вставьте код из фрагмента ниже. См. «Фрагмент кода 1 — MySampleDataSource.cs ».
  5. Щелкните правой кнопкой мыши папку « DataModel » и добавьте новый класс.

    • Назовите этот новый класс » MyStack.cs
  6. Вставьте код из фрагмента ниже. См. « Фрагмент кода 2 — MyStack.cs ».
  7. Обозреватель решений должен выглядеть так:

    donar2c4

  8. После того, как вы вставили код для MyStack.cs и MySampleDataSource.cs, вы можете попробовать скомпилировать ваше приложение.

    1. Перейдите в меню Build и выберите Build Solution .
      nuiyayct
    2. Надеюсь, вы получите 0 ошибок в « окне вывода ».
      sphrfudz

  • Обратите внимание, что 3 новых класса повторяют структуру и поведение встроенных типов.
  • Вы можете увидеть эти объекты после добавления двух модулей.
  • Затем вы перестроите свое решение и переключитесь на «Class View».

    • Красный ящик , что мы добавим .

      • MySampleDataCommon, MySampleDataGroup, MySampleDataItem
    • Объекты в зеленом поле были предоставлены Visual Studio .

      • SampleDataCommon, SampleDataGroup, SampleDataItem
  • Мы добавим MySampleSource .cs.

    • MySampleSource.cs будет включать следующие классы ключей, которые позволяют нам выполнять асинхронный вызов в хранилище данных JSON с использованием http.
  • Мы также добавим MyStack.cs
    1. Этот класс поможет управлять отдельными предметами и их группами .

xwjsoroz 

Некоторые дополнительные примечания относительно кода

  1. Мы добавили некоторые заявления об использовании .

    • используя System.Net.Http;

      • Поддерживает http веб-запросы
    • используя Windows.Data.Json;

      • Обеспечивает полезные функции синтаксического анализа
    • использование System.Threading.Tasks;

      • Поддержка потоков
  2. Я немного изменил название соглашения.

    • От SampleData к MySampleData
      • Не очень хорошая идея для производственного кода, но хорошая для быстрого взлома.
  3. Вы должны легко увидеть параллели между новым MySampleDataSource.cs и старым SampleDataSource.cs.
    • Изменения в базовом классе ( MySampleDataCommon )
  4. Мы анализируем 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.
  1. Мы реализуем объект Stack, чтобы помочь нам сгруппировать элементы вместе.
  2. Элементы принадлежат группе, поэтому для отслеживания этой связи используется механизм стека.

Фрагмент кода 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.

  1. Откройте файл App.xaml.cs
    g2tp4ltv
  2. Добавьте код, выделенный ниже

 
454u4szh

  • Обратите внимание, что мы добавили две строки кода здесь.

    • Мы объявили объект типа 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

  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

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

  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
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.

  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
<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

  1. Open ItemDetailPage.xaml.cs
  2. 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.

  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
    • 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
  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