[MOOC] Windows 8 - 2/ Cycle de vie des apps Windows Store

25. March 2013 13:14 by Renaud in MOOC, Windows 8  //  Tags: , , , ,   //   Comments (0)

 

Dans cette deuxième vidéo (la première étant ici : [MOOC] Windows 8 - 1/ Les contrats en C#) , on aborde le cycle de vie des applications Windows Store. Qu'est-ce qui change par rapport aux applications classiques? Pourquoi ces modifications? Et comment gérer tout ça? C'est ce que je vous explique ici !

La gestion du cycle de vie des applications Windows Store

Les applications Windows Store passent par différents états au cours de leur vie. Elles peuvent successivement être actives, suspendues, ou terminées.

Au sommaire :

- Qu'est-ce qui a changé ?

- Gérer le passage entre les différents états

- Sauvegarder la session des utilisateurs avec le SuspensionManager

- Le SplashScreen étendu

- Les BackgroundTask

- Mettre à jour les LiveTile depuis le background

 

  

Après avoir suivi cette vidéo :

Vous saurez comment sauvegarder une session utilisateur, effectuer des tâches en arrière plan et mettre à jour une vignette dynamique depuis votre application !

La suite

Dans la prochaine vidéo, on verra quelques Tips & Tricks sur le XAML, Visual Studio et Blend

[MOOC] Windows 8 - 1/ Les Contrats en C#

14. March 2013 00:03 by Renaud in MOOC, Windows 8  //  Tags: , , ,   //   Comments (0)

Dans cette vidéo d'une heure, on passe en revue les contrats dans les applications Windows Store. Les contrats permettent de développer des applications qui soient cohérentes et intégrées avec l'ensemble du système Windows 8, ce qui donne globalement une meilleure expérience utilisateur.

Les Contrats

On parle ici des trois principaux contrats que sont le Search, le Share et les Settings. De quoi s'agit-il et comment les implémenter dans une application écrite en C# ? Toutes les réponses sont dans la vidéos.

Au sommaire :

03'40" : Search : permettre de chercher dans votre application depuis n'importe où et n'importe quand.
24'33" : Share : tirer profit du potentiel des autres applications grâce au partage de données App-To-App.
41'32" : Settings : rassembler les paramètres de l'app dans un seul endroit pour offrir une expérience consistante.

Après avoir suivi cette vidéo :

Vous aurez appris à développer des applications super-intégrées à Windows 8, ce qui rendra vos utilisateurs contents :) et ça fait toujours plaisir de recevoir des reviews positives !

La suite

Dans les prochaines vidéos, on parlera du Cycle de vie de l'application et on verra quelques Tips & Tricks sur le XAML, Visual Studio et Blend.

 

[Windows 8] Un exemple court pour comprendre ce que sont Promise, then() et done() avec WinJS

27. January 2013 14:01 by Renaud in Windows 8  //  Tags: , , , , ,   //   Comments (0)

Le concept des Promise et la différence entre then() et done() sont assez difficiles à comprendre. Les explications dans la doc MSDN sont abstraites, et on pourrait croire que then() et done() font la même chose... mais ce n'est pas le cas. Voici un petit morceau de code qui, je pense, est plutôt explicite. Copiez/collez-le dans une nouvelle app Windows 8 JS/HTML et placez des breakpoints à chaque ligne, exécutez l'app, et lisez les commentaires. Le but de ce code est également de vous montrer qu'il n'est pas nécessaire avec les Promise d'écrire des fonctions, dans des fonctions, dans des fonctions... En effet, j'ai vu quelques personnes demander en quoi ça facilitait la vie d'utiliser les Promise avec WinJS. Ils demandent cela parce qu'ils pensent qu'ils doivent écrire du code comme cela :

