Over a million developers have joined DZone.

Developing the ImagineCup WorldWide Finals Tracker app fro WP7 @ ImagineCup - Part 1

·

ImagineCup Worldwide Finals is a huge event where everything is going on so fast, that it is ultimately necessary to carry a schedule around just to make sure that one won't miss something important. The ImagineCup Worldwide Finals Tracker is a Windows Phone application that is built to make it easier to navigate through all the events going on in the context of ImagineCup in New York.

One of the main goals of this application was to make its core reusable for the next ImagineCup competitions. Therefore, code flexibility and UI generalization were a priority. I decided to implement simple Metro tiles as the main starting point - a way to keep the user in the WP7 "comfort zone".

The UI structure was built around the idea that the same content skeleton will be used year by year, the same being applied to internal content structure. In XAML, I decided to simply use a bunch of Grid controls inside a StackPanel:

<StackPanel Width="480" Grid.Row="2">
            <ScrollViewer Height="570" VerticalScrollBarVisibility="Visible">
               <StackPanel>
                   <StackPanel Height="200" Margin="0,0,0,10" Orientation="Horizontal">
                        <Grid Margin="35,0,10,0" Height="200" Width="200" Background="{StaticResource PhoneAccentBrush}">
                            <Image MouseLeftButtonUp="Image_MouseLeftButtonUp" Source="Images/metro_world.png" Height="110" Width="110" Margin="0,0,0,50"></Image>
                            <TextBlock TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeExtraLarge}" FontFamily="{StaticResource PhoneFontFamilyLight}" Text="teams" Foreground="White" Height="Auto" Margin="10,130,10,0"></TextBlock>
                        </Grid>

                        <Grid Height="200" Width="200" Background="{StaticResource PhoneAccentBrush}">
                            <Image Source="Images/metro_coverage.png" Height="110" Width="110" Margin="0,0,0,50"></Image>
                            <TextBlock TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeExtraLarge}" FontFamily="{StaticResource PhoneFontFamilyLight}" Text="coverage" Foreground="White" Height="Auto" Margin="10,130,10,0"></TextBlock>
                        </Grid>
                    </StackPanel>

                    <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
                        <Grid Margin="35,0,10,0" Height="200" Width="200" Background="{StaticResource PhoneAccentBrush}">
                            <Image Source="Images/metro_schedule.png" Height="110" Width="110" Margin="0,0,0,50"></Image>
                            <TextBlock TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeExtraLarge}" FontFamily="{StaticResource PhoneFontFamilyLight}" Text="schedule" Foreground="White" Height="Auto" Margin="10,130,10,0"></TextBlock>
                        </Grid>

                        <Grid Height="200" Width="200" Background="{StaticResource PhoneAccentBrush}">
                            <Image Source="Images/metro_alerts.png" Height="110" Width="110" Margin="0,0,0,50"></Image>
                            <TextBlock TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeExtraLarge}" FontFamily="{StaticResource PhoneFontFamilyLight}" Text="alerts" Foreground="White" Height="Auto" Margin="10,130,10,0"></TextBlock>
                        </Grid>
                    </StackPanel>


                    <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
                        <Grid Margin="35,0,10,0" Height="200" Width="200" Background="{StaticResource PhoneAccentBrush}">
                            <Image Source="Images/metro_nyc_info.png" Height="110" Width="110" Margin="0,0,0,50"></Image>
                            <TextBlock TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeExtraLarge}" FontFamily="{StaticResource PhoneFontFamilyLight}" Text="nyc info" Foreground="White" Height="Auto" Margin="10,130,10,0"></TextBlock>
                        </Grid>

                        <Grid Height="200" Width="200" Background="{StaticResource PhoneAccentBrush}">
                            <Image Source="Images/metro_about.png" Height="110" Width="110" Margin="0,0,0,50"></Image>
                            <TextBlock TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeExtraLarge}" FontFamily="{StaticResource PhoneFontFamilyLight}" Text="about" Foreground="White" Height="Auto" Margin="10,130,10,0"></TextBlock>
                        </Grid>
                    </StackPanel>
                </StackPanel>
            </ScrollViewer>
        </StackPanel>

Notice that Grid controls are grouped in two and inserted in a StackPanel that represents a single row. This can generally be simplified by using a single style and a template that defines the structure, but given the limited number of elements, it is not really necessary. That's it for the main page.

