Windows 10 UWP App Development ebook

3.165 visualizações

Publicada em

Window 10 UWP App Development ebook, Begineer Level.

Publicada em: Tecnologia
  • Seja o primeiro a comentar

Windows 10 UWP App Development ebook

  1. 1. 1 Windows 10 Universal Windows Platform (UWP) I. CONTROLES..............................................................................................................................................3 1. RELATIVEPANEL.............................................................................................................................................. 3 2. SPLITVIEW..................................................................................................................................................... 5 a. SplitView de base et Adaptive Triggers ................................................................................................. 5 b. Bouton Rechercher et AutoSuggestBox dans SplitView......................................................................... 8 c. « Page Header » : Contrôle utilisateur « Barre de titre » avec marge adaptable selon le mode d’affichage du SplitView............................................................................................................................... 10 3. NAVIGATION MODEL...................................................................................................................................... 13 4. BACKBUTTON............................................................................................................................................... 16 5. COMMANDBAR, SYMBOLICON, ….................................................................................................................... 17 6. MEDIA ELEMENT ET CUSTOM MEDIA TRANSPORT CONTROLS ................................................................................. 20 7. CONTENT DIALOG ......................................................................................................................................... 21 8. AUTOSUGGESTBOX (« REMPLAÇANT DE SEARCHBOX »)....................................................................................... 22 9. POPUP........................................................................................................................................................ 23 10. EXTENDED SPASHSCREEN........................................................................................................................... 23 11. VARIABLESIZEDWRAPGRID......................................................................................................................... 25 II. ACCENT ET THEME (DARK, LIGHT).......................................................................................................... 27 DÉFINIR SON PROPRE THÈME.................................................................................................................................... 27 III. DESIGN TIME.......................................................................................................................................... 28 IV. DATATEMPLATESELECTOR ................................................................................................................. 29  DERNIER ELEMENT « AFFICHER PLUS » POUR LISTVIEW OU GRIDVIEW.................................................................... 31 V. HEADERGROUP...................................................................................................................................... 34 DATATEMPLATES ........................................................................................................................................... 36 POUR LISTVIEW..................................................................................................................................................... 36 POUR GRIDVIEW ................................................................................................................................................... 38 VI. ADAPTIVE UI...................................................................................................................................... 40 1. ADAPTIVE TRIGGERS...................................................................................................................................... 41 2. MASTER DETAILS................................................................................................................................................ 43 3. CUSTOM ADAPTIVE TRIGGERS .......................................................................................................................... 45 4. DEVICEFAMILY ............................................................................................................................................. 48 5. ASTUCE : REACTIVER LES ANIMATIONS, TRANSITIONS WINDOWS ............................................................................ 48 VII. X :BIND « COMPILED BINDING »........................................................................................................ 49 1. BINDING DE COLLECTION ................................................................................................................................ 49 2. AVEC DATATEMPLATE.................................................................................................................................... 50 3. AVEC DICTIONNAIRE DE RESSOURCES (RESOURCEDICTIONARY)............................................................................... 50 4. « RELATIVESOURCE » ET « ELEMENTNAME » … LIER AU NOM DE L’ELEMENT........................................................... 51 5. « SOURCE » ET « DATACONTEXT ».. AJOUTER UNE PROPRIETE DU VIEWMODEL DANS LE CODE-BEHIND ....................... 51 6. BINDING EVENTS .......................................................................................................................................... 52 7. DEFER LOADING............................................................................................................................................ 53
  2. 2. 2 VIII. APPLICATION LIFECYCLE..................................................................................................................... 53 1. HISTORIQUE DE NAVIGATION (« APP ») ............................................................................................................ 54 2. ETATS DE LA PAGE ET DONNEES........................................................................................................................ 54 IX. TILES, TOASTS ........................................................................................................................................ 56 TILE .................................................................................................................................................................... 56 Adaptive Tiles ............................................................................................................................................... 59 TOAST ................................................................................................................................................................. 60 Adaptive toast .............................................................................................................................................. 60 Documentation, exemples (page de tous les exemples de codes) Modèles d’application (Visual Studio 2015) Avec Windows 10, le modèle d’application universelle permet de créer à partir d’un seul projet une application pour le Windows Store (PC, tablettes et ordinateurs portables) et une application pour le Windows Phone Store. Modèle « Universel » Windows 10 (UWP) Modèles d’applications Windows, Windows Phone et « Universel » (avec projet « Shared ») Windows 8 Modèle « Universel Windows 8 » : 3 projets (un projet Windows Store, un Windows Phone et un « Shared ») Modèle « Universel Windows 10 » (UWP) : 1 seul projet
  3. 3. 3 I. Contrôles Liste des contrôles et exemples Voir l’exemple « XamlUIBasics » 1. RelativePanel Permet de positionner les éléments les uns par rapport aux autres et de gérer le repositionnement des éléments avec des AdaptiveTriggers. Propriétés de placement Placement par rapport à un élément  Above : Au-dessus de l’élément  Below : En-dessous de l’élément  LeftOf : A gauche de l’élément  RightOf : A droite de l’élément Plus  AlignHorizontalCenterWith  AlignVerticalCenterWith  AlignBottomWith  AlignTopWith  AlignLeftWith  AlignRightWith Exemple <RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Rectangle x:Name="rectangle1" Fill="Red" Width="300" Height="150" /> <Rectangle x:Name="rectangle2" Fill="Blue" RelativePanel.RightOf="rectangle1" Width="300" Height="150" /> <Rectangle x:Name="rectangle3" Fill="Green" RelativePanel.Below="rectangle2" RelativePanel.AlignHorizontalCenterWith="rectangle2" Width="300" Height="150" /> </RelativePanel> Rectangle bleu placé à droite du rectangle rouge Rectangle vert placé en-dessous du rectangle bleu (Below) et aligné avec celui-ci (AlignHorizontalCenterWith)
  4. 4. 4 Placement par rapport au RelativePanel  AlignTopWithPanel : En haut (à gauche si pas précisé) du RelativePanel  AlignBottomWithPanel : En bas (et à gauche si pas précisé) du RelativePanel  AlignLeftWithPanel : A gauche (et en haut si pas précisé) du RelativePanel  AlignRightWithPanel : A droite (et en haut si pas précisé) du RelativePanel Plus pour centrer horizontalement et verticalement un élément par rapport au RelativePanel  AlignHorizontalCenterWithPanel  AlignVerticalCenterWithPanel <RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Rectangle x:Name="rectangle1" Fill="Red" Width="300" Height="150" /> <Rectangle x:Name="rectangle2" RelativePanel.AlignRightWithPanel="True" Fill="Blue" Width="300" Height="150" /> <Rectangle x:Name="rectangle3" Fill="Green" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignRightWithPanel="True" Width="300" Height="150" /> </RelativePanel> Rectangle bleu placé à droite du RelativePanel (AlignRightWithPanel) Par défaut les éléments sont placés à gauche en haut du RelativePanel Rectangle vert placé à droite (AlignRightWithPanel) et en bas (AlignBottomWithPanel)du RelativePanel
  5. 5. 5 2. SplitView a. SplitView de base et Adaptive Triggers Exemple DisplayMode - Overlay : par-dessus le contenu quand ouvert et caché fermé - CompactOverlay : par-dessus le contenu quand ouvert et avec une barre quand fermé - Inline : ancré ouvert, caché fermé - CompactInline : ancré ouvert et avec une barre quand fermé <SplitView> <SplitView.Pane> <!-- menu --> </SplitView.Pane> <SplitView.Content> <!-- content--> </SplitView.Content> </SplitView> De 720 à 1024, « CompactInline » et panel fermé Les « Adaptive Triggers » permettent de basculer le mode d’affichage du splitview selon la taille de la page. Plus de1024 « CompactInline », panel ouvert En-dessous 720, Mode d’affichage « Overlay » Le bouton hamburger permet en plus d’ouvrir, fermer le panel. Il est placé en dehors du splitview pour ne pas être caché quand le panel est fermé en mode « Overlay »
  6. 6. 6 SplitView <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualState x:Name="VisualStateMin0"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="0" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="splitView.DisplayMode" Value="Overlay"/> <Setter Target="splitView.IsPaneOpen" Value="False"/> </VisualState.Setters> </VisualState> <VisualState x:Name="VisualStateMin720"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="720" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="splitView.DisplayMode" Value="CompactInline"/> <Setter Target="splitView.IsPaneOpen" Value="False"/> </VisualState.Setters> </VisualState> <VisualState x:Name="VisualStateMin1024"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="1024" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="splitView.DisplayMode" Value="CompactInline"/> <Setter Target="splitView.IsPaneOpen" Value="True"/> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <SplitView x:Name="splitView" DisplayMode="CompactInline" IsPaneOpen="True" OpenPaneLength="320"> <SplitView.Pane> <ListView Margin="0,48,0,0" VerticalAlignment="Stretch" ItemsSource="{x:Bind ViewModel.MenuItems}" ItemTemplate="{StaticResource MenuItemTemplate}" SelectionMode="None" IsItemClickEnabled="True"/> </SplitView.Pane> <SplitView.Content> <Frame x:Name="mainFrame"></Frame> </SplitView.Content> </SplitView> <Button Name="splitViewButton" Style="{StaticResource Square48x48ButtonStyle}" VerticalAlignment="Top" Click="splitViewButton_Click"> <FontIcon FontFamily="{ThemeResource ContentControlThemeFontFamily}" Glyph="≡" FontSize="32" Margin="0,-8,0,0"/> </Button> </Grid> On peut définir ce que l’on veut en contenu .De plus on peut omettre « SplitView.Content » Bouton hamburger placé en dernier Marge de 48 par rapport au top pour laisser la place au bouton hamburger Adaptive Triggers, on change le mode d’affichage du splitview selon la taille de la page
  7. 7. 7 Template <DataTemplate x:Key="MenuItemTemplate" x:DataType="local:MenuItem"> <StackPanel Orientation="Horizontal" Margin="2,0,0,0"> <SymbolIcon Symbol="{x:Bind Symbol}"/> <TextBlock Text="{x:Bind Title}" Margin="24,0,0,0" VerticalAlignment="Center"/> </StackPanel> </DataTemplate> Code-behind public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); ViewModel = new MainPageViewModel(); } public MainPageViewModel ViewModel { get; set; } private void splitViewButton_Click(object sender, RoutedEventArgs e) { splitView.IsPaneOpen = !splitView.IsPaneOpen; } } Classe utilisée pour le menu public class MenuItem { public Symbol Symbol { get; set; } public string Title { get; set; } public MenuItem(string title, Symbol symbol) { Title = title; Symbol = symbol; } } On remplit la collection public class MainPageViewModel { public ObservableCollection<MenuItem> MenuItems { get; set; } public MainPageViewModel() { MenuItems = new ObservableCollection<MenuItem>(); MenuItems.Add(new MenuItem("Accueil", Symbol.Home)); MenuItems.Add(new MenuItem("Vidéos", Symbol.Video)); MenuItems.Add(new MenuItem("Musiques", Symbol.Audio)); } }
  8. 8. 8 b. Bouton Rechercher et AutoSuggestBox dans SplitView <SplitView x:Name="splitView" DisplayMode="CompactInline" IsPaneOpen="True" OpenPaneLength="320"> <SplitView.Pane> <StackPanel Margin="0,48,0,0"> <Grid Height="48"> <AutoSuggestBox x:Name="autoSuggestBox" Margin="12,0" PlaceholderText="Rechercher" VerticalAlignment="Center" QueryIcon="Find" Visibility="{Binding IsPaneOpen, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=splitView}" /> <Button x:Name="searchButton" Style="{StaticResource Square48x48ButtonStyle}" Visibility="{Binding IsPaneOpen, Converter={StaticResource BooleanToCollapsedConverter}, ElementName=splitView}" Click="searchButton_Click"> <SymbolIcon Symbol="Find" /> </Button> </Grid> <ListView ItemsSource="{x:Bind ViewModel.MenuItems}" ItemTemplate="{StaticResource MenuItemTemplate}" SelectionMode="None" IsItemClickEnabled="True"/> </StackPanel> </SplitView.Pane> <SplitView.Content> <Frame x:Name="mainFrame"></Frame> </SplitView.Content> </SplitView> Il suffit en plus de permettre au clic sur le bouton rechercher d’ouvrir le panel private void searchButton_Click(object sender, RoutedEventArgs e) { splitView.IsPaneOpen = true; } Style du bouton On lie la visibilité de l’AutoSuggestBox et du bouton rechercher à SplitView IsPaneOpen et on utilise des converters AutoSuggestBox quand le panel est ouvert Bouton quand le panel est fermé On fait en sorte que chaque élément fasse 48px de haut
  9. 9. 9 <Style x:Key="Square48x48ButtonStyle" TargetType="Button"> <Setter Property="Background" Value="Transparent" /> <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}" /> <Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" /> <Setter Property="Height" Value="48" /> <Setter Property="Width" Value="48" /> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="UseSystemFocusVisuals" Value="True" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="RootGrid" Background="{TemplateBinding Background}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListLowBrush}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListMediumBrush}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ContentPresenter x:Name="Content" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" AutomationProperties.AccessibilityView="Raw" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> Bouton 48x48 avec contenu centré
  10. 10. 10 c. « Page Header » : Contrôle utilisateur « Barre de titre » avec marge adaptable selon le mode d’affichage du SplitView Création d’un UserControl « PageHeader » qui pourra être glissé sur chaque page de contenu (affichée dans la zone de contenu du SplitView) . On pourra personnaliser le contenu de cette barre de titre pour chaque page, mais surtout permettra de gérer la marge entre le titre et le bouton hamburger du splitview. En effet, en mode d’affichage Overlay, le titre (s’il est aligné à gauche) et le bouton hamburger risqueraient de se chevaucher, il faut donc gérer la marge. <UserControl x:Class="UWPNavigationDemo.Controls.PageHeader" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UWPNavigationDemo.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Height="48" d:DesignHeight="300" d:DesignWidth="400"> <Grid> <Grid x:Name="titleBar"> <ContentPresenter x:Name="content" VerticalAlignment="{x:Bind VerticalContentAlignment}" HorizontalAlignment="{x:Bind HorizontalContentAlignment}" Margin="{x:Bind Padding}" Content="{x:Bind HeaderContent}"/> </Grid> </Grid> </UserControl> Code-behind du contrôle public sealed partial class PageHeader : UserControl { public PageHeader() { this.InitializeComponent(); EasyMessenger.Default.Subscribe<bool>("SplitView-DisplayMode-Overlay", (isOverlay)=> { if (isOverlay) { this.titleBar.Margin = new Thickness(overlayMargin, 0, 0, 0); } else { this.titleBar.Margin = new Thickness(defaultMargin, 0, 0, 0); } }); } private const int defaultMargin = 12; private const int overlayMargin = 48; public UIElement HeaderContent { get { return (UIElement)GetValue(HeaderContentProperty); } set { SetValue(HeaderContentProperty, value); } } public static readonly DependencyProperty HeaderContentProperty = DependencyProperty.Register("HeaderContent", typeof(UIElement), typeof(PageHeader), new PropertyMetadata(DependencyProperty.UnsetValue)); } Hauteur de 48 pour être aligné avec le bouton hamburger J’utilise un Messenger pour m’abonner au changement de « DisplayMode » du SplitView. SI le DisplayMode est « Overlay » . Je mets une grosse marge (48px) pour que le bouton hamburger et le titre ne se chevauchent pas Dependency Property permettant de personnaliser le contenu du contrôle selon chaque page
  11. 11. 11 Voir l’exemple XamlNavigation, qui n’utilise pas tout à fait la même méthode, mais il m’a semblé observer quelques soucis Dans le code-behind de MainPage public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); ViewModel = new MainPageViewModel(); SizeChanged += (s, e) => { CheckDisplayMode(); }; mainFrame.Navigated += (s, e) => { CheckDisplayMode(); }; // navigation mainFrame.Navigate(typeof(PageOne)); } private void CheckDisplayMode() { var displayMode = splitView.DisplayMode; if (displayMode == SplitViewDisplayMode.Overlay) { // messenger EasyMessenger.Default.Publish("SplitView-DisplayMode-Overlay", true); } else { EasyMessenger.Default.Publish("SplitView-DisplayMode-Overlay", false); } } // etc. } PageOne <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <Controls:PageHeader HorizontalAlignment="Left"> <Controls:PageHeader.HeaderContent> <TextBlock Text="Titre de la page" Style="{StaticResource TitleTextBlockStyle}" /> </Controls:PageHeader.HeaderContent> </Controls:PageHeader> <Grid Background="#ccc" Grid.Row="1"> </Grid> </Grid> Abonnement au changement de taille de la page et en fin de navigation de la frame « mainFrame ». On vérifie le mode d’affichage du SPlitView et notifie par Messenger Utilisation du contrôle, ajout d’un simple TextBlock avec un titre aligné à gauche
  12. 12. 12 Marge de 12 en « CompactInline » (panel ouvert et fermé) Marge de 48 en mode « Overlay » pour éviter que le titre et le bouton hamburger se chevauchent
  13. 13. 13 3. Navigation model Documentation Voir l’exemple XamlNavigation La navigation doit se faire dans deux sens. La barre de navigation doit être « synchronisée » (navigation retour) avec les pages affichées. public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); var MenuItems = new ObservableCollection<MenuItem>(); MenuItems.Add(new MenuItem("HomePage", "Accueil", Symbol.Home)); MenuItems.Add(new MenuItem("VideosPage", "Vidéos", Symbol.Video)) ; MenuItems.Add(new MenuItem("MusicsPage", "Musiques", Symbol.Audio)); menusListView.ItemsSource = MenuItems; mainFrame.Navigated += (s, e) => { // affiche le bouton if (mainFrame.CanGoBack) { SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible; } else { SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Collapsed; } // sélectionne le menu correspondant à la page if (e.NavigationMode == NavigationMode.Back) { string pageName = e.SourcePageType.Name; foreach (var item in menusListView.Items) { var menu = item as MenuItem; if (menu.Id == pageName) { menusListView.SelectedItem = item; return; } } } }; // Gère la navigation retour SystemNavigationManager.GetForCurrentView().BackRequested += (s, a) => { if (mainFrame.CanGoBack) { mainFrame.GoBack(); a.Handled = true; } }; } Navigation « Retour »
  14. 14. 14 private void ListView_ItemClick(object sender, ItemClickEventArgs e) { var menu = e.ClickedItem as MenuItem; if (menu.Id == "HomePage") { mainFrame.Navigate(typeof(HomePage)); } if (menu.Id == "VideosPage") { mainFrame.Navigate(typeof(VideosPage)); } if (menu.Id == "MusicsPage") { mainFrame.Navigate(typeof(MusicsPage)); } } protected override void OnNavigatedTo(NavigationEventArgs e) { // navigation mainFrame.Navigate(typeof(HomePage)); menusListView.SelectedIndex = 0; } } public class MenuItem { public string Id { get; set; } public Symbol Symbol { get; set; } public string Title { get; set; } public MenuItem(string id,string title, Symbol symbol) { Id = id; Title = title; Symbol = symbol; } } La ListView modifiée en sélection « Single » <ListView x:Name="menusListView" ItemTemplate="{StaticResource MenuItemTemplate}" SelectionMode="Single" IsItemClickEnabled="True" ItemClick="ListView_ItemClick"/> Navigation « Aller » Ajout d’un paramètre « Id » permettant de retrouver la page correspondante au menu
  15. 15. 15 Navigation « Aller » Navigation « Retour », après avoir cliqué sur le bouton retour de la barre de titre, le menu est sélectionné et synchronisé par rapport à la page affichée. Il faut garder en tête ici que c’est un scénario simple. On pourrait par exemple imaginer créer un paramètre spécifique à la navigation avec des propriétés permettant de mieux gérer celle-ci (exemple la page doit elle être prise en compte dans la navigation par la barre de menus?) On peut également observer Template10, qui crée un contrôle « HamburgerMenu ».
  16. 16. 16 4. BackButton Voir l’exemple BackButton Affichage d’un bouton dans la barre de titre (Desktop) Dans « App » pour « rootFrame » (ou dans le code de la page pour une Frame particulière) protected override void OnLaunched(LaunchActivatedEventArgs e) { // etc. Window.Current.Activate(); // Affiche ou masque le bouton retour de la barre de titre rootFrame.Navigated += (s, a) => { if (rootFrame.CanGoBack) { SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible; } else { SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Collapsed; } }; // Gère la navigation retour SystemNavigationManager.GetForCurrentView().BackRequested += (s, a) => { if (rootFrame.CanGoBack) { rootFrame.GoBack(); a.Handled = true; } }; } Page d’accueil Navigation vers une autre page, le bouton retour apparait dans la barre de titre. La couleur correspond à l’accent sélectionné des paramètres de personnalisation de Windows 10
  17. 17. 17 5. CommandBar, SymbolIcon, … Voir l’exemple XamlCommanding et XamlUIBasics CommandBar <CommandBar> <CommandBar.Content> <TextBlock Text="Titre" /> </CommandBar.Content> <AppBarButton Icon="Like" Label="Like" /> <!--etc.--> <!-- Separator --> <AppBarSeparator/> <!-- MenuFlyout --> <AppBarButton Icon="OpenWith" Label="Show Flyout"> <AppBarButton.Flyout> <MenuFlyout> <MenuFlyoutItem Text="Option 1"/> <MenuFlyoutItem Text="Option 2"/> </MenuFlyout> </AppBarButton.Flyout> </AppBarButton> <!-- AppBarToggleButton --> <AppBarToggleButton Icon="Contact" Label="Contact" IsChecked="True"/> <!-- Secondary --> <CommandBar.SecondaryCommands> <AppBarButton Icon="Setting" Label="Settings"/> </CommandBar.SecondaryCommands> </CommandBar> Menu Flyout (apparait au-dessus ou en dessous la barre selon l’espace disponible) On peut définir « IsOpen » pour afficher ou non les labels Les commandes secondaires et labels apparaissent quand on clique sur le bouton « … » « Content » à gauche de la barre Commandes « primaires » placées à droite de la barre Commandes « secondaires » placées dans menu ouvert avec le bouton « … »
  18. 18. 18 « IconElement » (SymbolIcon, FontIcon, ou PathIcon) <AppBarButton Icon="Like" Label="Like" /> SymbolIcon Liste des symboles disponibles <AppBarButton Label="Dislike"> <AppBarButton.Icon> <SymbolIcon Symbol="Dislike" Foreground="Red"/> </AppBarButton.Icon> </AppBarButton> FontIcon permet de définir un symbole qui n’est pas inclus dans SymbolIcon (on peut s’aider avec la « table des caractères » <AppBarButton Label="FontIcon"> <AppBarButton.Icon> <FontIcon FontFamily="Candara" Glyph="Σ"/> </AppBarButton.Icon> </AppBarButton> PathIcon <AppBarButton Label="PathIcon"> <AppBarButton.Icon> <PathIcon Data="F1 M 16,12 20,2L 20,16 1,16" HorizontalAlignment="Center"/> </AppBarButton.Icon> </AppBarButton> BitmapIcon <AppBarButton Label="BitmapIcon"> <AppBarButton.Icon> <BitmapIcon UriSource="ms-appx:///Assets/YouTube.png" /> </AppBarButton.Icon> </AppBarButton> Custom <AppBarButton Label="Custom" HorizontalContentAlignment="Center"> <Grid Width="48" Height="48" Margin="0,-8,0,-4"> <SymbolIcon Symbol="Memo"/> <TextBlock Text="2" Margin="0,2,0,0" Style="{StaticResource CaptionTextBlockStyle}" HorizontalAlignment="Center"/> </Grid> </AppBarButton>
  19. 19. 19 Top et bottom AppBar (Peuvent être ajoutées rapidement depuis le panneau « Structure du document ») <Page.TopAppBar> <CommandBar x:Name="topBar" ClosedDisplayMode="Compact"> <CommandBar.Content> <StackPanel Orientation="Horizontal"> <AppBarButton Icon="Home" /> <!--etc.--> </StackPanel> </CommandBar.Content> <AppBarButton Icon="Accept" Label="Valider"/> <!--etc.--> </CommandBar> </Page.TopAppBar> <Page.BottomAppBar> <CommandBar> <AppBarButton Icon="Accept" Label="Valider"/> <!--etc.--> </CommandBar> </Page.BottomAppBar> On peut changer le mode d’affichage : - Compact (par défaut) - Minimal (n’affiche plus que le bouton « … » permettant d’afficher les éléments de la barre) - Hidden la barre est complètement cachée. TopAppBar BottomAppBar
  20. 20. 20 6. Media Element et Custom media transport controls Documentation Source <MediaElement x:Name="mediaElement" Source="/Assets/dhany.mp4" AreTransportControlsEnabled="True" /> SetSource <MediaElement x:Name="mediaElement" AreTransportControlsEnabled="True" /> Ouverture d’une boite de dialogue private async void button_Click(object sender, RoutedEventArgs e) { var picker = new FileOpenPicker(); picker.FileTypeFilter.Add(".wmv"); picker.FileTypeFilter.Add(".mp4"); picker.FileTypeFilter.Add(".mp3"); picker.FileTypeFilter.Add(".wma"); picker.SuggestedStartLocation = PickerLocationId.VideosLibrary; var file = await picker.PickSingleFileAsync(); if (file != null) { IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read); mediaElement.SetSource(stream, file.ContentType); } } Il est possible de customiser les contrôles, en ajouter (un bouton like en plus par exemple) Voir l’exemple XamlCustomMediaTransportControls
  21. 21. 21 7. Content Dialog Menu « Ajouter » … « Nouvel élément » … « Boite de dialogue de contenu » Desktop Mobile Exemple de « ContentDialog » <ContentDialog x:Class="UWPContentDialogDemo.MyContentDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UWPContentDialogDemo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="Ecrire une note" PrimaryButtonText="Envoyer" SecondaryButtonText="Annuler" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" SecondaryButtonClick="ContentDialog_SecondaryButtonClick"> <StackPanel> <TextBox Header="Titre"></TextBox> <TextBox TextWrapping="Wrap" AcceptsReturn="True" Header="Contenu" Height="100"></TextBox> </StackPanel> </ContentDialog> Afficher la boite de dialogue et gérer le résultat private async void Button_Click(object sender, RoutedEventArgs e) { var dialog = new MyContentDialog(); var dialogResult = await dialog.ShowAsync(); if(dialogResult == ContentDialogResult.Primary) { } else if (dialogResult == ContentDialogResult.Secondary) { } } La boite de dialogue s’adapte selon la taille de la page. Par défaut la boite occupe toute la page mais on peut la redimensionner depuis le designer Contenu Titre et boutons de la boite de dialogue
  22. 22. 22 8. AutoSuggestBox (« remplaçant de SearchBox ») Voir les exemples XamlUIBasics et XamlAutoSuggestBox <AutoSuggestBox x:Name="autoSuggestBox" Margin="0,8,12,0" Width="270" PlaceholderText="Entrer le nom d'une ville Française" QueryIcon="Find" TextChanged="autoSuggestBox_TextChanged" SuggestionChosen="autoSuggestBox_SuggestionChosen" QuerySubmitted="autoSuggestBox_QuerySubmitted" RelativePanel.AlignRightWithPanel="True" /> private void autoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) { if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) { var suggestions = GetSuggestions(sender.Text); sender.ItemsSource = suggestions; } } private void autoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args) { var selectedItem = args.SelectedItem.ToString(); suggestionChosenTextBlock.Text = "Suggestion choisie : " + selectedItem; } private void autoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) { if (args.ChosenSuggestion != null) { } else if (!string.IsNullOrEmpty(args.QueryText)){ } } private List<string> GetSuggestions(string query) { var suggestions = cities.FindAll(c => c.ToLower().StartsWith(query.ToLower())); return suggestions; } On change la liste des suggestions selon le texte saisi Déclenché lorsque l’utilisateur sélectionne une suggestion de la liste proposée
  23. 23. 23 private List<string> cities = new List<string> { "Lyon", "Marseille", "Nantes", "Nice", "Strasbourg", "Toulouse", "Paris" }; 9. Popup Exemple popup « chargement » affichée pendant le chargement des données <Popup IsOpen="{Binding IsBusy}" VerticalAlignment="Center" HorizontalAlignment="Center"> <Grid Background="{ThemeResource ContentDialogBorderThemeBrush}" Height="100" Width="200"> <Grid.RenderTransform> <TranslateTransform X="-100" Y="-50" /> </Grid.RenderTransform> <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"> <ProgressRing VerticalAlignment="Center" IsActive="True" Foreground="{ThemeResource ContentDialogDimmingThemeBrush}" /> <TextBlock x:Uid="loadingTextBlock" Foreground="{ThemeResource ContentDialogDimmingThemeBrush}" Text="Loading" FontSize="22" FontWeight="Light" Margin="12,0,0,0" /> </StackPanel> </Grid> </Popup> 10.Extended SpashScreen Ajout dans la méthode « OnLaunched » de « App » if (e.PreviousExecutionState != ApplicationExecutionState.Running) { bool loadState = (e.PreviousExecutionState == ApplicationExecutionState.Terminated); var splash = new ExtendedSplash(e.SplashScreen, loadState); rootFrame.Content = splash; Window.Current.Content = rootFrame; } Création d’une page avec image et progress ring. <Grid Background="#D03133" > <Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="180"/> </Grid.RowDefinitions> <Canvas Grid.RowSpan="2"> <Image x:Name="extendedSplashImage" Source="Assets/SplashScreen.png"/> </Canvas> <ProgressRing IsActive="True" Grid.Row="1" Width="80" Height="80" Foreground="White" HorizontalAlignment="Center" /> </Grid> Place correctement au centre de la page
  24. 24. 24 La taille de l’image et les éléments sont repositionnés selon la taille de la page public sealed partial class ExtendedSplash { internal Rect splashImageRect; private SplashScreen splash; private double ScaleFactor; public ExtendedSplash(SplashScreen splashscreen, bool loadState) { InitializeComponent(); Window.Current.SizeChanged += new WindowSizeChangedEventHandler(OnResize); ScaleFactor = (double)DisplayInformation.GetForCurrentView().ResolutionScale / 100; splash = splashscreen; if (splash != null) { splash.Dismissed += new TypedEventHandler<SplashScreen, Object>(OnDismissed); splashImageRect = splash.ImageLocation; PositionImage(); } } private void PositionImage() { extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.Left); extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Top); extendedSplashImage.Height = splashImageRect.Height / ScaleFactor; extendedSplashImage.Width = splashImageRect.Width / ScaleFactor; } private void OnResize(Object sender, WindowSizeChangedEventArgs e) { if (splash != null) { splashImageRect = splash.ImageLocation; PositionImage(); } } private async void OnDismissed(SplashScreen sender, object e) { // on peut effectuer un chargement, essayer de connecter l’utilisateur, etc. par exemple puis naviguer vers la page principale await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { WNavigationService.Default.Navigate(typeof(MainPage)); }); } }
  25. 25. 25 11.VariableSizedWrapGrid <VariableSizedWrapGrid Orientation="Horizontal" MaximumRowsOrColumns="4" ItemHeight="100" ItemWidth="100"> <Rectangle Fill="Green" Width="200" Height="200" VariableSizedWrapGrid.RowSpan="2" VariableSizedWrapGrid.ColumnSpan="2" Margin="4"/> <Rectangle Fill="Gray" Width="200" VariableSizedWrapGrid.ColumnSpan="2" Margin="4"/> <Rectangle Fill="Red" Margin="4"/> <Rectangle Fill="Blue" Margin="4"/> </VariableSizedWrapGrid> La même chose pour un GridView public class VariableGridView : GridView { protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { var variableItem = item as VariableSizedItem; if (variableItem != null) { var gridViewItem = element as GridViewItem; if (gridViewItem != null) { VariableSizedWrapGrid.SetColumnSpan(gridViewItem, variableItem.ColumnSpan); VariableSizedWrapGrid.SetRowSpan(gridViewItem, variableItem.RowSpan); } } base.PrepareContainerForItemOverride(element, item); } } Le modèle public class VariableItem { public string Title { get; set; } public SolidColorBrush BackgroundColor { get; set; } public int Height { get; set; } public int Width { get; set; } public int ColumnSpan { get; set; } public int RowSpan { get; set; } // etc. } On peut changer la largeur, hauteur et ainsi définir combien de colonnes, lignes sont occupées Propriétés liées pour définir l’élément dans le GridView, il pourrait avoir d’autres propriétés avec des données récupérées On dérive le GridView et redéfinit « PrepareContainerForItemOverride » On change le nombre de colonnes et lignes en rapport à l’élément « VariableItem » (modèle défini) Chaque élément de base a une taille de 100x100. Ils s’empilent horizontalement (orientation) dans la limite de 4
  26. 26. 26 Ajout à la page <local:VariableGridView x:Name="variableGridView"> <local:VariableGridView.ItemTemplate> <DataTemplate> <Grid Background="{Binding BackgroundColor}" Height="{Binding Height}" Width="{Binding Width}" Margin="4"> <TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </DataTemplate> </local:VariableGridView.ItemTemplate> <local:VariableGridView.ItemsPanel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Horizontal" MaximumRowsOrColumns="4" ItemHeight="100" ItemWidth="100"/> </ItemsPanelTemplate> </local:VariableGridView.ItemsPanel> </local:VariableGridView> Dans le code-behind de la page public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); Items = new List<VariableItem>(); Items.Add(new VariableItem { Title = "First", BackgroundColor = new SolidColorBrush(Colors.Green), Height = 200, Width = 200, ColumnSpan = 2, RowSpan = 2 }); Items.Add(new VariableItem { Title = "Second", BackgroundColor = new SolidColorBrush(Colors.Gray), Width = 200, Height = 100, ColumnSpan = 2 }); Items.Add(new VariableItem { Title = "Three", BackgroundColor = new SolidColorBrush(Colors.Red), Height = 100, Width = 100, }); Items.Add(new VariableItem { Title = "Four", BackgroundColor = new SolidColorBrush(Colors.Blue), Height = 100, Width = 100, }); variableGridView.ItemsSource = Items; } public List<VariableItem> Items { get; set; } }
  27. 27. 27 II. Accent et Thème (Dark, Light) <Rectangle Fill="{ThemeResource SystemAccentColor}" Grid.Row="1"></Rectangle> On peut changer le thème (« Light » ou « Dark ») pour toute l’application dans « app » <Application x:Class="UWPAutoSuggestBoxDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UWPAutoSuggestBoxDemo" RequestedTheme="Light"> </Application> Ou pour seulement des éléments ou contrôles avec l’attribut « RequestedTheme », exemple <RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" RequestedTheme="Dark"> <!--etc--> </RelativePanel> Définir son propre thème <Application.Resources> <ResourceDictionary> <ResourceDictionary.ThemeDictionaries> <ResourceDictionary x:Key="Default"> <SolidColorBrush x:Key="BackgroundBrush" Color="Red" /> </ResourceDictionary> <ResourceDictionary x:Key="Light"> <SolidColorBrush x:Key="BackgroundBrush" Color="Gray" /> </ResourceDictionary> <ResourceDictionary x:Key="HighContrast"> <SolidColorBrush x:Key="BackgroundBrush" Color="{ThemeResource SystemColorWindowColor}" /> </ResourceDictionary> </ResourceDictionary.ThemeDictionaries> </ResourceDictionary> </Application.Resources> Thème Dark, la couleur sera rouge Thème Light, la couleur sera gris
  28. 28. 28 On peut également définir les thèmes dans des dictionnaires de ressources et les rendre accessibles à l’application <Application x:Class="UWPThemesDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UWPThemesDemo" RequestedTheme="Dark"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.ThemeDictionaries> <ResourceDictionary Source="Resources/Dark.xaml" x:Key="Default" /> <ResourceDictionary Source="Resources/Light.xaml" x:Key="Light" /> </ResourceDictionary.ThemeDictionaries> </ResourceDictionary> </Application.Resources> </Application> Utilisation <Grid Background="{ThemeResource BackgroundBrush}"> </Grid> III. Design Time public class MainPageViewModel : ViewModelBase { public MainPageViewModel() { if (Windows.ApplicationModel.DesignMode.DesignModeEnabled) { } } }
  29. 29. 29 IV. DataTemplateSelector Permet d’afficher différents Templates dans une ListView ou un GridView pour une même source de données. public class SearchResultTemplateSelector : DataTemplateSelector { public DataTemplate VideoTemplate { get; set; } public DataTemplate ChannelTemplate { get; set; } public DataTemplate PlayListTemplate { get; set; } protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { if(item is Video) { return VideoTemplate; } if (item is Channel) { return ChannelTemplate; } if (item is PlayList) { return PlayListTemplate; } return base.SelectTemplateCore(item, container); } } Exemple de Templates <DataTemplate x:Key="YouTube320VideoItemTemplate" x:DataType="models:Video"> <!--etc--> </DataTemplate> <DataTemplate x:Key="YouTubeChannelItemTemplate" x:DataType="models:Channel"> <!--etc--> </DataTemplate> <DataTemplate x:Key="YouTubePlaylistItemTemplate" x:DataType="models:PlayList"> <!--etc--> </DataTemplate> Models public class ModelBase { // properties } public class Video :ModelBase { // } public class Channel :ModelBase { // } public class PlayList :ModelBase { // } 3 propriétés permettant de définir un Template à retourner selon le type de l’élément reçu Les 3 classes peuvent hériter d’une classe de base. Ainsi la source de données sera une collection de « ModelBase »
  30. 30. 30 ViewModel public class SearchPageViewModel : ViewModelBase { private ObservableCollection<ModelBase> _results; public ObservableCollection<ModelBase> Results { get { return _results; } set { SetProperty(ref _results, value); } } // etc. } Utilisation En ressources (de la page par exemple) <local:SearchResultTemplateSelector x:Key="TemplateSelector" VideoTemplate="{StaticResource YouTubeVideoItemTemplate}" ChannelTemplate="{StaticResource YouTubeChannelItemTemplate}" PlayListTemplate="{StaticResource YouTubeSearchPlayListItemTemplate}"> </local:SearchResultTemplateSelector> Puis définition de l’ « ItemTemplateSelector » <GridView x:Name="itemsGridView" ItemsSource="{x:Bind ViewModel.Results,Mode=OneWay}" ItemTemplateSelector="{StaticResource TemplateSelector}"> </GridView> Le GridView affiche un Template différent selon que c’est une vidéo, une playlist ou une chaine par exemple
  31. 31. 31  Dernier élément « Afficher plus » pour ListView ou GridView Sur ce même principe on peut imaginer un « last item » template qui afficherait un bouton afficher plus par exemple. public class LastTemplateSelector : DataTemplateSelector { public DataTemplate NormalTemplate { get; set; } public DataTemplate LastTemplate { get; set; } protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { if (item is DataItem) { return NormalTemplate; } if (item is LastItem) { return LastTemplate; } return base.SelectTemplateCore(item, container); } } Pour les modèles on a deux classes héritant de « ModelBase public class ModelBase { public string Title { get; set; } } public class LastItem : ModelBase { public LastItem(string title) { Title = title; } } public class DataItem : ModelBase { public int Id { get; set; } public string Subtitle { get; set; } // etc. }
  32. 32. 32 ViewModel public class MainPageViewModel { public ObservableCollection<ModelBase> Items { get; set; } private ICommand _goDetailsPageCommand; public ICommand GoDetailsPageCommand { get { return _goDetailsPageCommand ?? (_goDetailsPageCommand = new RelayCommand<ItemClickEventArgs>((item) => { var selectedItem = item.ClickedItem; if(selectedItem is DataItem) { // navigate to data } if(selectedItem is LastItem) { // load more data } })); } } public MainPageViewModel() { Items = new ObservableCollection<ModelBase>(); Items.Add(new DataItem(1, "Titre 1", "Subtile", "Lorem ipsum dolor sit …", "Images/group.jpg")); Items.Add(new DataItem(2, "Titre 2", "Subtile", "Lorem ipsum dolor sit …", "Images/group_2.jpg")); // etc. // Last Items.Add(new LastItem("Afficher plus")); } } On ajoute dans le code behind de la page une propriété pour le binding (avec x :Bind) public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); ViewModel = new MainPageViewModel(); } public MainPageViewModel ViewModel { get; set; } } Quand l’utilisateur clique sur un élément du GridView soit on le diriga vers la page détails soit on charge plus d’éléments. (Il faudra les insérer avant le bouton)
  33. 33. 33 En ressources de la page <local:LastTemplateSelector x:Key="LastTemplateSelector" NormalTemplate="{StaticResource ImageOverlayTemplate}" LastTemplate="{StaticResource LastTemplate}"/> On définit un template simple pour le type « LastItem » <DataTemplate x:Key="LastTemplate" x:DataType="models:LastItem"> <Grid Width="200" Height="200" Background="{ThemeResource SystemAccentColor}" Margin="4"> <TextBlock Text="{x:Bind Title}" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </DataTemplate> Le GridView avec ItemTemplateSelector et behavior <GridView x:Name="itemsListView" ItemsSource="{x:Bind ViewModel.Items}" ItemTemplateSelector="{StaticResource LastTemplateSelector}" SelectionMode="None" IsItemClickEnabled="True"> <Interactivity:Interaction.Behaviors> <Core:EventTriggerBehavior EventName="ItemClick"> <Core:InvokeCommandAction Command="{x:Bind ViewModel.GoDetailsPageCommand}" CommandParameter="{x:Bind itemsListView.SelectedItem}"/> </Core:EventTriggerBehavior> </Interactivity:Interaction.Behaviors> </GridView>
  34. 34. 34 V. HeaderGroup Models public sealed class DataSource { public static IEnumerable<DataGroup> GetGroups() { var groups = new List<DataGroup>(); var groupA = new DataGroup("A"); groupA.Items.Add(new DataItem("Aanor")); groupA.Items.Add(new DataItem("Aaricia")); groupA.Items.Add(new DataItem("Aaron")); // etc. groups.Add(groupA); var groupB = new DataGroup("B"); groupB.Items.Add(new DataItem("Babet")); groupB.Items.Add(new DataItem("Babeth")); // etc. groups.Add(groupB); // etc. return groups; } } public class DataGroup { public string TitleGroup { get; private set; } public ObservableCollection<DataItem> Items { get; set; } public DataGroup(string titleGroup) { TitleGroup = titleGroup; Items = new ObservableCollection<DataItem>(); } } public class DataItem { public string TitleItem { get; set; } public DataItem(string titleItem) { TitleItem = titleItem; } } Switch du nom de groupe au défilement
  35. 35. 35 ViewModel public class MyDataViewModel { public ObservableCollection<DataGroup> Groups { get; set; } public void LoadData() { var groups = DataSource.GetGroups(); if (groups != null) { Groups = new ObservableCollection<DataGroup>(groups); } } } Utilisation Code behind de la page public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); ViewModel = new MyDataViewModel(); } public MyDataViewModel ViewModel { get; set; } protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LoadData(); } } <ListView ItemsSource="{x:Bind cvs.View}" ItemTemplate="{StaticResource DataItemTemplate}"> <ListView.GroupStyle> <GroupStyle HeaderTemplate="{StaticResource HeaderTemplate}" /> </ListView.GroupStyle> </ListView> Templates et source de données groupée <Page.Resources> <CollectionViewSource x:Name="cvs" Source="{x:Bind ViewModel.Groups}" IsSourceGrouped="True" ItemsPath="Items" /> <DataTemplate x:Key="HeaderTemplate" x:DataType="models:DataGroup"> <TextBlock Text="{x:Bind TitleGroup}" Foreground="{ThemeResource ApplicationForegroundThemeBrush}" Style="{StaticResource SubtitleTextBlockStyle}"/> </DataTemplate> <DataTemplate x:Key="DataItemTemplate" x:DataType="models:DataItem"> <TextBlock Text="{x:Bind TitleItem}" Style="{StaticResource BaseTextBlockStyle}"/> </DataTemplate> </Page.Resources> Utilisation ici d’une ListView mais on peut faire la même chose avec un GridView
  36. 36. 36 DataTemplates Pour ListView Texte seul <DataTemplate x:Key="TextListTemplate" x:DataType="models:DataItem"> <Grid Width="280"> <TextBlock Text="{x:Bind Title}" Style="{StaticResource BaseTextBlockStyle}" Margin="8,0,0,0" HorizontalAlignment="Left" TextWrapping="Wrap"/> </Grid> </DataTemplate> Icone et texte <DataTemplate x:Key="IconTextTemplate" x:DataType="models:DataItem"> <StackPanel Orientation="Horizontal" Width="500"> <Image Height="45" Width="45" Margin="0,8,0,8" Source="{x:Bind Image}" Stretch="UniformToFill"/> <StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="8,8,0,0"> <TextBlock Text="{x:Bind Title}" Style="{StaticResource BaseTextBlockStyle}" /> <TextBlock Text="{x:Bind Subtitle}" Margin="0,4,8,0" Style="{StaticResource BodyTextBlockStyle}" /> </StackPanel> </StackPanel> </DataTemplate>
  37. 37. 37 Image et texte <DataTemplate x:Key="ImageTextListTemplate" x:DataType="models:DataItem"> <StackPanel Orientation="Horizontal" Width="500" Height="130"> <Image Height="110" Width="110" Margin="0,8,0,8" Source="{x:Bind Image}" Stretch="UniformToFill"/> <StackPanel VerticalAlignment="Center" Width="380" Margin="8,8,0,0"> <TextBlock Text="{x:Bind Title}" Style="{StaticResource BaseTextBlockStyle}" /> <TextBlock Text="{x:Bind Subtitle}" TextWrapping="WrapWholeWords" Style="{StaticResource CaptionTextBlockStyle}" /> <TextBlock Text="{x:Bind Description}" TextWrapping="WrapWholeWords" Margin="0,8,0,0" Style="{StaticResource BodyTextBlockStyle}"/> </StackPanel> </StackPanel> </DataTemplate> Overlay <DataTemplate x:Key="ImageOverlayTemplate" x:DataType="models:DataItem"> <Grid Height="110"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="300"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Image Source="{x:Bind Image}" Stretch="Uniform" Grid.Column="1" Grid.RowSpan="2" Margin="0,8,0,8"/> <Border Background="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" Margin="0,8,0,8"> <TextBlock Text="{x:Bind Title}" Margin="8,8,0,0" TextWrapping="Wrap" HorizontalAlignment="Left" Style="{StaticResource BaseTextBlockStyle}"/> </Border> <TextBlock Text="{x:Bind Subtitle}" Grid.Row="1" Style="{StaticResource BodyTextBlockStyle}" TextWrapping="Wrap" Margin="8,0,0,0"/> </Grid> </DataTemplate> </Page.Resources>
  38. 38. 38 Pour GridView Texte seul <DataTemplate x:Key="TextTemplate" x:DataType="models:DataItem"> <StackPanel Orientation="Horizontal" Width="300"> <TextBlock Text="{x:Bind Title}" Style="{StaticResource BaseTextBlockStyle}" Margin="8,0,0,0"/> </StackPanel> </DataTemplate> Icone et texte <DataTemplate x:Key="IconTextTemplate" x:DataType="models:DataItem"> <StackPanel Orientation="Horizontal" Width="280"> <Image Source="{x:Bind Image}" Width="45" Height="45" Margin="8" Stretch="UniformToFill"/> <StackPanel VerticalAlignment="Center" Margin="8,0"> <TextBlock Text="{x:Bind Title}" Style="{StaticResource BaseTextBlockStyle}" /> <TextBlock Text="{x:Bind Subtitle}" /> </StackPanel> </StackPanel> </DataTemplate> Image et texte <DataTemplate x:Key="ImageTextTemplate" x:DataType="models:DataItem"> <StackPanel Orientation="Horizontal" Width="500" Height="130"> <Image Source="{x:Bind Image}" Stretch="Fill" Height="110" Width="110" Margin="8,8,0,8"/> <StackPanel Width="350" Margin="8,8,0,0" VerticalAlignment="Center"> <TextBlock Text="{x:Bind Title}" Style="{StaticResource BaseTextBlockStyle}" /> <TextBlock Text="{x:Bind Subtitle}" Style="{StaticResource CaptionTextBlockStyle}"/> <TextBlock Text="{x:Bind Description}" TextWrapping="Wrap" Margin="0,8,0,0"/> </StackPanel> </StackPanel> </DataTemplate>
  39. 39. 39 Overlay <DataTemplate x:Key="ImageOverlayTemplate" x:DataType="models:DataItem"> <StackPanel Height="130" Width="190" Margin="4,4,4,8"> <TextBlock Text="{x:Bind Title}" Margin="8,4" Width="186" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Left"/> <Image Source="{x:Bind Image}" Margin="8,0,8,8" Stretch="UniformToFill"/> </StackPanel> </DataTemplate>
  40. 40. 40 VI. Adaptive UI Une application UWP doit pouvoir s’exécuter sur différents appareils. Il va falloir adapter la page pour les différentes résolutions et orientations (Portrait, paysage). Penser en Pixel effectif (et non en pixel réel) « Four is the magic number » (tailles, marges multiples de 4) « 6R »  Reposition  Resize  Reflow (basculer de 2 à 3 colonnes par exemple)  Reveal  Replace  Re-architect Utilisation de RelativePanel pour pouvoir repositionner les éléments Utilisation Visual States et AdaptiveTrigger pour déplacer des blocs ou éléments, redimensionner, cacher, etc. Snap points : - 320 - 548 Phone - 720 Tablet - 1024 Desktop Astuce : afficher la taille courante de la page public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.SizeChanged += (s, e) => { textBlock.Text = e.NewSize.Width.ToString(); }; }
  41. 41. 41 1. Adaptive Triggers Voir exemple Responsive Techniques 1. Ouvrir le projet avec Blend 2. Ajouter des Etats Visuels Panneau Etats : Ajouter un groupe d’états puis des états visuels 3. Changer les propriétés des éléments selon les états Sélectionner l’état dans le panneau « Etats » et l’élément dans le panneau « Objets et chronologie », puis modifier les propriétés de cet élément dans le panneau « Propriétés » AdaptiveTrigger ou Custom Trigger Ajout de groupe d’états Ajout d’états viusels Ajouter un AdaptiveTrigger Réglage des propriétés (MinWindowHeight et MinWindowWidth pour un AdaptiveTrigger)
  42. 42. 42 Repositionnement avec RelativePanel <RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualState x:Name="VisualStateMin0"> <VisualState.Setters> <Setter Target="textBlock.(RelativePanel.Below)" Value="image"/> </VisualState.Setters> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="1"/> </VisualState.StateTriggers> </VisualState> <VisualState x:Name="VisualStateMin548"> <VisualState.Setters> <Setter Target="textBlock.(RelativePanel.RightOf)" Value="image"/> </VisualState.Setters> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="548"/> </VisualState.StateTriggers> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Image x:Name="image" Source="Images/breakfast.jpg" Width="300" Margin="8"/> <TextBlock x:Name="textBlock" Text="Une petite faim?" TextWrapping="NoWrap" Style="{StaticResource SubheaderTextBlockStyle}" Margin="8" /> </RelativePanel> De 0 à 548 > 548 Setters StateTriggers De 0 à 548 on place le texte en-dessous l’image > 548 on place le texte à droite de l’image
  43. 43. 43 2. Master details > 548 Page divisée avec à gauche la liste, à droite le détail De 0 à 548 La liste occupe toute la largeur de la page (ColumnSpan=2), la partie détail est cachée (Collapsed) et lorsque l’on clique sur un élément de la liste on navigue vers une page détails Une variante consiste donner un nom à chaque colonne de la grille (exemple « masterColumn » et « detailsColumn ») et changer la taille pour de 0 à 548 (ou 720) passer masterColumn à « * » et detailsColumn à 0. Exemple XamlMasterDetail
  44. 44. 44 <Page …> <Page.Transitions> <TransitionCollection> <NavigationThemeTransition /> </TransitionCollection> </Page.Transitions> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualState x:Name="VisualStateMin0"> <VisualState.Setters> <Setter Target="personDetailsControl.(UIElement.Visibility)" Value="Collapsed"/> <Setter Target="peopleList.(Grid.ColumnSpan)" Value="2"/> </VisualState.Setters> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="1"/> </VisualState.StateTriggers> </VisualState> <VisualState x:Name="VisualStateMin548"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="548"/> </VisualState.StateTriggers> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ListView x:Name="peopleList" ItemsSource="{x:Bind People}" IsItemClickEnabled="True" ItemClick="peopleList_ItemClick" /> <Controls:PersonDetailsControl x:Name="personDetailsControl" Background="#ccc" DataContext="{Binding SelectedItem, ElementName=peopleList}" Grid.Column="1"/> </Grid> Navigation vers une page Détails si la partie détails est cachée (>548) private void ListView_ItemClick(object sender, ItemClickEventArgs e) { if (personDetailsControl.Visibility == Visibility.Collapsed) { Frame.Navigate(typeof(PersonDetailsPage), e.ClickedItem); } } La liste à gauche de la grille Un contrôle utilisateur affichant le détail à droite Visual States On peut ajouter une transition à page pour effet de retour
  45. 45. 45 La page Détails avec un bouton retour un titre et le contrôle utilisateur <RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button x:Name="backButton" Style="{StaticResource NavigationBackButtonSmallStyle}" TabIndex="1" Margin="8" Click="backButton_Click" AutomationProperties.Name="Back" ToolTipService.ToolTip="Back" /> <TextBlock Style="{ThemeResource TitleTextBlockStyle}" Text="{Binding Name, Mode=OneWay}" RelativePanel.RightOf="backButton" RelativePanel.AlignVerticalCenterWith="backButton" Margin="8"/> <Controls:PersonDetailsControl RelativePanel.Below="backButton" /> </RelativePanel> private void backButton_Click(object sender, RoutedEventArgs e) { Frame.GoBack(new DrillInNavigationTransitionInfo()); } 3. Custom adaptive triggers voir l’exemple XamlStateTriggers using Windows.System.Profile; using Windows.UI.Xaml; namespace UWPAdaptiveTriggerDemo.CustomTriggers { public class DeviceFamilyTrigger : StateTriggerBase { private string _deviceFamily; public string DeviceFamily { get { return _deviceFamily; } set { var qualifiers = ResourceContext.GetForCurrentView().QualifierValues; _deviceFamily = value; SetActive(_deviceFamily == qualifiers["DeviceFamily"]); // ou //var currentDeviceFamily = AnalyticsInfo.VersionInfo.DeviceFamily; //_deviceFamily = value; //SetActive(_deviceFamily == currentDeviceFamily); } } } } Avec Blend, pour un état visuel Hérite de « StateTriggerBase » On appelle la méthode « SetActive » Propriété(s) accessible(s) servant pour déterminer quand l’état est activé Sélectionner « Autre type » …
  46. 46. 46 On change ensuite les propriétés des éléments de la page selon les états visuels. Et le « Custom State Trigger » On définit ensuite les valeurs qui rendront actif l’état visuel
  47. 47. 47 Dans cet exemple, on affiche une image quand on est sur desktop, et une autre quand on est sur mobile <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="DeviceFamilyStates"> <VisualState x:Name="Desktop"> <VisualState.StateTriggers> <triggers:DeviceFamilyTrigger DeviceFamily="Windows.Desktop" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="desktopImage.Visibility" Value="Visible" /> <Setter Target="mobileImage.Visibility" Value="Collapsed" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Mobile"> <VisualState.StateTriggers> <triggers:DeviceFamilyTrigger DeviceFamily="Windows.Desktop" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="desktopImage.Visibility" Value="Collapsed" /> <Setter Target="mobileImage.Visibility" Value="Visible" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Image x:Name="desktopImage" Source="../Assets/desktop-family.png"></Image> <Image x:Name="mobileImage" Source="../Assets/mobile-family.png"></Image> </Grid>
  48. 48. 48 4. DeviceFamily On peut créer une version de page selon les devices Si on navigue vers la page « Scenario4 », automatiquement la vue pour Mobile sera affichée si on est sur ce device. On peut obtenir le device également en code var qualifiers = ResourceContext.GetForCurrentView().QualifierValues; if (qualifiers["DeviceFamily"] == "DeviceFamily-Xbox") { Frame.Navigate(typeof(MainPage_Xbox)); } else { Frame.Navigate(typeof(MainPage)); } 5. Astuce : réactiver les animations, transitions Windows Régler les effets visuels de Windows. Permet de réactiver les animations, transitions au cas où elles auraient été désactivées pour améliorer les performances par exemple. Vue correspondante pour Mobile Vue par « défaut »
  49. 49. 49 VII. x :Bind « Compiled Binding » Voir exemple XamlBind Les + - Meilleures performances que le binding « classique ». Très performant pour afficher des listes. - Va chercher automatiquement les propriétés dans le code-behind de la page sans définir de DataContext. Pour l’utilisation de ViewModels on définit donc directement une propriété dans le code-behind. - Le mode par défaut de binding est « OneTime ». Il faut donc définir un mode OneWay ou TwoWay si ce n’est pas suffisant. - On peut utiliser « x :Bind » pour binder des événements, permet d’éviter l’utilisation de RelayCommand ou de behavior pour des scénarios simples. Les - - Problème avec les commandes dans les DataTemplates de fichier de ressources (Besoin d’aller chercher le DataContext du parent) - Problème avec SelectedItem (renvoyant un objet) avec ListView / GridView .., besoin de faire des convertisseurs ? - Problème avec les styles - Les attributs ElementName, RelativeSource, Source, UpdateSourceTrigger ne sont pas pris en charge par « x :Bind » - Solution : il faudra parfois mélanger le binding classique et binding avec « x :Bind ». On règle alors le DataContext sur le même ViewModel défini en propriété dans le code-behind de la page. 1. Binding de collection On définit une propriété dans le code-behind de la page sans définir de DataContext public sealed partial class Scenario1 : Page { public Scenario1() { this.InitializeComponent(); People = new ObservableCollection<Person> { new Person { Name ="Marie Bellin"}, new Person { Name ="Jérôme Romagny"} }; } public ObservableCollection<Person> People { get; set; } } (Le modèle utilisé) public class Person { public string Name { get; set; } public override string ToString() { return Name; } }
  50. 50. 50 2. Binding dans la page <ListView ItemsSource="{x:Bind People}" /> 2. Avec DataTemplate On définit le DataTemplate en ressources de la page <DataTemplate x:Key="personItemTemplate" x:DataType="models:Person"> <StackPanel Orientation="Horizontal"> <SymbolIcon Symbol="People" Margin="8,0,0,0"/> <TextBlock Text="{x:Bind Name}" Margin="8,0,0,0"/> </StackPanel> </DataTemplate> <ListView ItemsSource="{x:Bind People}" ItemTemplate="{StaticResource personItemTemplate}" /> 3. Avec dictionnaire de ressources (ResourceDictionary) 1. Ajouter un dictionnaire de ressources <ResourceDictionary x:Class="UWPDataBindingDemo.Resources.Templates" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UWPDataBindingDemo.Resources" xmlns:models="using:UWPDataBindingDemo.Models"> <DataTemplate x:Key="personItemTemplateFromResourceDictionary" x:DataType="models:Person"> <StackPanel Orientation="Horizontal"> <SymbolIcon Symbol="People" Margin="8,0,0,0"/> <TextBlock Text="{x:Bind Name}" Margin="8,0,0,0"/> </StackPanel> </DataTemplate> </ResourceDictionary> 2. Ajout d’une classe (partielle) du même nom que le dictionnaire de ressources namespace UWPDataBindingDemo.Resources { public partial class Templates { public Templates() { InitializeComponent(); } } } Utilisation du DataTemplate du dictionnaire de resources <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <resources:Templates /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> Si aucun DataTemplate n’est défini c’est la méthode ToString des éléments qui est appelée Ajouter un namespace et définir le DataType Exemple de template Ajout d’un nom de classe Attention au namespace Référencer le dictionnaire en ressources de la page ou pour toute l’application dans « App »
  51. 51. 51 <ListView ItemsSource="{x:Bind People}" ItemTemplate="{StaticResource personItemTemplateFromResourceDictionary}" /> 4. « RelativeSource » et « ElementName » … lier au nom de l’élément <Slider x:Name="slider" Header="Slider" Maximum="10" Margin="8"/> <TextBlock Text="{x:Bind slider.Value,Mode=OneWay}" Margin="8"/> 5. « Source » et « DataContext ».. Ajouter une propriété du ViewModel dans le code-behind public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); ViewModel = new MyViewModel(); DataContext = ViewModel; } public MyViewModel ViewModel { get; set; } } Variantes public sealed partial class MainPage : Page { private MainViewModel viewModel; public MainPage() { this.InitializeComponent(); this.Loaded += MainPage_Loaded; } void MainPage_Loaded(object sender, RoutedEventArgs e) { viewModel = new MyViewModel(); this.DataContext = viewModel; } } } public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.DataContextChanged += (s, e) => { ViewModel = DataContext as MyViewModel; }; } public MainPageViewModel ViewModel { get; set; } } Parfois il faudra définir le DataContext pour mélanger les deux formes de binding
  52. 52. 52 <TextBlock Text="{x:Bind ViewModel.Message}"/> public class MyViewModel { public string Message { get; set; } public MyViewModel() { Message = "Bonjour!"; } } 6. Binding Events  Permet d’éviter l’utilisation de « RelayCommand » (ICommand) ou de behavior « EventToCommand » pour des scénarios simples, de « déplacer » les événements du code- behind de la page vers le ViewModel par exemple.  N’accepte pas les paramètres « personnels », pas de support de CanExecute Exemple public class MyViewModel { public void DoWork1() { } public void DoWork2(object sender, RoutedEventArgs e) { } public void DoWork3(object sender, object e) { } } Dans le code-behind de la page public sealed partial class Scenario4 : Page { public Scenario4() { this.InitializeComponent(); ViewModel = new MyViewModel(); } public MyViewModel ViewModel { get; set; } } <Button Content="Sans paramètre ... void DoWork1()" Click="{x:Bind ViewModel.DoWork1}"/> <Button Content="void DoWork2(object sender, RoutedEventArgs e)" Click="{x:Bind ViewModel.DoWork2}"/> <Button Content="void DoWork3(object sender, object e)" Click="{x:Bind ViewModel.DoWork3}"/> Les 3 signatures acceptées
  53. 53. 53 7. Defer loading Dans la page <Grid x:Name="myGrid" x:DeferLoadStrategy="Lazy" Background="Red" /> Dans le code-behind private void Button_Click(object sender, RoutedEventArgs e) { FindName("myGrid"); } VIII. Application Lifecycle Documentation Il faut donner l’illusion que l’application n’a pas été suspendue quand celle-ci est résumée.  Launch (« OnLaunched » de « App »). - « PreviousExecutionState » : NotRunning, Running,Suspended, Terminated, ClosedByUser. Si l’application est fermée par l’utilisateur (« ClosedByUser ») ne pas restaurer. - « ActivationKind » : Launch, File, Protocol, Cortana, Secondary tile, toast, search contract,etc.  Suspend : Les applications Desktop sont suspendues quand elles sont minimisées dans la barre de tâches. - Sauvegarder les états de la page et les données (Local, Roaming,…) - « App » : Sauvegarder l’historique de navigation  Resume : - « App » : Restaurer l’historique de navigation, retourner sur la page avant la suspension - Restaurer les états de la page (champs) et les données (Local, Roaming …) Note : Les applications UWP n’incluent pas « SuspensionManager » et « NavigationHelper ». La grille n’est affichée qu’à l’appel de FindName
  54. 54. 54 1. Historique de navigation (« App ») Sauvegarder la navigation private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); Frame rootFrame = Window.Current.Content as Frame; ApplicationData.Current.LocalSettings.Values["NavigationState"] = rootFrame.GetNavigationState(); deferral.Complete(); } Restaurer la navigation protected override void OnLaunched(LaunchActivatedEventArgs e) { // etc. if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { if (ApplicationData.Current.LocalSettings.Values.ContainsKey("NavigationState")) { rootFrame.SetNavigationState((string)ApplicationData.Current.LocalSettings.Values["NavigationState"]); } } } 2. Etats de la page et données Sauver les états de la page courante à la suspension (« code-behind de la page » ) protected override void OnNavigatedFrom(NavigationEventArgs e) { vm.SaveData(); } ViewModel public void SaveData() { ApplicationData.Current.RoamingSettings.Values["ValueOne"] = ValueOne; ApplicationData.Current.RoamingSettings.Values["ValueTwo"] = ValueTwo; } Restaurer les états protected override void OnNavigatedTo(NavigationEventArgs e) { if(e.NavigationMode == NavigationMode.New) { // clear vm.ClearData(); } else { vm.LoadData(); } } Exemple sauvegarde de deux valeurs de champs
  55. 55. 55 ViewModel public void LoadData() { if (ApplicationData.Current.RoamingSettings.Values.ContainsKey("ValueOne")) { ValueOne = ApplicationData.Current.RoamingSettings.Values["ValueOne"].ToString(); } if (ApplicationData.Current.RoamingSettings.Values.ContainsKey("ValueTwo")) { ValueTwo = ApplicationData.Current.RoamingSettings.Values["ValueTwo"].ToString(); } } public void ClearData() { if (ApplicationData.Current.RoamingSettings.Values.ContainsKey("ValueOne")) { ApplicationData.Current.RoamingSettings.Values.Remove("ValueOne"); } if (ApplicationData.Current.RoamingSettings.Values.ContainsKey("ValueTwo")) { ApplicationData.Current.RoamingSettings.Values.Remove("ValueTwo"); } }
  56. 56. 56 IX. Tiles, toasts Documentation Voir exemple Notifications Tile On définit les Assets toujours dans le manifeste de l’application, onglet « Ressources visuelles » - Petit : 71x71 (« généré automatiquement » si l’Asset n’est pas présent) - Moyen : 150x150 (Square150x150Logo.png) - Large : 310x150 (Wide310x150Logo.png) - Grand : 310x310 (Wide310x310Logo.png) Vignette de l’application : Chercher l’application depuis le champ recherche de Windows 10 puis « Epingler à l’écran de démarrage » 71x71 150x150 310x150 310x310 Seulement pour desktop
  57. 57. 57 Note : si on définit la couleur d’arrière-plan des vignettes sur « transparent », c’est alors la couleur d’accent de Windows qui est appliqué Vignette secondaire : ajoutée par code. La vignette est désormais ajoutée directement sans boite de dialogue (comme c’était le cas avec Windows 8.1) private async void OnPinSecondaryTile(object sender, RoutedEventArgs e) { var isPinned = SecondaryTile.Exists(tileId); if (!isPinned) { await PinSecondaryTile(tileId); } } const string tileId = "DetailsTile "; private async Task<bool> PinSecondaryTile(string tileId) { string tileActivationArguments = " mon argument"; var tile = new SecondaryTile(tileId, "Vignette secondaire", tileActivationArguments, new Uri("ms-appx:///Assets/Square150x150Logo.png", UriKind.Absolute), TileSize.Square150x150); tile.VisualElements.Wide310x150Logo = new Uri("ms-appx:///Assets/Wide310x150Logo.png", UriKind.Absolute); tile.VisualElements.Square310x310Logo = new Uri("ms-appx:///Assets/Square310x310Logo.png", UriKind.Absolute); // détails tile.VisualElements.ShowNameOnSquare150x150Logo = true; tile.VisualElements.ShowNameOnWide310x150Logo = true; tile.VisualElements.ShowNameOnSquare310x310Logo = true; //tile.VisualElements.BackgroundColor = Colors.DarkOrange; tile.VisualElements.ForegroundText = ForegroundText.Light; tile.RoamingEnabled = false; bool isPinned = await tile.RequestCreateAsync(); return isPinned; } Nom affiché, arguments, uri de la tile 150x150 et taille désirée de la vignette
  58. 58. 58 Notification de vignette private void OnUpdateTile(object sender, RoutedEventArgs e) { string image = "ms-appx:///Assets/image_1.jpg"; string title = "Titre!"; string message = "Nouveau message!"; string xml = string.Format(@"<tile> <visual version=""2""> <binding template=""TileSquare150x150PeekImageAndText02"" fallback=""TileSquarePeekImageAndText02""> <image id=""1"" src=""{0}"" alt=""alt text""/> <text id=""1"">{1}</text> <text id=""2"">{2}</text> </binding> <binding template=""TileWide310x150PeekImage01"" fallback=""TileWidePeekImage01""> <image id=""1"" src=""{0}""/> <text id=""1"">{1}</text> <text id=""2"">{2}</text> </binding> </visual> </tile>", image, title, message); var document = new XmlDocument(); document.LoadXml(xml); var notification = new TileNotification(document); TileUpdateManager.CreateTileUpdaterForApplication().Update(notification); } « Scheduled » (différé) // etc. var notification = new ScheduledTileNotification(document, DateTimeOffset.Now.AddSeconds(10)); TileUpdateManager. CreateTileUpdaterForSecondaryTile(tileId). AddToSchedule(notification); « Periodic » (Web Service appelé toutes les 30 minutes, 60 minutes, 6 heures, 12 heures, 24 heures) // etc. var updater = TileUpdateManager.CreateTileUpdaterForSecondaryTile(tileId); var uri = new Uri("http://localhost:3660/api/Notification/"); updater.StartPeriodicUpdate(uri, PeriodicUpdateRecurrence.Hour); « Push » (Push Services) Documentation Animation « cube 3D » Défilement Notification de la vignette principale. On peut également notifier les vignettes secondaires en passant le « tile id »
  59. 59. 59 Adaptive Tiles On peut toujours utiliser les templates de Windows 8.1 ou on peut utiliser les « Adaptive Templates » qui permettent plus de souplesse et de personnalisation Adaptive Tile Schema Exemple private void OnAdaptiveTile(object sender, RoutedEventArgs e) { string image = "ms-appx:///Assets/image_1.jpg"; string title = "Titre!"; string message = "Nouveau message!"; string xml = string.Format(@"<tile> <visual> <binding template=""TileSmall""> <image src=""{0}"" placement=""background""></image> </binding> <binding template=""TileMedium""> <image src=""{0}"" placement=""background""></image> <text hint-style=""caption"" hint-wrap=""true"">{1}</text> <text hint-style=""captionSubtle"" hint-wrap=""true"">{2}</text> </binding> <binding template=""TileWide""> <image src=""{0}"" placement=""background""></image> <text hint-style=""title"" hint-wrap=""true"">{1}</text> <text hint-style=""subSubtle"" hint-wrap=""true"">{2}</text> </binding> <binding template=""TileLarge"" branding=""nameAndLogo""> <group> <subgroup> <text hint-style=""title"">{1}</text> <text hint-style=""subSubtle"">{2}</text> </subgroup> </group> <image src=""{0}"" placement=""inline""></image> </binding> </visual> </tile>", image, title, message); var document = new XmlDocument(); document.LoadXml(xml); var notification = new TileNotification(document); TileUpdateManager.CreateTileUpdaterForApplication().Update(notification); } Image en arrière-plan, texte par-dessus avec styles Vignette 71x71 Vignette 150x150 Vignette 310x150 Vignette 310x310 Placement de l’image en arrière-plan (background) , on pourrait la placer en ligne avec un alignement On définit le style du texte (caption, etc.) .« hint- wrap » place devant l’image d’arrière-plan « branding » : nom et logo (44x44) Affichage du nom et logo Image « inline » Groupe et image en ligne
  60. 60. 60 Toast Les toasts servent soit à afficher un message, soit propose une action. On peut utiliser les templates de toast de Windows 8.1 ou on peut utiliser les adaptives templates. private void OnSendToast(object sender, RoutedEventArgs e) { string title = "Mon application"; string message = "Nouveau message!"; string xml = string.Format(@"<toast> <visual> <binding template=""ToastText02""> <text id=""1"">{0}</text> <text id=""2"">{1}</text> </binding> </visual> </toast>", title, message); var document = new XmlDocument(); document.LoadXml(xml); var notification = new ToastNotification(document); ToastNotificationManager.CreateToastNotifier().Show(notification); } Adaptive toast Toast Image remplaçant le logo Image « inline » Centre de notifications
  61. 61. 61 private void OnSendAdaptiveToast(object sender, RoutedEventArgs e) { string image = "ms-appx:///Assets/image_1.jpg"; string newLogo = "ms-appx:///Assets/image_2.png"; string title = "Mon application"; string message = "Nouveau message!"; string xml = string.Format(@"<toast> <visual> <binding template=""ToastGeneric""> <image src=""{0}"" placement=""appLogoOverride""/> <text>{1}</text> <text>{2}</text> <image src=""{3}"" placement=""inline""/> </binding> </visual> </toast>",newLogo, title, message,image); var document = new XmlDocument(); document.LoadXml(xml); var notification = new ToastNotification(document); ToastNotificationManager.CreateToastNotifier().Show(notification); } Scénarios <toast scenario=""reminder""> private void OnSendAlarmToast(object sender, RoutedEventArgs e) { string title = "Alarme!"; string message = "Message!"; string xml= string.Format(@"<toast launch='args' scenario='alarm'> <visual> <binding template='ToastGeneric'> <text>{0}</text> <text>{1}</text> </binding> </visual> <actions> <action arguments = 'snooze' content = 'snooze' /> <action arguments = 'dismiss' content = 'dismiss' /> </actions> </toast>",title,message); var document = new XmlDocument(); document.LoadXml(xml); var notification = new ToastNotification(document); ToastNotificationManager.CreateToastNotifier().Show(notification); } Reminder, alarm ou incomingCall
  62. 62. 62 Historique de notification ToastNotificationManager.History.Clear(); Il est possible de définir les applications pouvant envoyer des notifications dans les options de Windows 10 (« Système » … « Notifications et actions » ou depuis le centre de notifications de la barre de tâches)

×