asyncCall().then(function() {
    anotherAsyncCall().then(function(){
        aThirdAsyncCall();
    }
}

Et c'est vrai que dans ce cas, on pourrait se poser des questions sur l'utilité des Promise. On ne fait que réécrire du code avec des callbacks imbriqués pour finir avec un bordel absolu. Evidemment ce n'est pas le cas ! Regardez le code qui suit et lisez les commentaires :)

 WinJS.xhr({ url: "http://www.microsoft.com" })
        .then(function (microsoftResult) {
            // Ici, on reçoit une réponse, comme on s'y attendait.
            // Que faisons-nous ensuite? 
            // On pourrait faire un autre appel async,
            // ou juste retourner une valeur simple pour voir...
            // Retournons une url, que l'on utilisera 
            // par la suite pour faire une nouvelle requête :
            return "http://dev.windows.com";
        }).then(function (devWindowsUrl) {
            // devWindowsUrl contient simplement la valeur retournée dans la partie précédente.
            // Maintenant, essayons un autre appel async
            // et plutôt que d'utiliser des callbacks (then)
            // retournons simplement l'objet Promise !!
            return WinJS.xhr({ url: devWindowsUrl });
        }).then(function (devWindowsResult) {
            // Quand on arrive ici, on peut voir que dewWindowsResult ne contient pas de Promise.
            // Ca contient en fait le résultat de la Promise, c'est à dire la réponse à la requête.
            // C'est donc là que se situe la magie =)

            // Maintenant déclenchons une exception en appelant une fonction inexistante...
            return devWindowsResult.callNonExistingFunction();
        }).then(function (nonExistingResult) {
            // Nous n'atteignons pas ce point-ci...
            return "ha bon, ça existait finalement?";
        }, function (nonExistingError) {
            // Au lieu de ça, on arrive ici, parce que la Promise s'est terminée avec une erreur.
            // Mais c'est pas grave. Vu qu'on est là, on peut gérer cette erreur.
            // On va retourner un message gentil pour rassurer tout le monde.
            return "cette fonction n'existe pas mais c'est ok, on a géré l'exception !";
        }).then(function (errorHasBeenHandledResult) {
            // du coup, tout rentre dans l'ordre et on continue
            return "content =)";
        }).done(function (result) {
            // et pour finir, il faudrait cloturer par un appel à done()
            // La différence est que si une exception est déclenchée
            // à l'intérieur de cette fonction, l'exception sera vraiment lancée
            // et pas simplement passée comme paramètre à un prochain callback

            // L'exception sera lancée de manière asynchrone, donc si elle vous intéresse
            // il faut catcher les exceptions de la dernière chance au niveau de l'app

            // Essayez de dé-commenter cette ligne pour voir.
            //return result.callANonExistingMethod();
        }, function (error) {
            // Ici on peut éventuellement gérer une erreur renvoyée par l'appel asynchrone précédent.
            var resultError = error;
        });

Pour être sûr de catcher les exceptions asynchrone, un petit coup de WinJS pour ajouter un eventhandler au niveau de l'app :

    var app = WinJS.Application;
    app.onerror = function (error) {
        //Log the last-chance exception 
    };

Enfin, sachez que dans le cas où vous terminez par done(), si vous ne fournissez pas de callback pour le onError, et que la Promise se termine avec une erreur, cette erreur sera automatiquement forwardée, et donc une exception de la dernière chance sera déclenchée !

 

[Windows 8] C# / XAML : FlipView, Context Indicator et Prévisualisation

4. December 2012 16:12 by Renaud in Windows 8  //  Tags: , ,   //   Comments (2)

Dans cet article on va voir comment reproduire le Context Indicator que l'on peut voir dans le Store. Cet indicateur, c'est ce que vous pouvez voir en dessous du flipview montrant les différents screenshots de l'application, et qui vous donne une idée de l'endroit où vous vous trouvez dans la liste.

Indicateur dans le store

Reproduire l'indicateur du store

Pour y arriver, on va modifier légèrement le template du FlipView. Pour ça, il suffit d'utiliser Blend et de modifier une copie du template existant.

On va ensuite  y ajouter une ListView :

Une fois qu'on a cette ListView, on va faire plusieurs choses :

  • Binder la propriété ItemsSource avec le FlipView pour avoir le même nombre d'éléments dans chaque liste
ItemsSource="{TemplateBinding ItemsSource}"
  • Binder la propriété SelectedItem avec le FlipView  pour les synchroniser
SelectedItem="{Binding SelectedItem, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
  • Modifier l'orientation de la liste en activant le scroll horizontal et en changeant l'orientation du StackPanel qui va contenir les items.
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"

Le panel :

    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel  Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
  • Et pour finir, on va sauvagement modifier le style des items et des items containers. On va vraiment faire un gros nettoyage pour éviter d'avoir l'habituelle bordure mauve lorsqu'un élément est sélectionné. On va garder le strict minium et faire en sorte d'avoir un rectangle gris par défaut, et blanc lorsque l'élément est sélectionné. Je vous épargne le code, mais vous pouvez le trouver dans le projet téléchargeable à la fin de l'article :)

Et voilà le résultat :

Aller plus loin et créer une barre de preview !

L'idée est d'utiliser cet indicateur pour donner plus d'infos à l'utilisateur, et lui permettre de l'utiliser même pour naviguer dans le flipview ! Pour vous donner une idée du résultat :)

Pour faire ça, on va principalement modifier le ItemTemplate de la ListView :

<ListView.ItemTemplate>
    <DataTemplate>
        <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
            <Image Source="{Binding ImageUri}" Stretch="UniformToFill"/>
        </Grid>
    </DataTemplate>
</ListView.ItemTemplate>

Si l'on s'arrête là, lorsque l'on se déplace à partir du FlipView, on modifie bien l'item sélectionné dans la ListView. Mais si l'on se déplace trop loin, il se peut que cet item se retrouve en dehors du champs de vision ! Pour éviter ça, on va écouter l'événement SelectionChanged de la ListView, et faire en sorte que l'item sélectionné soit toujours visible.

private void ListViewSelectionChanged1(object sender, Windows.UI.Xaml.Controls.SelectionChangedEventArgs e)
{
    // Si un nouvel item est bien sélectionné
    if (e.AddedItems.Count > 0){
        // On scroll vers cet item !
        ((ListView)sender).ScrollIntoView(e.AddedItems.First());    }
}

Et voilà :) Les sources des deux projets sont téléchargeables sur mon Skydrive : http://sdrv.ms/SJv8PX

 

