Part 14: Binding to Real Data at Runtime

Play Part 14: Binding to Real Data at Runtime
Sign in to queue

Description

Source Code: https://aka.ms/absbeginnerdevwp8
PDF Version: https://aka.ms/absbeginnerdevwp8pdf

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:

  1. 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.
  2. 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:

Generic Episode Image

 

... 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:

 

Generic Episode Image

 

All that's left is to implement the CreateAnimalsGroup() helper method, like so:

 

Generic Episode Image

 

  1. CreateAnimalsGroup will return an instance of SoundGroup.
  2. We'll create an instance of SoundGroup, which we'll build throughout this helper method.
  3. We'll set the Title property—this is what gets displayed as the Header property of the PivotItem.
  4. 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.
  5. 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:

 

Generic Episode Image

 

... 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():

 

Generic Episode Image

 

Here's the listing for the CreateTauntsGroup():

 

Generic Episode Image

 

Here's the listing for the CreateWarningsGroup():

 

Generic Episode Image

 

And now we'll use those helper methods in our LoadData() method to populate the associated Property of each:

 

Generic Episode Image

 

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:

Generic Episode Image

 

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:

 

Generic Episode Image

 

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:

 

Generic Episode Image

 

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:

 

Generic Episode Image

 

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():

 

Generic Episode Image

 

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:

 

Generic Episode Image

 

By adding the explicit call to LoadData() in the App.xaml.cs, we can re-run the app:

 

Generic Episode Image

 

... and our PivotItem Titles re-appear and we can navigate to each new category to see the data we've loaded into each.

 

Recap

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.

Embed

Download

