WinJS, ListViews and Limiting the Number of Items

2/6/2013 12:09:40 PM

When working with a Listview in Windows 8 applications using HTML and JavaScript, one of the challenges that is often faced is how to limit the number of items added to ListView control.  There are different reasons to limit the number of items, but the one I am going to focus is where you want to fill the vertical space of the ListView (using the ListLayout render function) with as many items as possible but without causing the ListView to scroll vertically.

Image demonstrating insufficient padding for an embedded pannable area.This is a common ‘problem’ when building modern UI applications.  Because Windows 8 apps typically pan horizontally, the design guidelines state that you should not have vertically scrollable content.  Looking at the picture to the right, imagine that those “vertical” regions are displaying tweets from various users you are following.  You want to show as many tweets as possible for each user, but you do not want to show so many as to have the ListView enable scrolling.

So solve this problem, use the createFiltered function on the Binding List you are using to put data into the ListView.  For example, I may have setup my ListView of tweets like this in my HTML:

<div id="tweets" data-win-control="WinJS.UI.ListView" 
     data-win-options="{itemTemplate:select('#tweetTemplate'),
                        layout:{type:WinJS.UI.ListLayout},
                        selectionMode:'none',
                        swipeBehavior:'none'}">
</div>

In my page.js file, I need to do a few things.  First, I need to get the height of my ListView.  So after my page has loaded (typically in the ready handler for my Page control), I do this:

var tweetList = document.getElementById('tweets');
var listHeight = tweetList.clientHeight;

with that, I can now setup my Binding List like this:

function displayTweets (tweetArray, listHeight) {
     var count = 0;
     var list = new WinJS.Binding.List(newTweets).createFiltered(
           function (item) {
                 count += 90;
                  return (count < listHeight
           }
     );
     tweets.winControl.itemDataSource = list.dataSource;
}

Now you will always get the correct number of items displayed in your ListView regardless of the screen size.

Tags:

WinJS | Windows 8

Ways You Can Use the Search Contract in Your Windows Game

9/18/2012 10:07:01 AM

ATTENTION:  Great resources for Windows 8 developers!!! Make sure you take advantage of 30 Days.

 

clip_image002The Search contract is a way you can integrate the Windows 8 search experience into your game. Windows 8 provides a Search feature that is enabled via the Charm bar (shown at right). The Charm bar is activated by a user swiping from the right edge of the screen with a finger, or by either a mouse or keyboard action (mouse to the upper right corner of the screen or Windows+C ). From the Charm bar, the user can select the Search charm and enter the default Search experience.

The default Search experience allows users to search through their files, applications, and settings to find things they are interested in. More interesting, however, is that this Search experience can be extended in two unique ways.

First, if you look at the default Search pane, you will see a list of applications in the bottom half of the pane. The Search pane allows the user to send the search term they have typed into the search text box to another application by tapping on its tile. Windows 8 will launch the selected app and pass the search term to the application. It is up to the application to use the search term in a way that makes sense for the app. For a game, this could be looking up the term in a help file, finding a match with online players, or looking in saved games for a possible match.

clip_image004The second way that Search can be extended is by integrating the search contract with an application while it is running. In this case, any search term entered would be immediately passed to your app. The Search panes is updated to show that the search term will be passed to your game and not to a more general search.

Getting Started

In a game scenario, it is most likely that the second option, “in game” searching, is the most applicable. There are a lot of potential uses for search. A few examples include searching help, looking up friends or “frenemies” to see who is playing online, finding units or game pieces on a board, and more. Fundamentally, all of these scenarios are handled the same. They only differ in how you will present the results to the player. Because “in game” searching is most common for games, let’s start with an example of how to hook a game into Windows 8 Search.

clip_image007In order to integrate with Search, it is important that you set up the Search Declaration in your game’s package.appxmanifest file. Double-click on the file in Solution Explorer, and then click on the Declaration tab. In the Available Declarations dropdown, select the Search Declaration and click the Add button. See the picture at the right for what your package.appxmanifest should look like when you are finished.

We are now ready to add Search to our game. In most cases, you can configure search in the page that serves as the entry point into your game. If you are starting with one of the default Visual Studio templates, this will be default.html and its corresponding script file, default.js. Hooking into search in this manner will provide search handling through the lifetime of your application. You can, however, connect to search from individual pages instead of the main page if this makes sense for your game. Just remember, you will need handle the search events in each page you want search enabled.

In the Application activated event handler, you will want to add the following code:

 

var searchPane = Windows.ApplicationModel.Search.SearchPane.getForCurrentView();
searchPane.onquerysubmitted = function (eventObject) {
 
   var query = eventObj.queryText;
 
   // handle query as necessary
}

This is the minimum required to receive a query typed into the Search pane. What you do with the query is up to you. You could navigate to a page that lists online friends that match the query, or navigate to a help page and list matching topics. You could simply highlight game pieces that match the search on the currently viewed game board.

You can also provide suggestions to the user as they enter text into the search field. Handle the suggestionsrequested event and provide suggestion responses in the function handler:

 

Windows.ApplicationModel.Search.SearchPane.getForCurrentView().onsuggestionsrequested = function (eventObject) {
 
   var queryText = eventObject.queryText, suggestionRequest = eventObject.request;
   var query = queryText.toLowerCase();
   var maxNumberOfSuggestions = 5;
 
   for (var i = 0, len = suggestionList.length; i < len; i++) {
      if (suggestionList[i].substr(0, query.length).toLowerCase() === query) {
         suggestionRequest.searchSuggestionCollection.appendQuerySuggestion(suggestionList[i]);
 
         if (suggestionRequest.searchSuggestionCollection.size === maxNumberOfSuggestions) {
             break;
         }
      }
};

You are only permitted to list a total of five suggestions. How you create suggestions and order them is up to you.

The suggestionsrequested event fires with every key stroke. If you are performing async operations as part of your suggestion lookup, you need to handle that appropriately. Below is some skeleton code that shows how to handle hitting a remote web service to lookup search suggestions. This could be hitting the cloud component of your game to compare the query to the names of online players, for example.

 

Windows.ApplicationModel.Search.SearchPane.getForCurrentView().onsuggestionsrequested = function (eventObject) {
 
   var queryText = eventObject.queryText, 
          language = eventObject.language,
          suggestionRequest = eventObject.request;
 
   var deferral = suggestionRequest.getDeferral();
 
   if (xhrRequest && xhrRequest.cancel) {
      xhrRequest.cancel();
   }
 
   xhrRequest = WinJS.xhr({ url: suggestionUri });
   xhrRequest.done(
       function (request) {
           if (request.responseText) {
               // process response
           }
           deferral.complete(); // Indicate we're done supplying suggestions.
       },
       function (error) {
           deferral.complete();
       });
   };
  }
};

There are a few things to point out. First, make sure you get a deferral from the request if you are going to be making async calls. This allows your handler to run async operations and still be able to notify the Search pane when you are finished. In addition, you should make sure you check that the async operation you started on the first key press is complete or cancelled before firing off another request on the second key press. You can see that we do that here by using a .done() on the xhrRequest, which returns undefined. If xhrRequest is not undefined, then we need to cancel that request. In our case, xhrRequest is defined outside the onsuggestionsrequested handler to ensure it “lives” between requests.

Wrapping Up

Using search is a great way to provide a consistent experience for your users. There are a lot of scenarios where search makes sense for a game, and you are only limited by your imagination on how to use it. You can find a great sample app that illustrates the techniques discussed here and more at http://code.msdn.microsoft.com/windowsapps/Search-app-contract-sample-118a92f5

Tags:

HTML | WinJS | Windows 8

Ways You can Leverage Share Contracts in your Windows Store Game

9/18/2012 9:35:48 AM

For more detailed information and examples, see Adding share (Metro style apps using JavaScript and HTML) article and the two share sample applications, Share Source and Share Target.  Make sure you take advantage of 30 Days.

 

