Part 12: Improving the View Model and Sample Data
Now that we've completed the basic setup chores, we want to focus on the heart of the application: the data model. This requires we translate our low-tech mockups from the previous lesson into a viable data model that best represents the data and hierarchical relationships between the data.
Here's our game plan for this lesson:
- We'll briefly analyze our "requirements" (the mockup is all we have) and will quickly diagram a data model that we'll implement in our app.
- We'll implement the data model in code.
- We'll create some data in an XML file that will be used as DESIGN-TIME data for the purpose of displaying something in our MainPage's XAML Designer.
- Modify the MainPage.xaml's binding expressions to point to instances of classes and properties in our data model.
1. Analyze our mockup and design a data model
After looking over the mockups and thinking about how best to delegate responsibilities to the various classes, I come up with a very basic class model that is my first best guess at how to structure the data I'll need for the app:
The SoundModel class will contain references to each of the five groupings of sounds in the app. Notice that each of these properties are of type SoundGroup.
The SoundGroup class represents what I've been calling a "view" or "type". I've struggled for the correct terms, but in essence it is a grouping, not a view (which only connotes the visual aspect of the sound tiles) or a type (which has special connotations in the .NET world). A SoundGroup has a title (that will be used to display as the title of the PivotItem in the Pivot control on the MainPage.xaml) and a generic collection of SoundData.
The SoundData class represents the sounds themselves. Each instance of the SoundData will be displayed in a tile, and by tapping the tile, we'll play the sound associated with the SoundData as stored in the FilePath property.
Given this clarity, it's time to implement this diagram in code. I'm sure there will be a few additions as we go along, but this is a good start.
2. Create the new data model classes for our app
With a plan and our data model diagram in tow, we'll begin by implementing the new data model.
Right-click ViewModels folder, Add | New Item:
... open the Add New Item dialog:
- Make sure you're in the Visual C# file templates
- Select the Class file template
- Rename to: SoundData.cs
- Click Add button
The SoundData.cs file is created and loaded into the main area:
... we'll add two public properties: Title and FilePath:
Add another new item to the ViewModels folder using the technique described a moment ago. You'll create another Class called: SoundGroup.cs:
In the new SoundGroup.cs, we'll create two public properties and a constructor:
- Create the Title property and Items property. Items will be a generic list of SoundData.
- In the constructor, we initialize Items setting it to a new instance of List<SoundData>.
We'll add one more class file to the ViewModels folder called SoundModel. SoundModel will contain a SoundGroup for each "view" or "categories" of sounds we'll display in our app. It will also implement the logic to determine whether data has been loaded into the new data model, and the vast majority of the code therein will create instances of the SoundData classes for each sound we want to use in our app.
We'll start simple and grow this important class throughout this lesson:
- In lines 11 through 15 we'll create properties representing each SoundGroup (i.e, category) of sounds.
- We'll use the IsDataLoaded property to determine whether or not we need to perform the LoadData() method to create instances of SoundGroup and SoundData.
- We'll load the data into the model using a number of private helper method which we'll implement in just a moment.
3. Modify the App.xaml.cs to use the new data model
Before we perform the bulk of the work in the LoadData() method (and create helper methods to the heavy lifting) I want to replace the OLD data model (from the project template) in the app with our new one we just created by changing the class that the App.xaml.cs uses to back its ViewModel property.
First, I'll comment out references to the OLD model in the App.xaml.cs. I typically comment out things I no longer THINK I need before I actually delete them from the project. This is a habit I've acquired through the years. I've hastily deleted code thinking I'll replace it, only to realize there was some small detail I was missing. However, since I deleted the old working version and can no longer reference it, it becomes more time consuming and painful to determine where I made the mistake.
Here I comment out the implementation of MainViewModel property, both the private backing field as well as the public getter and setter:
Next, I'll implement the new version of the ViewModel using our new data model type SoundModel:
That should be all that's required. References to the SoundModel's IsDataLoaded property and LoadData() method should work just fine.
Now that I've removed the old data model from the app, I'm sure I've broken several other things. At this moment, I'm most concerned about the XAML views. The binding expressions are pointing at properties that no longer exist. Furthermore, at DESIGN-TIME, there's no sample data to display in the XAML designer, and at RUNTIME there's no data either.
4. Create sample / design-time display data
It will take some time to massage all of this back into working form, so I have to do this in small chunks. My first priority is to make some DESIGN-TIME data available so that I can then edit the XAML views and make sure they're displaying data from my new model correctly.
I'll right-click the SampleData folder, select Add | New Item ... from the context menu:
... which opens the Add New Item dialog:
- Make sure you're in the Windows Phone file templates
- Select the Windows Phone Portrait Page file template
- Rename the file to: SampleData.xaml
- Click Add
I'll use this file for sample data, not as a XAML page, therefore I'll need to remove everything about the templated file that makes it uniquely
When the new SampleData.xaml file opens in the main area, I'll remove all of the XAML inside that file:
In the Solution Explorer, I'll delete the code behind file that's associated with my new SampleData.xaml file by right-clicking and selecting Delete from the context menu:
Back in the SampleData.xaml, I hide the visual XAML designer. This file will contain just data, nothing that could be displayed in the designer:
I add quite a bit of code in the SampleData.xaml file:
Notice the XAML Namespace prefix "vm"—it references the CLR namespace SoundBoard.ViewModels—the Namespace for the new data model we just created. We want the XAML in this document to create instances of our SoundModel, SoundGroup, and SoundData classes.
5. Use the new sample data in the MainPage.xaml
Next, I want to modify the MainPage.xaml file to change the design-time data (i.e., the data we're loading into our design-time) that the XAML Designer loads and displays:
Now that we have a data model and real "fake" data, we can see the impact of changes we make in the DataTemplate for our LongListSelector. I'll make changes to the binding expressions in each of the TextBlocks:
- I'll set the ItemSource binding of the LongListSelector to Animals.Items ... Animals is a public property of the SoundModel, which is the parent class that's defined in the SampleData.xaml file, which we set as the DataContext of the entire MainPage.xaml page. Therefore, every child property of the SoundModel in that file is accessible (such as Animals, Cartoons, Taunts, etc.).
- Since the LongListSelector.ItemsSource is set to Animals.Items, and Items is a List<SoundData>, we can reference any property of the SoundData class inside the DataTemplate. So, I set the Text binding to the Title property (of the SoundData class), and ...
- I set the Text binding to the FilePath property (of the SoundData class).
- I also set the Header binding in for the PivotItem to Animals.Title. Again, I can reference Animals because it's parent, the SoundModel, is set as the DataContext for the entire MainPage.xaml.
In the XAML designer, we should now see sample data appear:
Hopefully you can see the correlation between the MainPage.xaml's controls and the sample data for Animals:
We made a change to the FIRST PivotItem, and now we will change the second pivot item to display the SoundGroup Cartoons. Notice it's practically identical to what I wrote for Animals. I simply substituted Animals for Cartoons and it works.
When I put my mouse cursor anywhere inside the XAML definition for the second PivotItem, the XAML designer changes its view to that PivotItem:
To recap, the big takeaway in this lesson is how we replaced the default data model and sample data in the Pivot project template with our own new data model for working with groups of sounds and bound to the new sample data by modifying the MainPage.xaml's page and control declarations. We learned about data contexts and how they're set and accessed from within the page.