Working with GridView Control in WinRT App

As I am continuing to learn WinRT and its component, I spent sometime learning new native controls that are available for use in Metro style applications designed for Windows 8.  One of those controls is GridView.  This control can be easily visualized by looking at Windows 8 start screen.  You see groups of tiles, scrollable horizontally.  This is what this control is all about: presenting a list of items in a horizontally scrollable container, which can be further grouped.  In the case of grouping, the control essentially just deals with list of lists.

In the example below I will be using data that comes from WCF SOAP based service.  It exposes a list of contacts, where each contract has just a handful of properties:

    public class Contact
    {
        public int ContactId { get; set; }

        [StringLength(30)]
        public string FirstName { get; set; }

        [StringLength(30)]
        public string LastName { get; set; }

        [StringLength(1000)]
        public string FavoritePicture { get; set; }

    }

I am using Entity Framework code first for the demo, where the service method to get contacts is pretty simple:

 

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class ContactService : IContactService
    {
        static ContactService()
        {
            Database.SetInitializer(new Initializer());
        }

        public Contact[] GetContacts()
        {
            Contact[] returnValue;
            using (var context = new Context())
            {
                returnValue = context.Contacts.OrderBy(c => c.LastName).ThenBy(c => c.FirstName).ToArray();
            }
            return returnValue;
        }

    }

So far is pretty simple.  The cool part of working with WCF is that I can just right click on References note in solution explorer on Windows 8 application and choose Add Service Reference.  And voila, I have a proxy built for me.

Now, being a good citizen I am going to build a view model class to expose my data to my XAML page.  Getting this data is very simple.  I just need a property of type ObservableCollection<Contact> and this will provide the source of items to my GridView control.

 

namespace Win8App
{
    public class ViewModel : INotifyPropertyChanged
    {
#if DEBUG
        private static string Url = "http://localhost:4472/ContactService.svc";
#else
        private static string Url = "http://localhost:4472/ContactService.svc";
#endif
        private ContactService.ContactServiceClient client;

        public ViewModel()
        {
            Initialize();
        }
        public async void Initialize()
        {
            ConfigureCLient();
            Contacts = await client.GetContactsAsync();
        }
        private void ConfigureCLient()
        {
            BasicHttpBinding binding = new BasicHttpBinding();
            binding.ReaderQuotas = XmlDictionaryReaderQuotas.Max;
            client = new ContactService.ContactServiceClient(binding, new EndpointAddress(Url));
        }
        private ObservableCollection<Contact> _contacts;
        public ObservableCollection<Contact> Contacts
        {
            get { return _contacts; }
            set { _contacts = value; OnPropertyChanged("Contacts"); }
        }

You will see that my view model implements INotifyPropertyChanged which essential for supporting data binding.

To configure GridView I only need two things: assign ItemSource which points to my collection property and a template that specifies how each item is displayed:

<GridView  ItemsSource="{Binding Contacts}" ItemTemplate="{StaticResource StandardItemTemplate}"/>

 

The template I added as a resource to the page:

    <Page.Resources>
        <DataTemplate x:Key="StandardItemTemplate">
            <StackPanel Orientation="Vertical" Width="200" Height="250">
                <TextBlock Text="{Binding LastName}" Style="{StaticResource TitleTextStyle}" HorizontalAlignment="Center" Margin="20,10,20,0"/>
                <TextBlock Text="{Binding FirstName}" Style="{StaticResource ItemTextStyle}" HorizontalAlignment="Center" Margin="20,10"/>
                <Image Source="{Binding FavoritePicture}" Width="300" Stretch="UniformToFill"/>
            </StackPanel>
        </DataTemplate>
    </Page.Resources>

The last step is to create an instance of my view model and assign it to DataContext property of the Page.

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            this.DataContext = this.DataContext ?? new ViewModel();
        }
    }

Pretty simple so far, and now I can just run the application and look at the data.

image

Looks very nice, doesn’t it?  Now, there are more features to look at.  Next, selection.  GridView automatically handles selections of items by the user.  By default it comes with selection mode of single item.  You can see how it works below.  The template is altered and a check mark is places into top right corner.  I can switch the selection mode to multiple by altering SelectionMode property.

image

The next very cool feature is grouping support.  You can see the behavior on the Windows 8 start screen.  In my case I am going to group the contacts based on the first letter of the last name, building sort of rolodex user experience.  What I need from grouped data source is a list of list, where each item in the outer list has a key (first letter of the last name) and a list of contacts that match that key.  I am simply going to use Linq for that and a helper class for grouped item:

    public class GroupedData<T> : List<T>
    {
        public object Key { get; set; }


        public new IEnumerator<T> GetEnumerator()
        {
            return (IEnumerator<T>)base.GetEnumerator();
        }

    }