[Windows 8] C# / XAML : FlipView, Context Indicator & Items Preview

4. December 2012 15:01 by Renaud in Windows 8  //  Tags: , , ,   //   Comments (3)

Let's see how we can reproduce the Context Indicator that you can see in the Store application. This indicator is what you see behind the flipview that shows you the screenshots of an app, and that tells what is your position in that list.

 Indicator for the Wikipedia app in the Store.

Reproduce the Store's Indicator

To achieve this, we will have to slighty modify the FlipView template. Open Blend and use the command "Edit Template > Edit a copy...

 

Add a ListView to it in the visual tree of the FlipView. 

 

Now that we have that list, we can do a few things :

  • Bind the ItemsSource with the parent FlipView so that we have the same quantity of elements in each list
ItemsSource="{TemplateBinding ItemsSource}"
  • Bind the SelectedItem property with the FlipView to make sure both are syncrhonized together
SelectedItem="{Binding SelectedItem, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
  • Set the orientiation and the scrolling horizontal
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"

The stackpanel :

    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel  Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
  • And finally, we will dramaticaly change the items style and the items containers style. We clean them up to remove the purple border on the selected item. We just keep what we need : a gray-filled border which becomes white when the item is selected. I wont copy/paste the code here but you can grab it at the end of this post.

And here is the result : 

Go further with a preview bar !

Now the idea is to use that indicator to give more information to the user. Let's say you have a big list and you want to show a preview with a thumbnail, so that it's easier to navigate throught the flipview. Here is what it would look like :) 

To achieve that, we just have to modify the ItemTemplate of the ListView :

<ListView.ItemTemplate>
    <DataTemplate>
        <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
            <Image Source="{Binding ImageUri}" Stretch="UniformToFill"/>
        </Grid>
    </DataTemplate>
</ListView.ItemTemplate>

If we stop here, we will just miss a thing : when you navigate in the list using the FlipView itself, you can see that the selected item in the ListView is well syncrhonized. But if you go to far in the list, the selected item may be out of the field of view. It's easy to avoid that, by listening to the SelectionChanged event on the ListView, and each time an item is selected you ask the ListView to scroll and make sure the item is visible !

private void ListViewSelectionChanged1(object sender, Windows.UI.Xaml.Controls.SelectionChangedEventArgs e)
{
    // Si un nouvel item est bien sélectionné
    if (e.AddedItems.Count > 0){
        // On scroll vers cet item !
        ((ListView)sender).ScrollIntoView(e.AddedItems.First());    }
}

And there you are :) You can grab the bits of those two projects on my Skydrive : http://sdrv.ms/SJv8PX

[Windows 8] C# / XAML : Drag & Drop (English)

