Working with tiles and the splash screen in Hilo (Windows Store apps using C++ and XAML)
From: Developing an end-to-end Windows Store app using C++ and XAML: Hilo
Using tiles and the splash screen effectively can give your users a great first-impression of your Windows Store app using C++ and XAML.
Download
After you download the code, see Getting started with Hilo for instructions.
You will learn
- How we incorporated an app tile that displays the user's photos.
- How we added the splash screen.
- What considerations to make in your own app.
Applies to
- Windows Runtime for Windows 8
- Visual C++ component extensions (C++/CX)
- XAML
Why are tiles important?
Traditional Windows desktop apps use icons. Icons help visually connect an app or file type with its brand or use. Because an icon is a static resource, you can often wait until the end of the development cycle to incorporate it. However, tiles are different from icons. Tiles add life and personality and can create a personal connection between the app and the user. The goal of tiles is to keep your users coming back by offering a personal connection.
[Top]
Choosing a tile strategy
You have a few options when choosing a tile strategy. You can provide a wide tile, which your user can then change to a square tile if they prefer. You can also display badges and notifications on your tile.
We settled on the following rules for the tile behavior:
- Display wide default tile that shows the Hilo logo before the app is ever launched.
- Each time the app is launched, update the square and wide tiles according to these rules:
- If the user has less than 5 pictures in the Pictures folder, display the default tile that shows the Hilo logo.
- Otherwise, randomly choose 15 pictures from the most recent 30 and set up the notification queue to cycle among 3 batches of 5 pictures. (If the user chooses the square tile, it will show only the first picture from each batch.)
Read Guidelines and checklist for tiles to learn how tile features relate to the different tile styles. Although you can provide notifications to the square tile, we wanted to also enable the wide tile so that we could display multiple images.
You can also enable secondary tiles for your app. A secondary tile enables your users to pin specific content or experiences from an app to the Start screen to provide direct access to that content or experience. For more info about secondary tiles, read Pinning secondary tiles.
[Top]
Designing the logo images
Our UX designer created the small, square, and wide logos according to the pixel size requirements for each. The designer suggested a theme that fitted the Hilo brand. Choosing a small logo that represents your app is important so that users can identify your app when the tile displays custom content. This is especially important when the contents of your tile changes frequently—you want your users to be able to easily find and identify your app. The small Hilo logo has a transparent background so that it looks good when it appears on top of a tile notification or other background.
The Assets folder contains the small, square, and wide logo images. For more info about working with image resources, see Quickstart: Using file or image resources and How to name resources using qualifiers.

30 x 30 pixels

150 x 150 pixels

310 x 150 pixels
[Top]
Placing the logos on the default tiles
The Visual Studio manifest editor makes the process of adding the default tiles relatively easy. To learn how, read Quickstart: Creating a default tile using the Visual Studio manifest editor.
[Top]
Updating tiles
You use tile templates to update the tiles. Tile templates are an XML-based approach to specify the images and text used to customize the tile. The Windows::UI::Notifications namespace provides classes to update Start screen tiles. For Hilo, we used the TileUpdateManager and TileUpdater classes to get the tile template and queue the notifications. Hilo defines the TileUpdateScheduler and WideFiveImageTile classes to choose the images to show on the tile and form the XML that is provided to Windows Runtime.

