Générer une image pour le fond d'une tuile

Générer une image pour le fond d'une tuile

Nous allons dans cet article reprendre le projet de l’article précédent pour le compléter afin qu’il génère lui même le fond de sa tuile en incrustant dans l’image de fond : un label et l’heure courante.

Nous avons découpé notre application en trois projets :

  • l’application en même
  • le background agent
  • la libraire commune contenant notamment la fonction de génération de tuile.
Pour modifier la génération, nous n’allons qu’éditer le troisième projet, et plus particulièrement la fonction :
public static StandardTileData GenerateTile()

Création de l’image

Pour créer notre image, nous allons utiliser la classe WriteableBitmap et plus particulièrement sa fonction Render qui permet de faire le rendu d’un contrôle XAML directement dans l’image au lieu de l’écran.

Nous allons donc créer deux TextBlock, le premier contenant le label “heure:”, le seconc contenant l’heure courante.

Commençons par créer la police et la brush pour nos textblocks :

var fontFamily = new FontFamily("Segoe WP SemiLight");
var fontColor = new SolidColorBrush(Colors.White);

Créons maintenant nos deux textblocks :

//label
var textTextBlock = new TextBlock()
{
Text = "time:",
FontSize = 40,
Foreground = fontColor,
FontFamily = fontFamily
};

//time textblock
var timeTextBlock = new TextBlock()
{
Text = DateTime.Now.ToShortTimeString(),
FontSize = 20,
Foreground = fontColor,
FontFamily = fontFamily,
TextWrapping = TextWrapping.Wrap,
Width = 173
};

il nous reste maintenant qu’à créer notre writeableBitmap et d’y dessiner nos deux textblocks. La fonction Render prenant comme deuxième paramètre une transformation, nous allons nous en servir pour positionner nos contrôles via une TranslationTransform.

var bitmap = new WriteableBitmap(173, 173);

//render the label
bitmap.Render(textTextBlock, new TranslateTransform()
{
X = 0,
Y = 16
});

//render the time
bitmap.Render(timeTextBlock, new TranslateTransform()
{
X = 0,
Y = 55
});
//permet de forcer le dessin
bitmap.Invalidate();

Reste à sauvegarder notre image dans l’isolated Storage de notre application. Attention toutefois, nous ne pouvons les sauvegarder n’importe ou, il faut le faire dans le dossier “/Shared/ShellContent” qui permet de partager du contenu entre l’agent et l’application.

using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
var stream = store.CreateFile(tileImage);
bitmap.SaveJpeg(stream, 173, 173, 0, 100);
stream.Close();
}

Voici le résultat :

Bon, le texte est bien affiché, mais le fond de la tuile est noir (si on met le thème noir, le fond est toujours noir) et non de la couleur du téléphone.

En fait, le writeableBitmap ne peut être sauvegardé qu’en JPG, donc dans un format de fichier sans transparence. Par défaut, le WriteableBitmap étant tout noir, voilà pourquoi notre tuile est ainsi.

Il n’est pas possible de générer des PNG, toutefois, il est possible de générer des JPG ayant comme fond la couleur d’accent courante. Attention toutefois, comme nous l’avons pu noter dans l’article Les différentes solutions pour dessiner dans une tuile, si l’utilisateur change sa couleur de fond, la tuile garder la précédente couleur jusqu’au prochain refresh (lancement de l’application ou background agent).

Pour remplir le fond, il faut déjà créer un rectangle avec cette couleur :

var backgroundRectangle = new Rectangle() { Height = 173, Width = 173, Fill = (Brush)Application.Current.Resources["PhoneAccentBrush"] };

puis juste avant de dessiner les textblock, on dessine le rectangle :

bitmap.Render(backgroundRectangle, new TranslateTransform());

On recompile, on relance l’application et voici le résultat :

Attention au contexte !

Un point important, est le contexte, Textblock, Rectangle et WriteableBitmap nécessite d’être dans le thread UI pour fonctionner, sinon une exception “Invalid cross-thread access” est lancée.

Du côté de notre application pas de soucis, App_Desactivate et App_Closing sont bien appelé dans le thread UI, toutefois, le background Agent par défaut n’est pas dans le thread UI, il faut donc forcer le passage sur ce thread en entourant l’instruction UpdateTiles de l’agent de :

            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                CustomTile.TileGenerator.UpdateTiles();
            });

Téléchargez la solution complète : CustomTile ZIP

Comments are closed.