Part 14: Binding to Real Data at Runtime
So, how far along are we with our SoundBoard app?
Well, we have a new data model in place, and in the previous lesson we added sample data that we used at DESIGN time to help us properly layout the app's user interface, particularly the DataTemplate that is bound to instances of the SoundData class.
Now, IN THIS LESSON, we want to turn our attention to binding to REAL data AT RUN TIME.
Truth be told, we could use this SAME XAML file for the "live data" at run time in our app. If you wanted to implement it that way, you certainly could and you've already got a head start to taking that approach ... just build out that XAML file with more instances of SoundGroup and SoundData, then load that file at RUNTIME in the LoadData() method of the SoundModel class.
In fact, that might be a great way to really challenge yourself ... after you finish with this series of lessons, you could go back and re-create this app but stop at this point and take a different data access approach. You only learn when you struggle, and an exercise like this will force you to struggle with how to load XAML data into our data model at runtime.
But I digress ...
Our game plan for this lesson:
- In the SoundModel.cs file, we'll create a series of helper methods, each helper method designed to create instances of the SoundData class which will be added to the SoundGroup's Items collection. So we'll create a helper method called CreateAnimalsGroup() and CreateCartoonsGroup() and so on, each one of these helper methods will create instances of the SoundData class and add them to the proper SoundGroup's Items collection.
- After we have all of our helper methods complete, we'll modify the LoadData() method and call each of those helper methods. So that, when we call LoadData(), real data will be available at runtime.
1. Adding real run-time data to our app
As you can see, right now were we to run the app:
... we're not loading any data at runtime. What we want to accomplish is to set each of the public properties of our SoundModel class, like Animals for example, to an instance of the SoundGroup object loaded with data. So, here's an example of the interaction I want to enable in my LoadData() method:
All that's left is to implement the CreateAnimalsGroup() helper method, like so:
- CreateAnimalsGroup will return an instance of SoundGroup.
- We'll create an instance of SoundGroup, which we'll build throughout this helper method.
- We'll set the Title property—this is what gets displayed as the Header property of the PivotItem.
- Instead of typing out the full path to the audio files, we'll save these in a variable and append it as we're initializing the FilePath property for each new instance of SoundData.
- Here we add a new instance of SoundData to the Items property (a List<SoundData>) of the SoundGroup and use object initializer syntax to populate the Title and FilePath properties of each.
When we run the app:
... we can now see all of the actual data in the Animals PivotItem.
But wait ... where is the Animals PivotItem Title? We'll have to fix that in a moment. Right now, let's finish adding the rest of the Create___Group() helper methods.
Here's the listing for the CreateCartoonsGroup():
Here's the listing for the CreateTauntsGroup():
Here's the listing for the CreateWarningsGroup():
And now we'll use those helper methods in our LoadData() method to populate the associated Property of each:
2. Fixing a data binding problem with the PivotItem Header
If I attempt to run the app, I can't look at the other PivotItems to see the data because we're missing the PivotItem Header:
My first reaction is that it's a binding issue—that the data is not being loaded correctly. I start by looking at the OnNavigatedTo() event handler. Clearly, LoadData() is being called here:
Next, I look at the App.xaml.cs file. In the constructor, if the viewModel is null, it will create a new instance of the SoundModel:
I suspect that this is a timing issue. The Pivot must be requesting the App.ViewModel when it's empty, then the DataTemplate is requesting the App.ViewModel after it has been filled.
After staring at this for a few moments, I set breakpoints on the MainPage.xaml.cs:
Debugging the app reveals that the DataContext is being set prior to calling the LoadData(). This means the PivotItem titles are binding right away, however the PivotItems ItemTemplates / DataTemplates are only binding AFTER we call LoadData():
This is how we see part of the data, but not the other part. I determine that the remedy is simple—we'll call LoadData() right after we create a new instance of the SoundModel:
By adding the explicit call to LoadData() in the App.xaml.cs, we can re-run the app:
... and our PivotItem Titles re-appear and we can navigate to each new category to see the data we've loaded into each.
To recap, the big take away in this lesson is how we implemented the real data. While there are definitely different approaches we could have taken, we chose to create helper methods containing hard coded instances of our SoundData and SoundGroup classes in C#. We also saw how to reason our way through an odd timing issue with data binding ... debugging and understanding the order of events is a valuable skill.
Don't forget my challenge at the outset of this lesson ... I hereby challenge you to re-create this app using a different data access technique, such as expanding the SampleData.xaml file with real data, then loading that data at RUN TIME. Can you figure that out? I'll bet if you spend a day working on it, you'll have it working without my help. You might learn more from that challenge than the rest of this series because you only truly learn when you challenge yourself.