Tile updates occur in the App::OnLaunched method, which is called during app initialization.
m_tileUpdateScheduler = std::make_shared<TileUpdateScheduler>(); m_tileUpdateScheduler->ScheduleUpdateAsync(m_repository, m_exceptionPolicy);
The TileUpdateScheduler::ScheduleUpdateAsync method performs the following steps to update the tile in the background:
- Create a local folder that will store a copy of the thumbnails to be displayed on the tile.
- If there are at least 30 photos in the user's collection:
- Randomly select 15 of the user's 30 most recent photos.
- Create 3 batches of 5 photos.
- Generate thumbnails for the randomly selected photos in the local app folder.
- Create the notification and update the tile.
Verify your URLs describes the ways you can reference images that appear in your tile notification. We use ms-appdata:///local/ for Hilo because we copy thumbnails to a local app folder.
The following example shows the TileUpdateScheduler::ScheduleUpdateAsync method. This code controls the process that creates the thumbnails folder, selects the images, and updates the tile. Each call to the Windows Runtime is asynchronous; therefore, we create a chain of continuation tasks that perform the update operations.
TileUpdateScheduler.cpp
task<void> TileUpdateScheduler::ScheduleUpdateAsync(std::shared_ptr<Repository> repository, std::shared_ptr<ExceptionPolicy> policy) { // The storage folder that holds the thumbnails. auto thumbnailStorageFolder = make_shared<StorageFolder^>(nullptr); return create_task( // Create a folder to hold the thumbnails. // The ReplaceExisting option specifies to replace the contents of any existing folder with a new, empty folder. ApplicationData::Current->LocalFolder->CreateFolderAsync( ThumbnailsFolderName, CreationCollisionOption::ReplaceExisting)).then([repository, thumbnailStorageFolder](StorageFolder^ createdFolder) { assert(IsBackgroundThread()); (*thumbnailStorageFolder) = createdFolder; // Collect a multiple of the batch and set size of the most recent photos from the library. // Later a random set is selected from this collection for thumbnail image generation. return repository->GetPhotoStorageFilesAsync("", 2 * BatchSize * SetSize); }, task_continuation_context::use_arbitrary()).then([](IVectorView<StorageFile^>^ files) -> task<IVector<StorageFile^>^> { assert(IsBackgroundThread()); // If we received fewer than the number in one batch, // return the empty collection. if (files->Size < BatchSize) { return create_task_from_result(static_cast<IVector<StorageFile^>^>( ref new Vector<StorageFile^>())); } auto copiedFileInfos = ref new Vector<StorageFile^>(begin(files), end(files)); return RandomPhotoSelector::SelectFilesAsync(copiedFileInfos->GetView(), SetSize * BatchSize); }, task_continuation_context::use_arbitrary()).then([this, thumbnailStorageFolder, policy](IVector<StorageFile^>^ selectedFiles) -> task<Vector<StorageFile^>^> { assert(IsBackgroundThread()); // Return the empty collection if the previous step did not // produce enough photos. if (selectedFiles->Size == 0) { return create_task_from_result(ref new Vector<StorageFile^>()); } ThumbnailGenerator thumbnailGenerator(policy); return thumbnailGenerator.Generate(selectedFiles, *thumbnailStorageFolder); }, task_continuation_context::use_arbitrary()).then([this](Vector<StorageFile^>^ files) { assert(IsBackgroundThread()); // Update the tile. UpdateTile(files); }, concurrency::task_continuation_context::use_arbitrary()).then(ObserveException<void>(policy)); }
To create a thumbnail image, the ThumbnailGenerator::CreateThumbnailFromPictureFileAsync method gets the thumbnail from the image, decodes it, and then encodes it as a .jpg image. Because tile notifications only support the .jpg/.jpeg, .png, and .gif image formats, we re-encode each image to enable the app to use the .bmp, and .tiff image formats. We chose .jpg as the target format because it produces the smallest images, while still providing the desired image quality.
ThumbnailGenerator.cpp
task<InMemoryRandomAccessStream^> ThumbnailGenerator::CreateThumbnailFromPictureFileAsync(
StorageFile^ sourceFile,
unsigned int thumbSize)
{
(void)thumbSize; // Unused parameter
auto decoder = make_shared<BitmapDecoder^>(nullptr);
auto pixelProvider = make_shared<PixelDataProvider^>(nullptr);
auto resizedImageStream = ref new InMemoryRandomAccessStream();
auto createThumbnail = create_task(
sourceFile->GetThumbnailAsync(
ThumbnailMode::PicturesView,
ThumbnailSize));
return createThumbnail.then([](StorageItemThumbnail^ thumbnail)
{
IRandomAccessStream^ imageFileStream =
static_cast<IRandomAccessStream^>(thumbnail);
return BitmapDecoder::CreateAsync(imageFileStream);
}).then([decoder](BitmapDecoder^ createdDecoder)
{
(*decoder) = createdDecoder;
return createdDecoder->GetPixelDataAsync(
BitmapPixelFormat::Rgba8,
BitmapAlphaMode::Straight,
ref new BitmapTransform(),
ExifOrientationMode::IgnoreExifOrientation,
ColorManagementMode::ColorManageToSRgb);
}).then([pixelProvider, resizedImageStream](PixelDataProvider^ provider)
{
(*pixelProvider) = provider;
return BitmapEncoder::CreateAsync(
BitmapEncoder::JpegEncoderId,
resizedImageStream);
}).then([pixelProvider, decoder](BitmapEncoder^ createdEncoder)
{
createdEncoder->SetPixelData(BitmapPixelFormat::Rgba8,
BitmapAlphaMode::Straight,
(*decoder)->PixelWidth,
(*decoder)->PixelHeight,
(*decoder)->DpiX,
(*decoder)->DpiY,
(*pixelProvider)->DetachPixelData());
return createdEncoder->FlushAsync();
}).then([resizedImageStream]
{
resizedImageStream->Seek(0);
return resizedImageStream;
});
}
The TileUpdateScheduler::UpdateTile method uses the TileUpdateManager class to create a TileUpdater object. The TileUpdater class updates the content of the app's tile. The TileUpdateScheduler::UpdateTile method calls the TileUpdater::EnableNotificationQueue method to queue notifications for each batch. The TileUpdateScheduler::UpdateTile method then builds a list of image paths for each batch of pictures and passes that list to a WideFiveImageTile object. The WideFiveImageTile object formats the XML for the tile update and for each batch of pictures, and then the TileUpdateScheduler::UpdateTile method calls the TileUpdater::Update method to update the tile.
ThumbnailGenerator.cpp
void TileUpdateScheduler::UpdateTile(IVector<StorageFile^>^ files) { // Create a tile updater. TileUpdater^ tileUpdater = TileUpdateManager::CreateTileUpdaterForApplication(); tileUpdater->Clear(); unsigned int imagesCount = files->Size; unsigned int imageBatches = imagesCount / BatchSize; tileUpdater->EnableNotificationQueue(imageBatches > 0); for(unsigned int batch = 0; batch < imageBatches; batch++) { vector<wstring> imageList; // Add the selected images to the wide tile template. for(unsigned int image = 0; image < BatchSize; image++) { StorageFile^ file = files->GetAt(image + (batch * BatchSize)); wstringstream imageSource; imageSource << L"ms-appdata:///local/" << ThumbnailsFolderName->Data() << L"/" << file->Name->Data(); imageList.push_back(imageSource.str()); } WideFiveImageTile wideTile; wideTile.SetImageFilePaths(imageList); // Create the notification and update the tile. auto notification = wideTile.GetTileNotification(); tileUpdater->Update(notification); } }
The WideFiveImageTile class, which is defined in WideFiveImageTile.cpp, encapsulates the creation of the XML for the tile update. It builds upon the TileTemplateType::TileWideImageCollection tile template by inserting the provided list of file names into the template's XML content. It also uses the TileSquareImage tile template to show just the first picture if the user chooses the square tile. The WideFiveImageTile class then creates a TileNotification object using the updated XML content.
This code example shows how the WideFiveImageTile::UpdateContentWithValues method updates the template's XML content.
WideFiveImageTile.cpp
void WideFiveImageTile::UpdateContentWithValues(XmlDocument^ content) { if (m_fileNames.size() == 0) return; // Update wide tile template with the selected images. for(unsigned int image = 0; image < m_fileNames.size(); image++) { IXmlNode^ tileImage = content->GetElementsByTagName("image")->GetAt(image); tileImage->Attributes->GetNamedItem("src")->InnerText = ref new String( m_fileNames[image].c_str()); } // Update square tile template with the first image. TileTemplateType squareTileTemplate = TileTemplateType::TileSquareImage; XmlDocument^ squareTileXml = TileUpdateManager::GetTemplateContent(squareTileTemplate); IXmlNode^ tileImage = squareTileXml->GetElementsByTagName("image")->First()->Current; tileImage->Attributes->GetNamedItem("src")->InnerText = ref new String( m_fileNames[0].c_str()); auto node = content->ImportNode(squareTileXml->GetElementsByTagName("binding")->First()->Current, true); content->GetElementsByTagName("visual")->First()->Current->AppendChild(node); }
The XML for a typical tile notification looks like this:
<tile> <visual> <binding template="TileWideImageCollection"> <image id="1" src="ms-appdata:///local/thumbnails/thumbImage_0.jpg"/> <image id="2" src="ms-appdata:///local/thumbnails/thumbImage_1.jpg"/> <image id="3" src="ms-appdata:///local/thumbnails/thumbImage_2.jpg"/> <image id="4" src="ms-appdata:///local/thumbnails/thumbImage_3.jpg"/> <image id="5" src="ms-appdata:///local/thumbnails/thumbImage_4.jpg"/> </binding> <binding template="TileSquareImage"> <image id="1" src="ms-appdata:///local/thumbnails/thumbImage_0.jpg"/> </binding> </visual> </tile>
If you use text with your tile, or your images are sensitive to different languages and cultures, read Globalizing tile and toast notifications to learn how to globalize your tile notifications.
Use these resources to learn more about tile templates:
[Top]
Adding the splash screen
All Windows Store apps must have a splash screen, which is a composite of a splash screen image and a background color, both of which you can customize. The splash screen is shown as your app loads. As with tiles, our designer created the splash screen image. We chose an image that resembled the default tile logos and fits the Hilo brand. It was straight forward to add the splash screen to the app. Read Quickstart: Adding a splash screen to learn how.

You might need to display the splash screen for a longer duration, to show real-time loading information to your users, if your app needs more time to load. This involves creating a page that mimics the splash screen by showing the splash screen image and any additional info. For Hilo, we considered using an extended splash screen, but because the app loads the hub page very quickly, we didn't have to add it. For more info about extending the duration of the splash screen, see How to extend the splash screen.
Use these resources to learn more about splash screens:
[Top]