The Discussion

  • User profile image
    QuocTruong

    Awesome. Now I can load data from XAML/XML file at run time Smiley.

  • User profile image
    Richard Freytag

    I decided to forego a lot of the typing and wrote a procedure that built SoundGroups instead:

    public bool LoadData()
    {
    // load data here.
    this.Animals = CreateSoundGroup("Assets/Audio/Animals");
    this.Cartoons = CreateSoundGroup("Assets/Audio/Cartoons");
    this.Taunts = CreateSoundGroup("Assets/Audio/Taunts");
    this.Warnings = CreateSoundGroup("Assets/Audio/Warnings");

    return IsDataLoaded = true;
    }

    private SoundGroup CreateSoundGroup(string pSoundDirectory)
    {
    List<String> soundFiles = new List<String>(Directory.GetFiles(pSoundDirectory));
    SoundGroup soundGroup = new SoundGroup();

    foreach (String soundPath in soundFiles)
    {
    SoundData sound = new SoundData();

    sound.FilePath = soundPath;
    sound.Title = soundPath.Split('\\').Last().Split('.').First();

    soundGroup.Items.Add(sound);
    soundGroup.Title = soundPath.Split('\\').First().Split('/').Last();
    }

    return soundGroup;
    }

  • User profile image
    Clint

    @Richard Freytag: cool, issue becomes mapping name to the sound.  Lots of ways to solve the problem

  • User profile image
    Anh Duong

    Thank you very much for the series.

    In this Part 14, I don't really understand why Bob had to add the line " viewModel.LoadData(); " into the " If statement" near the end to make the Group Titles appear.

    I am almost ready to rebuild this app myself and this is the only part I am having problem with. Please help me.

  • User profile image
    BobTabor

    @Anh Duong: I believe this was a timing issue, if I recall.  The headers and the data bound to the tiles loads at different moments / events in the lifecycle.  I had to explicitly call the Load() to get the titles to load at the right time, otherwise, they would no appear.  Try that, experiment, and see what I mean.  I'm not sure how to explain it beyond that.  Hope that helps! 

  • User profile image
    Anh Duong

    @BobTabor: Thank you for your response. I understand your explanation; however, the original code of the Template doesn't contain that line, and it does make the Group Titles appear. That is what I am struggling with.

  • User profile image
    Anh Duong

    Oh I have just looked at the code again and realized that in the template they hard coded the Group Titles into the MainPage.Xaml file!
    I think I got it, on my way recreating this cute app!

  • User profile image
    Danny

    can someone explain to me what does the following means?
    "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."

    Do i need to load data from xaml file and create objects according to the data in the xaml file? don't i need a corresponding dtd file that holds the structure of the xaml file in order to use it

  • User profile image
    BobTabor

    @Danny: Yes, so if you look at the default template for a Windows Phone 8 project, it pushes you to a certain style of data access.  There's a "design time" approach and a "run time" approach.  Again, by default, the "design time" utilizes a file filled with data SO THAT the designer in Visual Studio can display fake data.  Fake data is helpful so that you can see the aesthetic design of the form you're building.  However, at "run time" the REAL data is provided by a set of hard coded instances of collections of classes.

    My challenge (think: homework ... something to help you flex your programming muscle) was this ... instead of using two different techniques for data access -- one for design time and one for run time -- change the run time approach to utilize the file of data we already are using for design time.

    Does that clarification make more sense?

  • User profile image
    Danny

    @BobTabor: ok thank you for the clarification!
    So as I wrote before do I need to find a technique to load data from xaml or do I need to find a way to bind the data in the load data method?

    a couple more questions, if I may:
    1. what is the difference between Binding and StaticResources? does the second one refers to styles and first one for data\none-hard-coded-data?

    2. what does the Path and Source attributes in the Binding statement mean? I saw the use of the those in Localization Lesson but I still can't understand what is the responsibility of each of them

  • User profile image
    Thierry

    Hi,

    I've just discovered the tutorials! Great so far!! Here is a bit of code I've created instead of creating an entry for each sound! Works a charm! I've haven't tried it on my phone just yet, but from reading the code above, there's no mapping issues, at least not on the emulator!

    I've just spotted very similar code has been posted, but no arm in posting a slightly different variance of it I guess!

    public void LoadData()
    {
    Animals = CreateItemsForGroup("Animals");
    Cartoons = CreateItemsForGroup("Cartoons");
    Taunts = CreateItemsForGroup("Taunts");
    Warnings = CreateItemsForGroup("Warnings");
    IsDataLoaded = true;
    }

    private SoundGroupViewModel CreateItemsForGroup(string soundType)
    {
    SoundGroupViewModel soundGroupViewModel = new SoundGroupViewModel();
    soundGroupViewModel.Title = soundType.ToLower();

    string path = @".\Assets\Audio\" + soundType;

    foreach (string sound in Directory.GetFiles(path))
    {
    string title = Path.GetFileNameWithoutExtension(sound);
    string filePath = sound;
    soundGroupViewModel.Items.Add(new SoundDataModel() { Title = title, FilePath = filePath });
    }
    return soundGroupViewModel;
    }

  • User profile image
    Clint
    @Thierry: the reason why I coded it like I did was I didn't want to map the file name directly to the file asset and didn't want to get too fancy given the audience Smiley
  • User profile image
    Thomas

    Hi All,
    I have a short question.
    Is it possible to change the Text of the Buttons during the runtime?

    After starting my App, the predefined contents of the buttons were shown fine.
    But I cannot re-load the Data.
    Do I have to set the IsDataLoaded to false again?
    Please give me a hint.
    Thank you!

  • User profile image
    Clint

    @Thomas: yup, just need a hook into that button.  From an event handler or a name on the element or searching the DOM.  https://channel9.msdn.com/Series/Windows-Phone-8-Development-for-Absolute-Beginners/Part-4-Introduction-to-XAML we covered doing this via the name

    How are you attempting to reload your data?  You should be able to alter the view model and it should just update automatically.  This is since our ViewModel uses INotifyPropertyChanged and our SoundGroup collections are ObservableCollections. 

    All we do is validate should the data be loaded on the OnNavigatedTo in MainPage.Xaml.cs

  • User profile image
    Clint

    @Thomas: if you want to test stuff out, download our known working sample and try altering that code a bit as well.

  • User profile image
    Graham

    I'm just starting out, learning Windows Phone 8 development and I'm enjoying following this tutorial so far. I'm confused though, as to why there's a need for a 'IsDataLoaded' property. Would it not be better to have a constructor in the 'SoundModel' class, which calls 'LoadData()'? That way the data will always be loaded at object creation time, and there won't be any need to worry about timing issues. Also... (and I'm not being facetious) but don't you think property names should read more like a statement than a question? eg. I personaly think that 'IsDataLoaded' should be 'DataIsLoaded' which make statements like 'if(!DataIsLoaded)' more logical. Please tell me if I'm being petty.

  • User profile image
    Clint

    @Graham: depends how you want to do it.  Think about an application where it loads data from an external source.

    Also what if you want to use this class for something else?  You have data being loaded into it every time even though you don't want it.

    For naming, I was following the naming convention for .Net / c#.  IsEnabled, IsClosed, IsReadOnly, HasLoggedErrors, HasRows, ... makes it easier when reading a full statement as a question and just by the leading "Is", I know it will return a Boolean.  Allows for grouping of members as well in the object viewer dropdown.

  • User profile image
    Graham

    @Clint: Thanks for your response. I can see your point with regards the loading in the constructor, I was being a little blinkered in my approach, though it does work without the need for the 'viewModel.LoadData();' entry. As for the naming convention, you have exposed my naivety with regards the .net/c# naming conventions, which I am reasonably new to. Great series though, I am very grateful for tutorials such as this so please keep up the good work.

  • User profile image
    L Cat

    I have learned much by these tutorials. Thanks. Question about VisualStudio2012:if there would be issued VisualStudio for 64 bit computor in the near future? But WindowPhone does not have a bit issues, isn´t that so?

  • User profile image
    Clint
    @L Cat: I am confused by your question. Vs works on windows in 64 bit.
  • User profile image
    Kian

    Hello. i want to know how to store data and fetch that data for my app.

  • User profile image
    Clint
  • User profile image
    John

    First thanks for this series. This is has been really helpful to get me started on Windows Phone development.

    I have been a programmer for a long time and have way too much C/C++/C# experience and a few MSFT certs.... but this is making me feel stupid :(

    I am having trouble getting binding to work. If I bind to an entire ObservableCollection, I get nothing on the screen. If I bind to the 1st occurrence of the collection [0], I get that bit of data, but as you would expect no other data.

    These are my two class declarations

    using System.Collections.ObjectModel;
    using System.ComponentModel;

    namespace ReportFactors
    {
    public class Report : ObservableCollection<Factors>
    {
    public ObservableCollection<Factors> MyItems;

    public Report()
    {
    MyItems = new ObservableCollection<Factors>();
    }

    //public ObservableCollection<Factors> MyItems { get; private set; }

    public void LoadDefaultData()
    {
    MyItems.Add(new Factors("string1", "string2", "string3", "string4"));
    MyItems.Add(new Factors("string5", "string6", "string7", "string8"));

    }
    }

    public class Factors : INotifyPropertyChanged
    {
    private string _fcDate;
    private string _fcIconURL;
    private string _fcTitle;
    private string _fcText;

    public Factors (string fcdate, string title, string fctext, string iconurl)
    {
    fcDate = fcdate;
    fcText = fctext;
    fcIconURL = iconurl;
    fcTitle = title;
    }

    public string fcDate
    {
    get {return _fcDate;}
    set {if (value != _fcDate){_fcDate = value; NotifyPropertyChanged("fcDate");}}
    }

    public string fcIconURL
    {
    get { return _fcIconURL; }
    set {if (value != _fcIconURL){_fcIconURL = value; NotifyPropertyChanged("fcIconURL");}}
    }

    public string fcTitle
    {
    get { return _fcTitle; }
    set { if (value != _fcTitle) {_fcTitle = value; NotifyPropertyChanged("fcTitle");}}
    }

    public string fcText
    {
    get { return _fcText; }
    set { if (value != _fcText) {_fcText = value; NotifyPropertyChanged("fcText");}}
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string property)
    {
    if (PropertyChanged != null)
    {
    PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
    }
    }
    }

    The Report constructor creates the ObservableCollection of Factors and assigns that to MyItems. The Report class also contains a LoadDefaultData method which Adds some hard coded data.

    I am setting DataContext = App.report.MyItems in the MainPage constructor in MainPage.xaml.cs

    At this point I am absolutely certain that I have made a bonehead mistake but I have been looking at this for so long that I can't see it.

    Any help would be helpful and welcome. Thanks for your time.

    John

  • User profile image
    Clint

    @John: Hey John, we can only really support questions related to the series. I'd ask this question on the forums over at https://dev.windowsphone.com or www.stackoverflow.com

    Sorry

  • User profile image
    john

    No worries. Thanks for the redirect

  • User profile image
    shubham kumar

    I have watched till Part 15 tutorial.The problem is in SDK the MainPage.xaml shows all three pivot items(I've just implemented 3) animals,cartoons and custom sounds but while running it on emulator it shows only one pivot item -'animals'.I've closely watched videos and checked the code.Please help.What could be wrong ?

  • User profile image
    Clint

    @shubham kumar: The first thing I would try to do is right click on the solution and use "clean solution" then try to redeploy.  If that doesn't fix it create a bug and upload solution minus the Bin, Obj, and Asset folder to https://absolutebeginner.codeplex.com/WorkItem/Create and I will take a look

  • User profile image
    ZhiLi

    I`ve tried many times to load data from SampleData.xml at RUN - Time(change the run time approach to utilize the file of data we already are using for design time.) as a challenge for me,but I failed to do that . I`m so confused .

    Could you give me some clues to complete that?

  • User profile image
    laiwayne

    I am trying to load the xaml file in C#, I am getting this Error,
    'System.IO.DirectoryNotFoundException'
    It seems like it's not finding the SampleData.xaml,
    The error says that path points to C:\Data\Programs\{8DDBDFC0-E495-4D59-ABD0-52877691D4FE}\Install\SampleData\RunData.xaml
    and I can't find C:\Data actually. Am I missing anything here?

    private void LoadXaml() {
    string path = @".\SampleData\SampleData.xaml";
    StreamReader reader = new StreamReader(path);
    }

  • User profile image
    laiwayne

    Figured that I have to make the Build Action property of SampleData.xaml as content, and also need to add "assembly=SoundBoard" on the top of xaml.
    xmlns:vm="clr-namespace:SoundBoard.ViewModels;assembly=SoundBoard

    is there a reason that I need to add that? since this xaml is included in the assembly already.

  • User profile image
    Clint

    @ZhiLi: that is outside the scope of the lesson here.  If memory serves, you can't alter a file like that, you'll need to save it to isolated storage

  • User profile image
    Joe

    I have a question about the challenge. Is the goal to use the same file at design time and run time?

  • User profile image
    BobTabor

    @Joe: Yes, instead of creating C# objects at runtime, use same XAML (XML) file at RUNTIME that we're using for design time.

  • User profile image
    nighilanth

    @BobTabor:Hey Bob,

    I finished your series and absolutely love it. I started an app similar to PetSounds, I retraced all steps until lesson 14 but for some reason I can't get data showing at runtime. I checked all ModelView methods and they are working, Load data working also but at some point, when I bind Datacontext to model view, DataContext doesn't get any info. I suspect that it has something to do with async methods in my code because I used HTML string to pull data from website. I trying to resolve it for 2 days and no luck. If you have 10 minutes of your time and look at my project which I uploaded to https://cineman.codeplex.com/ it will be much appreciated.

  • User profile image
    BobTabor

    @nighilanth: I'm really sorry, I'm simply too busy to commit to that.  You may want to ask for help on MSDN's forums ... maybe someone there can spot the issue: 

    https://social.msdn.microsoft.com/Forums/en-US/home?forum=wpdevelop&filter=alltypes&sort=lastpostdesc

  • User profile image
    nighilanth

    @BobTabor: That's alright:) I will probably resolve this by myself :). Thanks anyway! 

  • User profile image
    nighilanth

    Nailed it:) 

    needed to await LoadData before assigning DataContext.

  • User profile image
    BobTabor

    @nighilanth: Nice, great job figuring it out!!!  [H]

  • User profile image
    JosepW

    Very nice series! Thank you!
    In case it may help, I created this helper method to avoid too much typing:
    private static List<SoundData> GetSoundData(string path)
    {
    try
    {
    return Directory.GetFiles("Assets/Audio/" + path + "/").
    Where(f => f.EndsWith(".wav")).
    Select(p => new SoundData
    {
    Title = Path.GetFileNameWithoutExtension(p),
    FilePath = p
    }).ToList();
    }
    catch (DirectoryNotFoundException)
    {
    return new List<SoundData>();
    }
    }

    It can be used this way (I wanted to keep Title decoupled from the subpath name):
    Animals = new SoundGroup
    {
    Title = "animals",
    Items = GetSoundData("Animals")
    };

  • User profile image
    Joachim David

    I finally figured it out and got the XAML file to load into my app at runtime.
    The hardest part was figuring out why the xaml file just couldn't be loaded, the remedy is setting the build action of the SampleData.xaml file to content, what exactly does this do?

  • User profile image
    BobTabor

    @Joachim David: See here: https://msdn.microsoft.com/en-us/library/0c6xyb66(v=vs.100).aspx

    We don't need that XAML to be compiled into a .NET assembly.  Remember back to the beginning of the series ... XAML can be used to create instances of types.  HOWEVER in this case, we're using it to represent just data that we'll load into our app.  We could have used other formats (JSON, comma delimited, plain ol' XML).  This just demo'd another way.  I think I instructed you to set the type of Content in another video.  Maybe you missed it?

  • User profile image
    Sam

    Bob do you have any idea how to save camera capture image in sqlite database and how to show in the next page and where the camera image path is save

  • User profile image
    Sherif

    Thanks for this amazing series ...

    i have been trying to solve this error for a while...

    when i build solution it says that Build succeeded ,But when running the App on Emulator it Stops and gives me Unhandled exception and here is the details of it ..

    >>>
    System.NullReferenceException was unhandled by user code
    HResult=-2147467261
    Message = Object reference not set to an instance of an object.
    Source=Recorder
    StackTrace:
    at Recorder.ViewModels.SoundModel.CreateAnimalsGroup()
    at Recorder.ViewModels.SoundModel.LoadData()
    at Recorder.App.get_ViewModel()
    at Recorder.MainPage..ctor()
    InnerException:
    <<<<<<<<<<

    I googled for a while but without any results ... !!

    can anyone Help me to Solve this issue ?!

  • User profile image
    BobTabor

    @Sherif: That usually means you're attempting to use an instance of a class before you created the new instance.  I recommend putting a break point in the constructor or the methods that are used first in that class and walking through until you hit the error looking for where an instance of that class was new'd up.  Good luck.

  • User profile image
    Bivo

    Hi,

    There is something that I'm missing and can't figure out where you mention it.
    I the prev. lesson, when you introduced the ViewModel concept you showed how to
    you a Observable collection for the data binding of a list.

    In this lesson you switched from the static data binding (of the XAML data) to a run time approach. But where do you specify to bind the items source of every LongListSelector to one of the "Groups" you define?

  • User profile image
    Fagun Rain

    The navigation bar is not coming. I have added viewModel.LoadData() in the condition. But still now not getting the navigation bar. And I am little bit confuse about you debugging.

  • User profile image
    ChuckJ

    I like how this app was done but is there any possible way to implement a saveringtonetask in the same manner, where it will automatically create the tile/button?
    Or if not, how about the same thing you have going on with the sound tiles, but implement a HOLD gesture to save as ringtone on the same button that plays the sound?

  • User profile image
    ChuckJ

    Forget my above question. I added a Context Menu to the DataTemplate that would give the option to Save as Ringtone if you press and hold on the Tile. It works....BUT only when I specify an audio file in the mainpage.cs. How can I write the code below in a way that it will use the same source as the Tile you press, just like the playing sound does? Is there anything specific I can put in the source that will do this?

    private void Save_Click(object sender, System.Windows.RoutedEventArgs e)
    {
    _CustomRingtone.Source = new Uri(WHAT GOES HERE);
    _CustomRingtone.DisplayName = "Ring";
    _CustomRingtone.Show();

  • User profile image
    JeffreyFG

    When I run the App I get an error saying inconsistent Accessibility. Its for the ViewModel property. I have directly copied the code from the source code so I don't think is has to do with that file. What I am I missing.

Add Your 2 Cents