Key Component 1 - Teams

It is time to see how we can get the teams that are participating in ImagineCup. Each year at the finals, we get a fixed number of teams with data related to them. Basic information includes:

  • Team Name
  • Team Location
  • Team Project
  • Project Description
  • Team Picture¬†

This information can be obtained in the application in two ways - either build a custom WCF service that will return the serialized data from a central storage point (like a database) or stored locally. To simplify the process, Julio Farfan created a simple XML file and included it in the project locally. We got this:

<teams>
<!--Teams from julio-->
<team>
   <location>Algeria</location>
   <name>Epsilon</name>
   <category>Software Design</category>
   <description>SurgeReal helps students and surgeons practice basic surgeries by providing a 3-D organ image and step-by-step learning. With a solution much cheaper than its competitors and other simulators, SurgeReal answers a very critical issue faced by surgeons living in difficult areas around the world.</description>
    <image>http://www.imaginecup.com/upload/5690632162_cc35b11b2f_b1806542277.jpg</image>
</team>

<team>
   <location>Armenia</location>
   <name>X-Tech</name>
   <category>Software Design</category>
   <description>Teach Me Now is an interactive process on an educational, group-based exchange platform. The system implies conditions that will apply to everyone interested in delivering trainings on an issue and those interested in acquiring new skills and knowledge.</description>
    <image>http://www.imaginecup.com/upload/DSC_3032_5inch2019136558.jpg</image>
</team>
</teams>

Of course this is just a short snippet, because the number of teams goes way beyond 100, but you can see that the structure of the file is as simple as it can get. The file is included as Content in the project, so I can easily invoke it from inside the application itself.

To parse the data out, I decided to create two models - TeamSet and Team. TeamSet represents the entire XML entity:

namespace icww.Models
{
    [XmlRoot("teams")]
    public class TeamSet
    {
        [XmlElement("team")]
        public List<Team> Teams { get; set; }
    }
}

The serialization attributes will help me automate the reading process instead of going node-by-node to read the content. Team represents a single team entity:

namespace icww.Models
{
    public class Team
    {
        [XmlIgnore()]
        public BitmapImage Logo { get; set; }
        [XmlElement("image")]
        public string LogoUrl { get; set; }
        [XmlElement("name")]
        public string Name { get; set; }
        [XmlElement("location")]
        public string Location { get; set; }
        [XmlIgnore()]
        public List<string> MemberSchools { get; set; }
        [XmlIgnore()]
        public List<string> Members { get; set; }
        [XmlElement("category")]
        public string Category { get; set; }
        [XmlElement("description")]
        public string Description { get; set; }
    }
}

There is a reason why some properties are marked with XmlIgnore. Logo is a BitmapImage that cannot be directly deserialized from an XML file. Ultimately, we could store Base64 content, but that would be costly on the amount of memory taken by the application. So the logo is updated dynamically based on the logo URL (through the LogoUrl property).

Members and MemberSchools are not defined in the XML, so it was decided to keep it here for future reference and ultimately include this data, but as long as testing goes - we can keep it simple.

Deserialization goes in a helper static class:

public static class DataInitializer
    {
        public static void GetTeams()
        {
            XDocument doc = XDocument.Load("teams.xml");
            XmlReader reader = doc.CreateReader();

            XmlSerializer serializer = new XmlSerializer(typeof(TeamSet));
            TeamSet set = (TeamSet)serializer.Deserialize(reader);

            ModelLocator.MainModel.Teams = new System.Collections.ObjectModel.ObservableCollection<Team>();
            foreach (Team t in set.Teams)
            {
                if (!string.IsNullOrEmpty(t.LogoUrl))
                {
                    t.Logo = new System.Windows.Media.Imaging.BitmapImage(new Uri(t.LogoUrl, UriKind.Absolute));
                }

                ModelLocator.MainModel.Teams.Add(t);
            }
        }

ModelLocator here is a model locator (literally) implemented through the MVVM Light framework. MainModel is bound to the team page and Teams is an ObservableCollection<Team> that is used for it's basic notification mechanism (on update).

Once the deserialization is complete, the team list on the Teams.xaml page is updated automatically - specifically the ListBox that will be used to display team information.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}