Features and Contracts in Windows Store Apps

Download

Right click “Save as…”

 

While the goal of these quickstarts is to discuss the issues and solutions involved in porting our Windows Phone 7 app to a Windows Store app, there are some features and requirements in Windows 8 we wanted to address: snapping and appcontracts.

Windows 8 Contracts provide OS level functionality and can significantly extend the power of your app when implemented properly. In this video, we will look at two contracts we implemented, the Search Contract and the Share Contract. But first, let us look at how snapping is handled in a Windows Store app.

 

Handling Window Snapping

As we discussed in the last Quickstart, snapping can have a significant impact on how you choose to present your app interface. In a typical template project page, we will find a GridView and ListView element both positioned in Grid.Row="1" under the navigation and page header row of our root grid.

clip_image001

It is the VisualStateManager that controls the visibility of these elements. Since PageHub.xaml inherits from  Common\LayoutAwarePage.cs, we already have the orientation and positioning handlers taken care of. When the app is "snapped" to the screen and has its visual footprint significantly reduced, the VisualStateManager will toggle the visibility of the main GridView to Collapsed while making the thinner ListView based interface visible as shown below:

clip_image002

One other thing to note in this discussion is that while the ListView and GridView are both showing the same data, they would most likely need to reference different data templates and styles.

In the Khan Academy app, the ListView control that handles the snapped layout shares the same CollectionViewSource as the larger GridView control, but points to its own ItemTemplateSelector so that it will pull in the snapped styles accordingly.

clip_image003

 

The Search Contract

The Search Contract allows your app to register with the operating system as being searchable from the Windows 8 Search charm.

 

image

 

To set this up, we need to add some code to the LaunchApp() method in App.xaml.cs as shown below:

App.xaml.cs