One of the great new features available to developers creating Windows Store games is the Share Contract. The Share Contract, or sharing, provides an easy way for you to share data from your game to other applications the user has on their system.

Why is sharing useful? Games are, by their nature, social. Perhaps you are starting to play a game of Ultimate Zombie Battles and you want to let your friends know you are playing so they can come join you. You may want to let the world know that you just achieved a personal high score on Find the Chicken. Or better yet, you just downloaded a new game and you think it is so cool, you want to let your friend and family know about it so they can play it too.

As a game developer, you want to enable all these scenarios. It provides a great Windows 8 gaming experience, differentiates your game from those that don’t share, and it can create more awareness about your game. Ideally, you want to enable these scenarios because they are features your user wants and makes your game that much more enjoyable.

The Basics

sharebrokerLet’s start off by gaining a basic understanding of how sharing works and the steps required for an app to share data. Later, we will look at some specific scenarios, like those mentioned above, and how you would implement them in your game.  Fundamentally, sharing is a very simple operation. A share operation consists of three parts: the source application that is sharing some data, the data to be transferred via the share broker, and the target application that will receive and process the data. The share broker is provided by Windows 8 and mediates the data exchange between the source and target apps. The picture to the right shows how this process works.

The code below shows how an application that is a share source would provide data to be shared.

 

var share = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
share.ondatarequested = function (e) {
// Title of Share Options
e.request.data.properties.title = "New High Score";
 
// Description of what is being shared
e.request.data.properties.description = "You Scored 1,000pts";
 
// Share plain text
e.request.data.setText("Bob’s New High Score is 1,000pts in Zombie Battles");
};

sharepaneYou should always set the title and description properties. These properties provide context to the user when they are looking at the Share UI and give them confidence in what they are about to share. You can set a variety of different data types to share. In the example code, we are only sharing simple text data via the setText() function. You could however, share multiple data types in a single share request, and you are encouraged to do so.

In addition to sharing text, HTML, and images, there are a variety of other formats you can share, including custom data formats. The supported formatted are listed below:

  • Plain Text
  • Formatted text
  • URIs
  • HTML
  • Images
  • Files
  • Custom data formats

By providing multiple data types, you expand the total number of target applications the user can share your application’s data with, and you allow the target application to choose which data type it can use the best. For instance, if a target app can understand both text and HTML, it would likely prefer to use the HTML data format since it provides for more engaging way to display text.

Sharing Different Data Formats

When sharing a high score, we may want to provide something more engaging than a simple text string. You can craft a share request to use a variety of formats to provide a much nicer experience. In addition to text, you could provide HTML. This would allow the user to send the high score via email, share the high score image on a social network, etc. The variety of formats provides a richer experience for your users. Our share source example could be updated as shown:

 

   1:  var share = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
   2:   
   3:  share.ondatarequested = function (e) {
   4:   
   5:  e.request.data.properties.title = "New High Score";
   6:  e.request.data.properties.description = "You Scored 1,000pts";
   7:  e.request.data.setText("Bob set a New High Score of 1,000pts is Zombie Battles");
   8:   
   9:  var HTMLstr = "<b>Bob</b> <h3>New High Score</h3> is 1,000pts in <i>Zombie Battles</i>";
  10:   
  11:  // Format HTML 
  12:  e.request.data.setHTMLFormat(
  13:       Windows.ApplicationModel.DataTransfer.HTMLFormatHelper.createHTMLFormat(HTMLstr));
  14:  };

Notice, in the setHTMLFormat() function the need to call createHTMLFormat(). This prepares the HTML for use by the target application, and failure to call this on an HTML string will result in the target app not being able to display the transferred HTML.

Text and HTML are nice, but pictures are worth a thousand words. Sharing an image can add additional pizazz to your game’s share operation. Imagine your game is based on the player advancing from one level of difficulty to another. You may want to have an image, or a badge, that can be shared to show the player’s success. You have placed these badge images into your project just like any other asset so you can easily display them in an <img> tag on screen when congratulating your user. But how do you share an image that is part of your game’s install package with another application?