1. October 2012 11:10 by Renaud in Windows 8  //  Tags: , , ,   //   Comments (19)

 It's really easy to move items from a list in a Windows 8 app. We'll see in this blog how we can start dragging items in a few lines of code, keep track of the item being dragged, and finally how to handle the drop of that item. You probably noticed that in the XAML now (something that didn't exist in Silverlight for Windows Phone 7), you can say, for some events, whether you want them enabled or not. For example, on a GridView, you'll find properties such as IsItemClickEnabled, IsRightTapEnabled, ... But there is also one property that will be really useful for now. It's called CanDragItems, and we will use it in addition to the DragItemsStarting event. Those properties are  inherited from the ListViewBase class.

 

What are we going to drag ?

To make it as simple as possible, we'll use two GridView. We could make it also with ListView as it also inherits from ListViewBase. We will simply display two collections, and make it possible for the user to move items from a list to the other using drag and drop. In a blank Page, I add those two GirdViews, that are quite similar :

<GridView   ItemTemplate="{StaticResource ItemTemplate1}"
            ItemsSource="{Binding FirstCollection}"
            AllowDrop="True" CanDragItems="True" 
            DragItemsStarting="GridViewDragItemsStarting"
            Drop="GridViewDrop" Margin="10">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
</GridView>

<GridView   ItemTemplate="{StaticResource ItemTemplate2}"
            ItemsSource="{Binding SecondCollection}"
            AllowDrop="True" CanDragItems="True" 
            DragItemsStarting="GridViewDragItemsStarting"
            Drop="GridViewDrop" Margin="10">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
</GridView>

The only differences are the templates used (ItemTemplate1 and ItemTemplate2), and the bound collections (FirstCollection and SecondCollection). Notice alose those properties :

  • CanDragItems / DragItemStarting
  • AllowDrop / Drop
We explicitly enable the possibility to move items, while listening to the event fired the user actually starts moving an object. And then, we also activate the ability to "drop", and we listen to the corresponding event to know when an item is dropped on one of the GridViews.
 

Handle the drag and the drop !

Now that the UI can display our collections, we will implement the eventhandlers for DragItemsStarted and Drop. In this case, we will make it simple and we will use the same handlers for both grid so that the user can move items from one grid to the other and vice versa.

When you start moving an item, you end up with a DragItemsStartingEventArgs, which has some interesting properties :

  • Items, a collection containing the dragged items.
  • Data, which is nothing less than a DataPackage object. The same as the one used by the Share charm for data transfer between apps.
        private void GridViewDragItemsStarting(object sender, DragItemsStartingEventArgs e)
        {
            var item = e.Items.FirstOrDefault();
            if (item == null)
                return;

            e.Data.Properties.Add("item", item);
            e.Data.Properties.Add("gridSource", sender);
        }

Here we retrieve the first dragged item, if any, and we store it in the DataPackage, which will act as a context during the whole operation. We also keep track of the "sender" grid, to know where that item comes from.

Then comes the moment you drop the item. And if the user drop it on one of the grid, the following event handler will be executed :

private void GridViewDrop(object sender, DragEventArgs e)
{
    object gridSource;
    e.Data.Properties.TryGetValue("gridSource", out gridSource);

    if (gridSource == sender)
        return;

    object sourceItem;
    e.Data.Properties.TryGetValue("item", out sourceItem);
    if (sourceItem == null)
        return;

    _mainViewModel.SwitchItem((DemoItem)sourceItem);
}

In that piece of code, we compare the source grid with the target grid. If they are the same, then we don't have nothing to do, the item will simply stays where it is. Otherwise, we will get back the item that was dragged, thanks to the DataPackage, and we will put it in the other collection (that's what does the SwitchItem method). And that's all. You don't need nothing more to use drag & drop between two collections. Animations are automatically added, and the result is quite nice ! 

Conclusion

It has never been that easy to use drag and drop ! Don't hesitate to download the sample project if something seems fluzzy :

 

 

[Windows 8 / XAML] Modifier le style d'un FlipView

22. September 2012 19:09 by Renaud in Windows 8  //  Tags: , , ,   //   Comments (7)

Lors d'un Excellence Lab passé récemment, on m'a conseillé de modifier légèrement mon FlipView de sorte que les flèches de navigation vers les items suivants ou précédents ne se superposent pas au contenu. Il suffisait donc de légèrement ré-hausser les flèches pour régler ce problème. Un collègue m'a également posé une question pour savoir comment modifier la couleur de ces flèches, pour répondre à la demande d'un client. J'imagine que la personne a cherché après une propriété "NavigationTemplate", ou quelque chose du genre, un peu comme on retrouve un ItemTemplate En fait, les boutons de navigation font directement partie du template du FlipView. Pour s'en rendre compte, il suffit d'utiliser Blend !