public async void LaunchApp(
    ApplicationExecutionState previousExecutionState)
{
    DataSource = new KhanDataSource();
    await DataSource.LoadAllData();
 
    SearchPane searchPane = SearchPane.GetForCurrentView();
    searchPane.PlaceholderText = “Search Khan Academy”;
    searchPane.QuerySubmitted +=
        (sender, queryArgs) =>
        {
            KhanAcademy.SearchResultsPage.Activate(
                queryArgs.QueryText, previousExecutionState);
        };
        […]

 

First, we get the SearchPane object for the current app. Once we have a handle to the instance of the pane, we can set some properties to make the search experience a bit more tailored to our app. Namely, we set the PlaceholderText property to the string “Search Khan Academy”. Finally, we hook the QuerySubmitted event off the SearchPane object, which will be called once the user presses the Enter key or clicks/taps the magnifying glass icon to the right of the search box. In the event handler, the SearchResultsPage is activated and displayed with the user’s query text passed to the page.

The Search pane can also handle real-time suggestions based on the query the user is typing. To do this, we just need to handle the SuggestionsRequested and ResultSuggestionChosen events as shown:

C#

            searchPane.SuggestionsRequested += 
                (sender, suggestionArgs) =>
                {
                    var videos = App.DataSource.TopicGroups.SelectMany(g =>
                        g.Playlists.SelectMany(p => p.Videos.Where(v =>
                            Regex.IsMatch(v.Name ?? "", suggestionArgs.QueryText, RegexOptions.IgnoreCase) ||
                            Regex.IsMatch(v.Description ?? "", suggestionArgs.QueryText, RegexOptions.IgnoreCase))))
                            .Distinct(VideoItem.CreateComparer())
                            .Take(3);
                    foreach(VideoItem vi in videos)
                        suggestionArgs.Request.SearchSuggestionCollection.AppendQuerySuggestion(vi.Title);
                    var recommended = App.DataSource.TopicGroups.SelectMany(g =>
                        g.Playlists.SelectMany(p => p.Videos.Where(v =>
                            Regex.IsMatch(v.Name ?? "", suggestionArgs.QueryText, RegexOptions.IgnoreCase)))).FirstOrDefault();
                    if(recommended != null)
                    {
                        suggestionArgs.Request.SearchSuggestionCollection.AppendSearchSeparator("Recommended");
                        IRandomAccessStreamReference imgStream = RandomAccessStreamReference.CreateFromUri(recommended.ImagePath);
                        suggestionArgs.Request.SearchSuggestionCollection.AppendResultSuggestion(recommended.Title, recommended.Description, recommended.VideoPath.ToString(), imgStream, recommended.Title);
                    }
                };
            searchPane.ResultSuggestionChosen +=
                (sender, resultArgs) =>
                {
                    var recommended = App.DataSource.TopicGroups.SelectMany(g =>
                        g.Playlists.SelectMany(p => p.Videos.Where(v =>
                            Regex.IsMatch(v.VideoPath.ToString() ?? "", resultArgs.Tag, RegexOptions.IgnoreCase)))).FirstOrDefault();
                    Frame f = Window.Current.Content as Frame;
                    f.Navigate(typeof(VideoPage), JsonSerializer.Serialize(recommended));
                };

 

In the SuggestionRequested event handler, we use a LINQ query to find the first 3 results that match what the user is typing and add them to the suggestions list using the AppendQuerySuggestion method off the SearchSuggestionCollection object. In addition, we also use LINQ to find the closest match to what the user is typing to display and display that after a line separator.

The line gets added with the AppendSearchSeparator method, and the single result gets added with the AppendResultSuggestion method. The specific result suggestion can include an icon, and we get the stream for that icon using the CreateFromUri method off the RandomAccessStreamReference object, passing in the path to the image we want to use as the icon. This returns the IRandomAccessStreamReference required by the AppendResultSuggestion method.

In the ResultSuggestionChosen method, we determine which suggestion the user selected and then navigate to the VideoPage to show the video to the user.

 

The Share and Devices Contracts

The Share Contract allows the user to share a piece of data from the current app to another app which knows how to receive the data. For our app, we wanted to allow the user to share a link to the video they are watching to other apps. This works great with the Windows 8 mail app.

 image

 

To accomplish this, we override the DataRequested method on the VideoPage class as shown:

VideoPage.xaml.cs

public sealed partial class VideoPage
    : KhanAcademy.Common.LayoutAwarePage
{
    private PlayToManager _playToManager = null;
    private CoreDispatcher _dispatcher = null;
    private DisplayRequest _displayRequest = null;
 
    public VideoPage()
    {
        this.InitializeComponent();
    }
    protected override void DataRequested(
        DataTransferManager sender,
        DataRequestedEventArgs args)
    {
        VideoItem vi = (this.DataContext as VideoItem);
        args.Request.Data.Properties.Title = vi.Name;
        args.Request.Data.Properties.Description = vi.Description;
        args.Request.Data.SetUri(vi.KhanPath);
    }

In this method, we get the VideoItem that is currently being played, and use its properties to fill out the Data object property of the Request object. The SetUri method is used to pass the actual Uri to the Khan Academy website.

Finally, we wanted to enable PlayTo functionality so the user can quickly and easily send the video output to a TV or other media device on the network. The user can choose to do this while watching the video by selecting the Devices charm. Any PlayTo-enabled devices on the network will show up in this device list.

In code, we handle this scenario in the OnNavigatedTo and OnNavigatedFrom overridden methods.

VideoPage.xaml.cs

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    _dispatcher = Window.Current.CoreWindow.Dispatcher;
    _playToManager = PlayToManager.GetForCurrentView();
    _playToManager.SourceRequested += playToManager_SourceRequested;
    if(_displayRequest == null)
        _displayRequest = new DisplayRequest();
    _displayRequest.RequestActive();
    base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    _playToManager.SourceRequested -= playToManager_SourceRequested;
    if(_displayRequest != null)
        _displayRequest.RequestRelease();
    base.OnNavigatedFrom(e);
}
void playToManager_SourceRequested(PlayToManager sender, PlayToSourceRequestedEventArgs args)
{
    var deferral = args.SourceRequest.GetDeferral();
    var handler = _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        args.SourceRequest.SetSource(videoElement.PlayToSource);
        deferral.Complete();
    });
}

In the OnNavigatedTo method, we get an instance of the PlayToManager and hook the SourceRequested event. We also grab an instance of the current Dispatcher. In the SourceRequested event handler, we request a deferral. This tells the PlayTo manager to wait until we give it the PlayTo source so the request doesn’t time out. We then, using the dispatcher, set the source of the PlayTo request to the video player’s PlayToSource property, and then complete the deferral. At this point, the PlayTo request is sent and the media player talks to the OS to make the magic happen. Note that in the OnNavigatedFrom method we unhook the SourceRequested event handler since it is no longer needed once the user navigates away from this page.

So at this point, we have come up with a migration strategy for porting our Windows Phone 8 to Windows 8, handled changes to how we handle asynchronous data requests and have a well styled, data bound interface that leverages the newest features of Windows 8. From here, the next step would be submitting to the store for distribution.

Hopefully, this set of quickstarts has covered some key areas of interest in getting your app up and running on Windows 8.

I would like to extend a tremendous amount of gratitude for Joel Martinez and all his tireless work as well as opening up the Viewer for Khan Academy app to serve as a Coding4Fun Community project here on Channel 9.

The source code for the Windows 8 Khan Academy app is available for download:

 

If you have any questions, comments, or feedback feel free to join in the discussion.

Twitter: @rickbarraza, @joelmartinez

Tags:

Follow the Discussion

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.