The goal of my starter kit (and the other kits on the site) is to demonstrate how to call the API from a Windows Store app, how to display the results (in my case using databinding), and also to provide a starting point for folks who might want to “mash up” one of the starter kits with additional data from other APIs.
Reimagining for Windows Phone
Given the UI differences between a PC and a phone, I figured I would just approach the phone version of the app in the same way I did the Windows Store version…find the Visual Studio template that most closely approximates my needs, and modify it as little as needed to achieve the UX and functionality I desired.
I settled on the Windows Phone Databound app template, as shown in the dialog below:
This template provides a simple databound list in the main page, and when any given item is invoked, the app navigates to a details page for that particular item. The app uses a simplified MVVM architecture, and demonstrates the use of sample data as well.
Understanding the Template
The Windows Phone Databound App template is pretty straightforward. It consists of:
- 2 XAML pages, MainPage.xaml and DetailsPage.xaml (both with accompanying “code behind” C# files.
- An App.xaml file, whose main purpose in the context of the app template is to instantiate and initialize the main viewmodel of the app (more on that in a moment)
- A set of design-time sample data, implemented in a XAML file (this file is located in the aptly-named SampleData folder)
- A pair of ViewModel classes, implemented in C#, a MainViewModel, which implements the items collection which will be bound to a list in MainPage.xaml, and an ItemViewModel, which contains the properties that represent an item.
Let’s start with the ViewModels. Their job is to abstract the concerns of fetching and parsing data away from the pages. The ItemViewModel has three properties, LineOne, LineTwo, and LineThree, which are implemented with private members for internal representation (whose names start with an underscore and a lowercase first letter), and public properties (whose names start with an uppercase letter). Each one calls NotifyPropertyChanged if the property value changes, so that any controls bound to the data can update the value.
We could just stuff the values we want for a given meetup into these properties, which would avoid some changes elsewhere in the code, but there are a couple of problems with this…one, it would be potentially confusing to stick with such generic property names, since they don’t tell us anything about what the property represents, and two, we probably actually need more than three properties. We’ll come back to the ItemViewModel momentarily.
The MainViewModel, which as noted above is instantiated and initialized at app launch in App.xaml.cs, is responsible for creating an ObservableCollection of ItemViewModel items, and (via its LoadData function) populating the collection with data. In the template code, the runtime data is populated by simply adding a bunch of instances of the ItemViewModel class initialized with sample data.
To modify the template code for our purposes, we’ll start with the ItemViewModel.cs class. First, we’ll rename both the class and the filename to MeetupViewModel, to be more descriptive of what the model is. One nice feature of C#, if you’ve not used it before, is the support for refactoring, in particular for renaming members. So when we change the class name from ItemViewModel to MeetupViewModel, Visual Studio gives us a hint that there’s some context-specific commands available, which we can expand using the Ctrl+. keystroke combo, which shows the following:
This command will search the project, and find all references to the ItemViewModel class and update them to the new name. If you prefer to review each change, you can use Rename with preview… This is a very convenient way to quickly update all (well, nearly all…if a member is referred to by a string, such as in the call to NotifyPropertyChanged in the the property declarations, rename won’t update it).
Next, we’ll update the properties and replace the generic LineOne, LineTwo, etc. properties with more descriptive properties, including MeetupID, Name, Url, Description, CityState (combines the City and State fields from the Meetup venue property, for easier display), and more. One of the advantages of using a ViewModel is that we can describe how we want the data to look for our app, regardless of whether that’s how the original data actually looks. When we load the data, we can massage it to fit the format we want to use.
That’s all that’s needed for the MeetupViewModel class.
For the MainViewModel (view the full code here), we’ll start in the constructor. In order to support the retrieval of live data, we’ll add a couple of lines of code to create an instance of the System.Net.WebClient class, and handle its DownloadStringCompleted event:
In the LoadData function, we need to remove the code that creates sample data, and replace it with code that constructs the request URL for the Meetup API…in the code below, I’m referring to static members of an AppConstants class, which is used to provide a single location for customizing the most common parameters for the app (which city to search in, what keywords to search for, Meetup API key, etc.). Also, note that to keep things simple, I’ve not added any exception handling or code to deal with network availability (or lack thereof), so for a production app, you’d want to add that code yourself. Here’s the updated LoadData:
Pretty simple…we just construct the URI with the parameters desired, and call DownloadStringAsync with the URI.
Next, we add the handler function for handling the response from the WebClient request, like so (apologies for any extra line breaks in the code…you can view the code on gihub for a more readable version):
In the code above, we start by using Linq to XML (requires a reference to System.Xml.Linq) to parse the XML returned from the request to the Meetup API. We could request JSON as the format as in the HTML/JS version, but Linq to XML is pretty awesome, so XML makes more sense here.
Once parsed into a series of elements, we run a Linq query that looks for all meetups that are in-person (since we’re focused on finding local coffee shops near each meetup, including online/virtual meetups wouldn’t make much sense), and for each of the returned items, creates a new instance of the MeetupViewModel class, mapping the desired values from the elements in the XML data to the properties of the MeetupViewModel class.
Once that’s done, we loop over the results, and add a simple numeric ID to each (this property is used in the app navigation to identify which item was invoked…see DetailsPage.xaml.cs for how its used), and then add the item to the ObservableCollection created at app startup, and increment the ID value.
Assuming no errors, we’ve now got data! But since we’ve changed the names of the properties, the databinding code found in MainPage.xaml, and DetailsPage.xaml will no longer work. Let’s fix that.
In MainPage.xaml, the changes are pretty straightforward…we just need to update the bindings to the new property names. In the template, MainPage.xaml contains an ItemTemplate for the LongListSelector control, which has a DataTemplate containing two TextBlocks inside a StackPanel. These are bound to LineOne and LineTwo respectively, and I want them to show the meetup Name and the composite CityState property, so I just need to update these like so (some attributes omitted for readability):
I also updated the app name and page name to be specific to my app, and added a Meetup-style icon for each row in the list, and the result looks like this:
Note that no changes were needed to the code-behind for MainPage.xaml, as the page relies solely on declarative databinding for the display of the items. Tapping any of the items will navigate to DetailsPage.xaml, but if we do that now, the app won’t work, because we’ve not yet updated that page.
Here, the changes are a little more substantial, but not dramatically so. As with MainPage.xaml, we should change the app and page name, and then also update the placeholder property names to the ones in our MeetupViewModel class. We’ll also add a Map control to the page, as well as a couple of buttons to show us nearby coffee shops and to navigate to the web site for the specific meetup. Here’s what the updated XAML looks like (some attributes omitted for readability):
In the code-behind (DetailsPage.xaml.cs), we first need some using declarations for the namespaces of the various features we’ll add:
All but the last two are related to the map functionality. Microsoft.Phone.Tasks allows us to access two of the tasks (MapsTask and WebBrowserTask) we’ll use to handle our button clicks. And the last one provides easier access to the AppConstants class containing our static variables, which includes the search term used below.
Next, because we’ll be using its value in some of our other code, we need to move the declaration of the index variable to the start of the codebehind class:
The OnNavigatedTo event is unchanged, apart from the modification to where the index variable was declared.
When the map control is loaded, it will fire its Loaded event, which is mapped to the MyMap_Loaded handler:
The code above grabs the latitude and longitude values from the current meetup item, based on the index of the selected item, centers the map on those coordinates, and then adds an Ellipse object to mark the location of the meetup.
The last bit of additional code are the button click event handlers:
In the first, we once again grab the latitude and longitude from the meetup item, and use that to launch a new MapsTask, setting its SearchTerm to the configured searchTerm in our AppConstants class (which defaults to “coffee”).
The second click handler uses the WebBrowserTask to launch a new browser window for the web site for the meetup being viewed.
Here’s what DetailsPage.xaml looks like when we’re done:
Clicking the “Need COFFEE!” (and who doesn’t?) button launches the Maps app and shows coffee shops in the area:
Because we’re leveraging the built-in Maps app on the phone, we can now easily select a desired coffee shop and get walking or driving directions from our current location, without having to write any of that code ourselves.
Last, but not least, clicking the “Meetup Site” button opens a new browser window with the url set to the meetup site being viewed:
One other part of the template code that needed updating is the sample data. That’s not a critical part of the app, so I’ll leave that as an exercise for the reader. Or, if you don’t want to try updating that yourself, you can always take a look at the updated version on our Github repository.
Building for Both
If you’re interested in developing apps to target both Windows 8 and Windows Phone 8, there’s some good documentation available on the Windows Phone Dev Center. There’s also a video series covering the topic on Channel 9.