En créant un projet simple, contenant un FlipView et en l'ouvrant dans Blend, il est possible d'éditer une "copie" du template actuel:

Éditer une copie du template

Si on regarde ensuite le XAML, on peut voir que Blend a généré un Style représentant le FlipView, avec tous les Visual States nécessaires pour faire le FlipView tel qu'on le connait. Voici le code complet :

      <Style x:Key="FlipViewStyle1" TargetType="FlipView">
        	<Setter Property="Background" Value="Transparent"/>
        	<Setter Property="BorderThickness" Value="0"/>
        	<Setter Property="TabNavigation" Value="Once"/>
        	<Setter Property="IsTabStop" Value="False"/>
        	<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
        	<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/>
        	<Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False"/>
        	<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="False"/>
        	<Setter Property="ScrollViewer.IsHorizontalScrollChainingEnabled" Value="True"/>
        	<Setter Property="ScrollViewer.IsVerticalScrollChainingEnabled" Value="True"/>
        	<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False"/>
        	<Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True"/>
        	<Setter Property="ItemsPanel">
        		<Setter.Value>
        			<ItemsPanelTemplate>
        				<VirtualizingStackPanel AreScrollSnapPointsRegular="True" Orientation="Horizontal"/>
        			</ItemsPanelTemplate>
        		</Setter.Value>
        	</Setter>
        	<Setter Property="Template">
        		<Setter.Value>
        			<ControlTemplate TargetType="FlipView">
        				<Grid>
        					<VisualStateManager.VisualStateGroups>
        						<VisualStateGroup x:Name="FocusStates">
        							<VisualState x:Name="Focused">
        								<Storyboard>
        									<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisualWhite"/>
        									<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisualBlack"/>
        								</Storyboard>
        							</VisualState>
        							<VisualState x:Name="Unfocused"/>
        							<VisualState x:Name="PointerFocused"/>
        						</VisualStateGroup>
        					</VisualStateManager.VisualStateGroups>
        					<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="3">
        						<Border.Resources>
        							<ControlTemplate x:Key="HorizontalNextTemplate" TargetType="Button">
        								<Border x:Name="Root" BorderBrush="{StaticResource FlipViewButtonBorderThemeBrush}" BorderThickness="{StaticResource FlipViewButtonBorderThemeThickness}" Background="{StaticResource FlipViewButtonBackgroundThemeBrush}">
        									<VisualStateManager.VisualStateGroups>
        										<VisualStateGroup x:Name="CommonStates">
        											<VisualState x:Name="Normal"/>
        											<VisualState x:Name="PointerOver">
        												<Storyboard>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverBackgroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverBorderThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Arrow">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverForegroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        												</Storyboard>
        											</VisualState>
        											<VisualState x:Name="Pressed">
        												<Storyboard>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedBackgroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedBorderThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Arrow">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedForegroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        												</Storyboard>
        											</VisualState>
        										</VisualStateGroup>
        									</VisualStateManager.VisualStateGroups>
        									<Path x:Name="Arrow" Data="M4.12,0 L9.67,5.47 L4.12,10.94 L0,10.88 L5.56,5.47 L0,0.06 z" Fill="{StaticResource FlipViewButtonForegroundThemeBrush}" HorizontalAlignment="Center" Height="10.94" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Center" Width="9.67"/>
        								</Border>
        							</ControlTemplate>
        							<ControlTemplate x:Key="HorizontalPreviousTemplate" TargetType="Button">
        								<Border x:Name="Root" BorderBrush="{StaticResource FlipViewButtonBorderThemeBrush}" BorderThickness="{StaticResource FlipViewButtonBorderThemeThickness}" Background="{StaticResource FlipViewButtonBackgroundThemeBrush}">
        									<VisualStateManager.VisualStateGroups>
        										<VisualStateGroup x:Name="CommonStates">
        											<VisualState x:Name="Normal"/>
        											<VisualState x:Name="PointerOver">
        												<Storyboard>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverBackgroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverBorderThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Arrow">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverForegroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        												</Storyboard>
        											</VisualState>
        											<VisualState x:Name="Pressed">
        												<Storyboard>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedBackgroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedBorderThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Arrow">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedForegroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        												</Storyboard>
        											</VisualState>
        										</VisualStateGroup>
        									</VisualStateManager.VisualStateGroups>
        									<Path x:Name="Arrow" Data="M5.55,0 L9.67,0.06 L4.12,5.47 L9.67,10.88 L5.55,10.94 L0,5.48 z" Fill="{StaticResource FlipViewButtonForegroundThemeBrush}" HorizontalAlignment="Center" Height="10.94" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Center" Width="9.67"/>
        								</Border>
        							</ControlTemplate>
        							<ControlTemplate x:Key="VerticalNextTemplate" TargetType="Button">
        								<Border x:Name="Root" BorderBrush="{StaticResource FlipViewButtonBorderThemeBrush}" BorderThickness="{StaticResource FlipViewButtonBorderThemeThickness}" Background="{StaticResource FlipViewButtonBackgroundThemeBrush}">
        									<VisualStateManager.VisualStateGroups>
        										<VisualStateGroup x:Name="CommonStates">
        											<VisualState x:Name="Normal"/>
        											<VisualState x:Name="PointerOver">
        												<Storyboard>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverBackgroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverBorderThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Arrow">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverForegroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        												</Storyboard>
        											</VisualState>
        											<VisualState x:Name="Pressed">
        												<Storyboard>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedBackgroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedBorderThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Arrow">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedForegroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        												</Storyboard>
        											</VisualState>
        										</VisualStateGroup>
        									</VisualStateManager.VisualStateGroups>
        									<Path x:Name="Arrow" Data="M0.06,0 L5.47,5.56 L10.88,0 L10.94,4.12 L5.48,9.67 L0,4.12 z" Fill="{StaticResource FlipViewButtonForegroundThemeBrush}" HorizontalAlignment="Center" Height="9.67" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Center" Width="10.94"/>
        								</Border>
        							</ControlTemplate>
        							<ControlTemplate x:Key="VerticalPreviousTemplate" TargetType="Button">
        								<Border x:Name="Root" BorderBrush="{StaticResource FlipViewButtonBorderThemeBrush}" BorderThickness="{StaticResource FlipViewButtonBorderThemeThickness}" Background="{StaticResource FlipViewButtonBackgroundThemeBrush}">
        									<VisualStateManager.VisualStateGroups>
        										<VisualStateGroup x:Name="CommonStates">
        											<VisualState x:Name="Normal"/>
        											<VisualState x:Name="PointerOver">
        												<Storyboard>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverBackgroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverBorderThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Arrow">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPointerOverForegroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        												</Storyboard>
        											</VisualState>
        											<VisualState x:Name="Pressed">
        												<Storyboard>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedBackgroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Root">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedBorderThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        													<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="Arrow">
        														<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource FlipViewButtonPressedForegroundThemeBrush}"/>
        													</ObjectAnimationUsingKeyFrames>
        												</Storyboard>
        											</VisualState>
        										</VisualStateGroup>
        									</VisualStateManager.VisualStateGroups>
        									<Path x:Name="Arrow" Data="M5.63,0 L11.11,5.55 L11.05,9.67 L5.64,4.12 L0.23,9.67 L0.17,5.55 z" Fill="{StaticResource FlipViewButtonForegroundThemeBrush}" HorizontalAlignment="Center" Height="9.67" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Center" Width="10.94"/>
        								</Border>
        							</ControlTemplate>
        						</Border.Resources>
        						<Grid>
        							<ScrollViewer x:Name="ScrollingHost" BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalSnapPointsType="MandatorySingle" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsTabStop="False" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}" IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" Padding="{TemplateBinding Padding}" TabNavigation="{TemplateBinding TabNavigation}" VerticalSnapPointsType="MandatorySingle" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled">
        								<ItemsPresenter/>
        							</ScrollViewer>
        							<Button x:Name="PreviousButtonHorizontal" HorizontalAlignment="Left" Height="40" IsTabStop="False" Template="{StaticResource HorizontalPreviousTemplate}" VerticalAlignment="Center" Width="70"/>
        							<Button x:Name="NextButtonHorizontal" HorizontalAlignment="Right" Height="40" IsTabStop="False" Template="{StaticResource HorizontalNextTemplate}" VerticalAlignment="Center" Width="70"/>
        							<Button x:Name="PreviousButtonVertical" HorizontalAlignment="Center" Height="40" IsTabStop="False" Template="{StaticResource VerticalPreviousTemplate}" VerticalAlignment="Top" Width="70"/>
        							<Button x:Name="NextButtonVertical" HorizontalAlignment="Center" Height="40" IsTabStop="False" Template="{StaticResource VerticalNextTemplate}" VerticalAlignment="Bottom" Width="70"/>
        						</Grid>
        					</Border>
        					<Rectangle x:Name="FocusVisualWhite" IsHitTestVisible="False" Opacity="0" StrokeDashOffset="1.5" StrokeEndLineCap="Square" Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}" StrokeDashArray="1,1"/>
        					<Rectangle x:Name="FocusVisualBlack" IsHitTestVisible="False" Opacity="0" StrokeDashOffset="0.5" StrokeEndLineCap="Square" Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}" StrokeDashArray="1,1"/>
        				</Grid>
        			</ControlTemplate>
        		</Setter.Value>
        	</Setter>
        </Style>