Then I need a property to hold these items:

        private ObservableCollection<GroupedData<Contact>> _groupedContacts;
        public ObservableCollection<GroupedData<Contact>> GroupedContacts
        {
            get { return _groupedContacts; }
            set { _groupedContacts = value; OnPropertyChanged("GroupedContacts"); }
        }

Now, I am going to just create a simple method to convert list of contacts into the list of grouped items:

        private void CreateGroupedData()
        {
            var groupedData = from oneConctact in Contacts
                              group oneConctact by oneConctact.LastName.Substring(0, 1) into lastNameGroup
                              select new
                              {
                                  Name = lastNameGroup.Key,
                                  Items = lastNameGroup
                              };
            ObservableCollection<GroupedData<Contact>> data = new ObservableCollection<GroupedData<Contact>>();
            foreach (var item in groupedData)
            {
                GroupedData<Contact> list = new GroupedData<Contact>();
                list.Key = item.Name;
                foreach (var itemInGroup in item.Items)
                {
                    list.Add(itemInGroup);
                }
                data.Add(list);
            }
            GroupedContacts = data;
        }

Now I need a grouped data source for my list I can use to configure GridView.  I am going to use the same helper class that I used in WPF/Silverlight applications: CollectionViewSource.  I am going to use this control as a static resource on the page as well and bind it to a property of my view model:

    <Page.Resources>
        <CollectionViewSource x:Key="GroupedSource" IsSourceGrouped="True" Source="{Binding GroupedContacts}"/>
    </Page.Resources>

The last step is to configure GridView.  I am going to configure three things.  I am going to assign ItemsSource to my collection view source.  Next I am going to specify exact same item template as I did in non-grouped variant.  And lastly, I am going to build a a template for header information for each group, which will essentially point to the first letter of the last name, contained in the Key property of GroupedItem.

<GridView  ItemsSource="{Binding Source={StaticResource GroupedSource}}" ItemTemplate="{StaticResource StandardItemTemplate}" Grid.Row="1">
    <GridView.GroupStyle>
        <GroupStyle>
            <GroupStyle.HeaderTemplate>
                <DataTemplate>
                    <TextBlock Margin="20,20,0,0" Text="{Binding Key}" Style="{StaticResource PageSubheaderTextStyle}" />
                </DataTemplate>
            </GroupStyle.HeaderTemplate>
        </GroupStyle>
    </GridView.GroupStyle>
</GridView>

Here is how the grouped view looks:

image

So, very quickly we can build quite an awesome representation of a list of items, grouped and sorted and shown in a very Metro looking control.  You can download the entire demo here, which is also illustrating ListView and FlipView controls, as well as other new Windows 8 metro controls, such as AppBar and PopupMenu.

I will blog more about other new control in the forthcoming posts.

Thanks.

9 Comments

  1. Pingback: Windows 8 Developer Links – 2012-07-30Dan Rigby | Dan Rigby

  2. Hi,

    Thanks for the great article! I’m new to writing metro app and using WCF service. I’m doing an app that requires the retrieval of data from a database and call it through a WCF service which binds to the XAML. I’m using a grid app template for this. So, I’m unsure whether to call the service to return xml or json data.

    Does it makes any difference? Which is more preferable?

    In addition, do you mind explaining the following lines to me and what they do? I want to understand it.

    BasicHttpBinding binding = new BasicHttpBinding();
    binding.ReaderQuotas = XmlDictionaryReaderQuotas.Max;
    client = new ContactService.ContactServiceClient(binding, new EndpointAddress(Url));

    Thanks.

  3. I’d use Json, but it is a personal preference. The lines just setup a proxy / client with basic http binding (working over port 80 using XML) with maximum allowed payload size. You do not want to use this size, you want to make it as close as possible to actual payloads you have, for security reasons.

  4. Just estimate how much data you will send at most, and maybe double or triple it to be on the safe side. To do that just run Fiddler and look at the message size or your largest payload.

  5. Hi,

    I’ve got another question. Do we implement WCF REST only when we are using Azure? Or we can use it even when we are not using Azure to access data from database? And since you are using not using Azure in this example, thus you implement WCF SOAP instead. Is that right?

    I hope you can clarify the doubts that I have.

    Thanks.

  6. Hi,

    I am binding group data in Grouped Gridview. If some group header does not have and data, i need to display “No data available” under the group. For example the above example under “A” does not have any data. In this case i want to display header “A” and “No data available” text in item section. Is it possible?

Leave a Reply

Your email address will not be published. Required fields are marked *