How to customize Visual Studio template data (XAML)
[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]
Data already appears in the pages of a Hub, Grid, Split and Pivot project. That's because these templates contain files, code, and XAML tags that make this possible. It's easier to modify these files to show your data than it is to start with a blank page and add it all from scratch.
For a more in-depth example, see Quickstart: Read data from a service.
Items and groups
The Hub, Grid, Split and Pivot projects organize data into groups of items. The SampleDataSource file in the project contains these groups of items. It does that by defining three classes:
- SampleDataGroup
- SampleDataItem
- SampleDataSource
The SampleDataSource creates instances of groups and items and populates them by using data from the SampleData.json file of your project.
If your data closely resembles the structure of the SampleDataItem and SampleDataGroup classes, you can update the code in the GetSampleDataAsync function to retrieve your data. If you decide to add new data properties, you'll have to update the SampleDataItem and SampleDataGroup constructors.
If you're connecting to live data that might not be available at design-time (or might not load fast enough), you can change the DesignData source to provide alternate sample data. That way, you can see data on the XAML Designer's design-surface. Here is an example from GroupDetailPage.xaml in the Grid App template. This also uses the default sample data defined in SampleData.json at design-time.
<CollectionViewSource
x:Name="itemsViewSource"
Source="{Binding Items}"
d:Source="{Binding Groups[0].Items, Source={d:DesignData Source=/DataModel/SampleData.json}}"/>
If you leave the DesignData source untouched, along with any other DesignData references in the XAML file, the design-time values will be pulled from SampleData.json.
How group and item data is bound to the user interface
In the templates, data is typically bound to the UI through the Default DataContext. The DataContext is bound to the DefaultViewModel, which is defined on each page.
In each page, data is loaded in the LoadState event with a call to the SampleDataSource. When the data is retrieved, it is assigned to the DefaultViewModel, as shown in the following example.
var sampleDataGroups = await SampleDataSource.GetGroupsAsync((String)e.NavigationParameter);
this.DefaultViewModel["Groups"] = sampleDataGroups;
You typically use data templates, which are implemented in the XAML pages, to format and display multiple instances of data. Each data template binds to properties that are explicitly defined in xaml, as this example shows:
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
Style="{StaticResource TitleTextBlockStyle}" Height="60" Margin="15,0,15,0"/>
<TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}"
Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
</Grid>
</DataTemplate>>
It is important to understand that the project templates expect certain properties to be present in the data, and these properties are explicitly named in the XAML. In the preceding XAML code for GroupedItemsPage, you can find properties such as title, subtitle, and image path. If your custom app data doesn't use these specific property names, you need to do one of the following:
- Map your data to these property names (typically in SampleDataSource).
- Fix all the XAML and code references to these properties in the template code, so that they match the property names used in your data.
An example of how to incorporate your data into the project
First, update the SampleDataSource file so that groups and items define fields from your data. Then, bind that data to fields in your pages. This section uses a request to generate RSS data.
Update SampleDataSource
On the Visual Studio menu, click File > New Project.
In the left pane of the New Project dialog box, expand the Visual C#, Visual Basic, or Visual C++ node.
In the center pane, click Hub App,Grid App or Split App.
In the Name box, type DataModelExample, and then click OK.
The solution is created and the project files appear in Solution Explorer. For more info about the project files, see C#, VB, and C++ project templates.
Important When you run Visual Studio for the first time, it prompts you to obtain a developer license. For more info, see Get a developer license.
In the DataModel folder, openSampleDataSource.
In SampleDataItem add properties for publication date, author, and link, and update the constructor. Here is the new constructor code for SampleDataItem:
public SampleDataItem(String uniqueId, String title, String subtitle, String imagePath, String description, String content, String pubDate, String author, String uriLink) { this.UniqueId = uniqueId; this.Title = title; this.Subtitle = subtitle; this.Description = description; this.ImagePath = imagePath; this.Content = content; // Added this.PubDate = pubDate; this.Author = author; this.Link = uriLink; }
Add these declarations for the new properties to SampleDataItem:
// Added public string PubDate { get; private set; } public string Author { get; private set; } public string Link { get; private set; }
In SampleDataGroup add a new property for published date and update the constructor. Here is the new constructor code for SampleDataGroup:
public SampleDataGroup(String uniqueId, String title, String subtitle, String imagePath, String description, String pubDate) { this.UniqueId = uniqueId; this.Title = title; this.Subtitle = subtitle; this.Description = description; this.ImagePath = imagePath; // Added this.PubDate = pubDate; this.Items = new ObservableCollection<SampleDataItem>(); }
Add a declaration for the new property to SampleDataGroup:
public string PubDate { get; private set; }
In SampleDataSource, remove the GetSampleDataAsync function and all the code it contains. Replace this function with the following function, also named GetSampleDataAsync:
private async Task GetSampleDataAsync() { if (this._allGroups.Count != 0) return; await GetFeedsAsync(); }
Tip If you can retrieve the data in JSON format, you might be able to re-use more of the sample code in GetSampleDataAsync.
In SampleDataSource, add the following function:
public async Task GetFeedsAsync() { Task<SampleDataGroup> feed1 = GetFeedAsync("https://windowsteamblog.com/windows/b/developers/atom.aspx"); Task<SampleDataGroup> feed2 = GetFeedAsync("https://windowsteamblog.com/windows/b/windowsexperience/atom.aspx"); Task<SampleDataGroup> feed3 = GetFeedAsync("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"); Task<SampleDataGroup> feed4 = GetFeedAsync("https://windowsteamblog.com/windows/b/business/atom.aspx"); Task<SampleDataGroup> feed5 = GetFeedAsync("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"); Task<SampleDataGroup> feed6 = GetFeedAsync("https://windowsteamblog.com/windows/b/windowssecurity/atom.aspx"); Task<SampleDataGroup> feed7 = GetFeedAsync("https://windowsteamblog.com/windows/b/springboard/atom.aspx"); Task<SampleDataGroup> feed8 = GetFeedAsync("https://windowsteamblog.com/windows/b/windowshomeserver/atom.aspx"); // There is no Atom feed for this blog, so we use the RSS feed. Task<SampleDataGroup> feed9 = GetFeedAsync("https://windowsteamblog.com/windows_live/b/windowslive/rss.aspx"); Task<SampleDataGroup> feed10 = GetFeedAsync("https://windowsteamblog.com/windows_live/b/developer/atom.aspx"); Task<SampleDataGroup> feed11 = GetFeedAsync("https://windowsteamblog.com/ie/b/ie/atom.aspx"); Task<SampleDataGroup> feed12 = GetFeedAsync("https://windowsteamblog.com/windows_phone/b/wpdev/atom.aspx"); Task<SampleDataGroup> feed13 = GetFeedAsync("https://windowsteamblog.com/windows_phone/b/wmdev/atom.aspx"); this._allGroups.Add(await feed1); this._allGroups.Add(await feed2); this._allGroups.Add(await feed3); this._allGroups.Add(await feed4); this._allGroups.Add(await feed5); this._allGroups.Add(await feed6); this._allGroups.Add(await feed7); this._allGroups.Add(await feed8); this._allGroups.Add(await feed9); this._allGroups.Add(await feed10); this._allGroups.Add(await feed11); this._allGroups.Add(await feed12); this._allGroups.Add(await feed13); }
Add the following using statement to the top:
using Windows.Web.Syndication;
Add the following method to SampleDataSource:
private async Task<SampleDataGroup> GetFeedAsync(string feedUriString) { // using Windows.Web.Syndication; SyndicationClient client = new SyndicationClient(); Uri feedUri = new Uri(feedUriString); try { SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri); string description = "tbd"; // Use the date of the latest post as the last updated date. string date = feed.Items[0].PublishedDate.DateTime.ToString(); if (feed.Subtitle != null && feed.Subtitle.Text != null) { description = feed.Subtitle.Text; } // This code is executed after RetrieveFeedAsync returns the SyndicationFeed. // Process it and copy the data we want into our FeedData and FeedItem classes. SampleDataGroup feedData = new SampleDataGroup(feed.Id, feed.Title.Text, feed.Subtitle.Text, null, description, date); foreach (SyndicationItem item in feed.Items) { string content = "tbd"; string uriLink = "tbd"; string author = item.Authors[0].Name.ToString(); string datePub = item.PublishedDate.DateTime.ToString(); // Handle the differences between RSS and Atom feeds. if (feed.SourceFormat == SyndicationFormat.Atom10) { content = item.Content.Text; uriLink = new Uri("https://windowsteamblog.com" + item.Id).ToString(); } else if (feed.SourceFormat == SyndicationFormat.Rss20) { content = item.Summary.Text; uriLink = item.Links[0].Uri.ToString(); } SampleDataItem feedItem = new SampleDataItem(item.Id, item.Title.Text, "tbd", null, null, content, datePub, author, uriLink); feedData.Items.Add(feedItem); } return feedData; } catch (Exception) { return null; } }
Click Build > Build solution, or press F7 to make sure the solution builds without error.
These steps assume that you've created a project by using the Grid project template.
An example of how to bind data to your pages
To use the example code in the Grid App project template, open GroupedItemsPage.xaml.
In GroupedItemsPage.xaml, open the shortcut menu for the XAML Designer GridView, and then choose Edit Additional Templates, Edit Generated Items (ItemTemplate), Edit current.
The DataTemplate for the GridView is selected in the XAML Editor.
Replace the binding to the Subtitle property with a binding to the PubDate property.
The TextBox control that contains this binding should resemble this example:
<TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
Open GroupDetailPage.xaml.
Open the shortcut menu for the XAML Designer GridView, and then choose Edit Additional Templates, Edit Generated Items (ItemTemplate), Edit current.
The DataTemplate for the GridView is selected in the XAML Editor.
In the DataTemplate, locate the StackPanel, and then change Subtitle to Author.
The TextBox control that contains this binding should resemble this example:
<TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
In the left pane of the XAML Designer, choose Group subtitle: 1.
The corresponding TextBlock is selected in the XAML Editor.
Replace the binding to the Subtitle property with a binding to the group PubDate property.
The TextBox control that contains this binding should resemble this example:
<TextBlock Text="{Binding PubDate}" Margin="0,0,0,20" Style="{StaticResource SubheaderTextBlockStyle}" MaxHeight="60"/>
Save the project, and then choose the F5 key to debug the app.
The page title appears immediately, but the feed data may take a few seconds to be retrieved. When all the promises are fulfilled, each blog appears in the home page. Choose one of the group headings to show the group details page.