Dans ce style, on retrouve:

  •  4 boutons : deux pour le flip horizontal, et deux pour le flip vertical.
    • PreviousButtonHorizontal
    • NextButtonHorizontal
    • PreviousButtonVertical
    • NextButtonVertical
  • 4 ControlTemplate : un pour chaque bouton
    • HorizontalNextTemplate
    • HorizontalPreviousTemplate
    • VerticalNextTemplate
    • VerticalPreviousTemplate

Vous n'avez plus qu'à jouer sur ce XAML pour arriver à ce que vous voulez :) Encore une fois, vous allez pouvoir apprécier mes talents et mon bon goût digne des graphistes les plus skillés !

Navigation ré-haussée avec du texte

 

Vous pouvez télécharger ce sample ici:

 

L’App’ero Windows 8 au MIC

28. August 2012 15:08 by Renaud in Events, Windows 8  //  Tags: , ,   //   Comments (0)

Je vous avais parlé dans un article sur  la rentrée des développeurs Microsoft du fait qu'on allait continuer à organiser des évènements au Microsoft Innovation Center Belgique. Alors on va commencer tout de suite !

Inspiré et motivé par les events organisés en France par la Communauté des Développeurs Windows 8, on a voulu en faire autant dans notre région Wallonne. N'hésitez pas à vous inscrire, c'est gratuit et ouvert à tous.