There are two ways to approach this. The first is that you just want to share the image itself. The second is you want to take some HTML that is being displayed onscreen, including the badge image, and turn that into your share payload.

In the first scenario, all we need to do is acquire a stream to the image file and hand that stream to the share target application. Here is how you do that:

 

var deferral = e.request.getDeferral();
 
var imageUri = new Windows.Foundation.Uri('ms-appx:///images/highscore.png');
Windows.Storage.StorageFile.getFileFromApplicationUriAsync(imageUri).then(function (imageFile) {
 
var streamReference = Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(imageFile);
 
request.data.properties.thumbnail = streamReference;
request.data.setStorageItems([imageFile]);
request.data.setBitmap(streamReference);
 
deferral.complete();

Going through this code, there are few things worth pointing out. First, since we will be performing an asynchronous call, it is a good idea to grab a deferral from the DataTranferManager request. This lets the DTM know that we are doing something that could take a while. In our case, we could probably skip this since we are making a relatively quick call to local assets, If you had a longer running process it is a good thing to do.

highscoreNext, notice how we craft the Uri to the local image. We prefix the file path with “ms-appx://”, letting WinRT know that we are accessing a local resource. We turn that into an Uri, and then use the getFileFromApplicationUriAsync() call to obtain the file itself, which we then turn into a stream. Since we are passing an image, it is a good idea to a) set the thumbnail property for apps that would like to use a thumbnail, and pass the stream reference via setStorageItems(). We do this because some apps will want to deal with images files as streams instead of actual bitmaps, thus giving our use a wider range of applications they can share with and more flexibility for the target app when working with our share payload.

Now, let’s handle sharing parts of our UX. You have crafted a nice “reward page” showing someone that they have achieved a new level, set a new high score, whatever. Inside that page is some HTML like the following:

<div id="HTMLFragment" class="HTMLFragment" style="margin-top: 20px;">
 
<h2>Top Score of the Day</h2>
 
<img id="fragmentImage" src="/images/highscore.png">
 
<p>Congratulations!</p>
 
</div>

This example is admittedly simple, but it has all the elements we need to demonstrate the technique. The important thing to realize is that we are including an <img> that’s src property is set to a local image file. We process the HTML for sharing like this:

var request = e.request;
var range = document.createRange();
var fragment = document.getElementById("HTMLFragment");
range.selectNode(fragment);
 
request.data = MSApp.createDataPackage(range);
request.data.properties.title = "Share Text Example";
request.data.properties.description = "Demo shows how to share text string";
 
var path = document.getElementById("fragmentImage").getAttribute("src");
var imageUri = new Windows.Foundation.Uri(path);
 
var streamReference = Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(imageUri);
 
request.data.resourceMap[path] = streamReference;

Start by creating a range and selecting the HTML root element we want to send via our share operation. We convert that HTML into a fragment. There are img tags that need to have actual image files included in the payload so the fragment will render correctly. We grab the src Uri and create a stream reference that is passed as part of the resourceMap. The resource map will allow the target app to find the actual image and render it properly. In our example, there is a single image. If you have more than one image, you will need to find all the images in the fragment and repeat this process for each one.

Invoking Share

You will need to decide how you want to expose the opportunity to share information to your player. Over time, users will become accustomed to sharing data by swiping from the right edge of the screen to invoke the Charm bar, by hovering the mouse in the top-right corner, or pressing Windows Key+C . At times, however, it may not be obvious in your game that there is content that can be shared, or perhaps you want to emphasize that a particular item can be shared.

You can invoke the Share UI at any time from within your application via:

Windows.ApplicationModel.DataTransfer.DataTransferManager.showShareUI();

Being a Share Target

It is a less likely, given the highly specialized nature of a game, that you will need to be a share target. There are scenarios when being a share target makes sense. An example would be a game that also has a companion app, like a game map designer, a character designer, or something similar. In cases like that, you may want have your game to be a share target.

declarationsTo become a share target, the first thing you must do is declare that capability in your game’s package.appxmanifest file. Double-clicking on that file in Solution Explorer will open an editor. Click on the Declarations tab and you will see the screen shown at the right.

In the Available Declaration drop-down, select Share Target and click the Add button. This is step one. You must now add, using the settings to the right of the Supported Declarations pane, the Data Formats or File Types that you can accept.

In our map editor scenario, you could either use a unique file extension for saved map files or define a custom data format. The file extension approach is easy to understand. You save a map in the map editor, perhaps using the “.map” extension. You then set the Share Target setting on your game to by adding a new Supported File Type entry and set the value to “.map”. Now, from you game editor, you could simply share the saved “.map” file directly to the game, without the user having to launch the game and open the file. You game is still responsible for processing the “.map” file, but the user doesn’t have to leave the map editor to get the data to your game.

We will look at the other scenario, of working with a custom data type. It shares the same approach as the file type example, so it we are killing two birds with one stone. Let’s pretend we have a character editor that lets me build characters for the main game. In our character editor, we will share a custom data type and pass the character info as a JSON string. I won’t cover that here since the techniques are similar to what we have already looked at. If you want to see an example of doing that, check out the Share Source sample application here.

In our main game, we start by adding a new Data Format setting. In the textbox, you will enter a unique URI for you custom data format. Let’s set that to “http://mycoolgame.com/character”. At the bottom of the Declarations tab, with Share Target selected as the Supported Declaration, you will see a text box called Start Page. Here, you can enter the name of the HTML page that will be opened when your game is selected at the target of a share operation. You should design this page to reflect your game’s branding, colors, etc. Simply add a new page to the root of your project named ‘target.html, style it as necessary, and then types its name in the Start Page text box.

The more interesting aspect is handling the share operation. In the script file for target.html, you will want to do two things. Start handling the WinJS.Application activated event as shown below:

 

WinJS.Application.onactivated = function(e) {
 
    if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.shareTarget) {
 
           eventObject.setPromise(WinJS.UI.processAll());
           shareOperation = eventObject.detail.shareOperation;
 
           WinJS.Application.queueEvent({ type: "shareready" });
     }
};

There are two important things to note. First, we check to make sure that we are arriving at target.html via a shareTarget activation. Next, after we grab the data payload of the operation, we place an event on the Application queue. This is a user-defined event, not one provided by default. The reason we add a shareready event (or whatever you want to call it) to the queue is we want to return control to the system as soon as possible. By doing this, we can run our longer processes for parsing the data without slowing down the UI. Note, the variable shareoperation is defined elsewhere so that it is accessible to both the onactivated handler and the shareready handler.

In shareready, we can extract the data and update the UI on target.html as necessary.

 

function shareReady(eventArgs) {
 
   document.getElementById("title").innerText = shareOperation.data.properties.title;
   document.getElementById("description").innerText = shareOperation.data.properties.description;
 
   if (shareOperation.data.contains(customFormatName)) {
      shareOperation.data.getTextAsync(customFormatName).done(function (customFormatString) {
          var customFormatObject = JSON.parse(customFormatString);
 
          // Update rest of the UI
          });
    }
};

In our shareready handler, we update our UI immediately with the title and description provided in the share operation payload. We then look to see if it contains our custom data format. If so, we extract the custom data, which in this case has been passed to us as a JSON string. We can then continue on loading the character data into our game for future use. This might be saving it as a local data file, for example.

Wrapping Up

Sharing is a great way to extend the value of your game and to get some free publicity, as well. Players that enjoy your game will want to let others know of their accomplishments, and sharing allows you to leverage all of the great social media, email, and networking clients that the user will already have installed on their system. Sharing is easy to add, so make sure you do it, and take your Windows 8 game experience to the next level.

Tags:

HTML | WinJS | Windows 8

Windows 8 and HTML Part 9: Share Contract

9/4/2012 10:41:30 AM

Continuing with Part 8 of building WinRT applications with HTML and JavaScript.  You can find earlier installments at their respective links: Part 1Part 2Part 3- Part 4- Part 5- Part 6- Part 7- Part 8. If you are interested in the source code for the TweetScan application, you can get it off of GitHub.  Make sure you sign up for How to develop a Windows 8 app in 30 days from Generation App.

Get video here.

The Share Contract allows an application to either Share content with other applications on the user’s machine, or accept content from other applications.  Many applications will be both a Share Source and a Share Target.  In this installment, we look at adding Share Source capability to our TweetScan application.

For TweetScan, we want a user to be able to select a particular tweet he/she is interested in, and share it with other applications.  They may want to post the tweet to Facebook, retweet to other Twitter users, or maybe send it to a friend in an email message.  We will look at added Share Target capabilities to TweetScan in a future installment.  For now, we will just handle turning TweetScan into a Share Source.

To get started, we need to acquire the DataTransferManager.  The DTM is the communication broker between the Share Source and Share target applications.  When the Share Charm is invoked, the DTM will ask the Share Source application for any data that is can share at that time.  If there is data to be shared, the DTM will then look at the system’s installed applications and see which ones can support the type of data being shared (text, html, images, etc.).  You can share more than one type of data in a single share payload.  In fact, you are encouraged to support as many data types as possible so your user gets the broadest choice of Target applications.  It also allows the Target application to provide the best experience it can (html is better looking than text, for example).

Hooking into the DTM is straightforward:

var dtm = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
dtm.ondatarequested = function (e) { ... );

In the ondatarequested function, you set up the data you want to share.  In our case, we want to check the listView being used to display tweets, see if a tweet has been selected, and if so, share that tweet.

dtm.ondatarequested = function (e) {
                var deferral = e.request.getDeferral();
                gridView1.winControl.selection.getItems().done(
                    function (items) {
                        if (items.length > 0) {
                            e.request.data.properties.title = "TweetScan";
                            e.request.data.properties.description = "Share tweet from " + items[0].data.from_user_name;
                            e.request.data.setText(items[0].data.text);
                            
                        }
                    },
                    function (error) { console.error(error.message); }
                    );
                deferral.complete();
            };

A few things to point out. First, getting the selected items for the listView (gridView1) requires the use of a Promise.  Because we are doing an async operation, it is recommended that you get a deferral from the DTM request by calling getDeferral() on the request object.  This lets the DTM know that we may take a little while to finish our processing of the request, and that we will let the DTM know when we are finished with a call to deferral.compete().

It is also important to set the title and description values on the data payload.  This is what is used by the Share Charm UI to let the user know what your application is sharing, and it also used by the Source application to update its UI.

In our example, we are only calling setText() and passing back a simple string representation of the tweet.  As mentioned early, you are encouraged to make additional calls to other set operations (setImage, setHtml, etc.) to provide a richer experience for your users.

All in all, an easy way to get data out of your application and into other ones.

Tags:

HTML | Windows 8 | WinJS

Windows 8 and HTML Part 6: Displaying Data with WinJS ListView

8/20/2012 2:51:04 PM

Continuing with Part 6 of building WinRT applications with HTML and JavaScript.  You can find earlier installments at their respective links: Part 1Part 2Part 3- Part 4- Part 5. If you are interested in the source code for the TweetScan application, you can get it off of GitHub.

 

Watch video here

In all honesty, it is easier to watch the video than for me to try and describe in text what I do in this installment. :)

Tags:

Blend | Windows 8 | WinJS

Powered by BlogEngine.NET 1.6.0.0
Theme by Mads Kristensen

About the author

Jeff Brand Jeff Brand

This is the personal web site of Jeff Brand, self-proclaimed .NET Sex Symbol and All-Around Good guy. Content from my presentations, blog, and links to other useful .NET information can all be found here.

E-mail me Send mail


Calendar

<<  June 2013  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar

My Twitter Updates

XBOX
Live

Recent comments

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2013

Sign in