Venez nous montrer ce que vous savez faire, ou venez voir ce que les autres ont déjà fait. Notez qu'il est toujours tant de développer quelque chose juste pour l'occasion, et pour le fun :)

Téléchargez les outils maintenant (français)


L’App’ero Windos 8 est une initiative qui vise à faire découvrir la nouvelle plateforme de Microsoft d’un point de vue technique ! L’objectif est de rassembler des professionnels ou passionnés de l’IT, développeurs ou administrateurs systèmes, qui ont déjà ou ont l’intention de se lancer dans l’aventure Windows 8.

L’événement se tiendra au Microsoft Innovation Center de Mons, lors d’une soirée mêlant Apps Windows 8, et apéro ! :)

L'App'ero Windows 8

Les participants sont attendus le 26 septembre dès 18h. Des speakers (annoncés prochainement) viendront présenter Windows 8, ses particularités, et ses fonctionnalités, sous différents angles : développeur ou administrateur système.

Comme l’idée de l’événement est de rencontrer d’autres passionnés et de renforcer notre communauté en Wallonie, un concours sera organisé durant la soirée. Toute personne ayant développé une application Windows 8 pourra prendre la parole et présenter son travail sur une tablette Windows 8 aux autres participants qui voteront ensuite à main levée pour désigner la meilleure application. Le ou les gagnants seront sacrés Windows 8 App’Héros et gagneront le respect de la communauté ! :)

Pour vous inscrire, rendez-vous sur la page de l’événement : http://www.win8appero.be, et rejoignez-nous sur Twitter ou Facebook pour être tenu informé des nouveautés dans le programme et connaître les noms des speakers ;)

TextBox

About the author

I'm a developer, blog writer, and author, mainly focused on Microsoft technologies (but not only Smile). I'm Microsoft MVP Client Development since July 2013.

Microsoft Certified Professional

I'm currently working as an IT Evangelist with an awesome team at the Microsoft Innovation Center Belgique, where I spend time and energy helping people to develop their projects. I also give training to enthusiastic developers and organize afterworks with the help of the Belgian community.

MIC Belgique

Take a look at my first book (french only): Développez en HTML 5 pour Windows 8

Développez en HTML5 pour Windows 8

Membre de l'association Fier d'être développeur

TextBox

Month List