SlideShare una empresa de Scribd logo
1 de 62
Descargar para leer sin conexión
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
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
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
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
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
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="&#x2261;" 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
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
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
<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
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
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
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
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
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
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
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
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
« 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="&#x03A3;"/>
</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
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
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
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
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
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
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
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
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
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
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
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
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
 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
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
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
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
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
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
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
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
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
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
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
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
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
<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
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
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
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
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
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
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
<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
<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
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
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
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
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
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
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
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
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
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
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)

Más contenido relacionado

Destacado

Silex: From nothing to an API
Silex: From nothing to an APISilex: From nothing to an API
Silex: From nothing to an APIchrisdkemper
 
Silex, the microframework
Silex, the microframeworkSilex, the microframework
Silex, the microframeworkInviqa
 
Introduction to universal windows platform(uwp) app development
Introduction to universal windows platform(uwp) app developmentIntroduction to universal windows platform(uwp) app development
Introduction to universal windows platform(uwp) app developmentThilina Wijerathne
 
WBSE Class III-English
WBSE Class III-EnglishWBSE Class III-English
WBSE Class III-English0wlish0racle
 
Tecnología Aplicada a la Educación
Tecnología Aplicada a la Educación Tecnología Aplicada a la Educación
Tecnología Aplicada a la Educación Uapa
 
FELICES FIESTAS NAVIDAD 2016
FELICES FIESTAS NAVIDAD 2016FELICES FIESTAS NAVIDAD 2016
FELICES FIESTAS NAVIDAD 2016Silvia Sifuentes
 
v+f Dossier presentation ENG
v+f Dossier presentation ENGv+f Dossier presentation ENG
v+f Dossier presentation ENGMariona Soler
 
130830 market outlook bulan september 2013
130830 market outlook bulan september 2013130830 market outlook bulan september 2013
130830 market outlook bulan september 2013Satrio Utomo
 
Rf christmas
Rf christmasRf christmas
Rf christmasncantafio
 

Destacado (13)

Silex入門
Silex入門Silex入門
Silex入門
 
Silex: From nothing to an API
Silex: From nothing to an APISilex: From nothing to an API
Silex: From nothing to an API
 
Silex, the microframework
Silex, the microframeworkSilex, the microframework
Silex, the microframework
 
Introduction to universal windows platform(uwp) app development
Introduction to universal windows platform(uwp) app developmentIntroduction to universal windows platform(uwp) app development
Introduction to universal windows platform(uwp) app development
 
WBSE Class III-English
WBSE Class III-EnglishWBSE Class III-English
WBSE Class III-English
 
Tecnología Aplicada a la Educación
Tecnología Aplicada a la Educación Tecnología Aplicada a la Educación
Tecnología Aplicada a la Educación
 
Flores curiosas
Flores curiosasFlores curiosas
Flores curiosas
 
Seminarskiјл
SeminarskiјлSeminarskiјл
Seminarskiјл
 
FELICES FIESTAS NAVIDAD 2016
FELICES FIESTAS NAVIDAD 2016FELICES FIESTAS NAVIDAD 2016
FELICES FIESTAS NAVIDAD 2016
 
v+f Dossier presentation ENG
v+f Dossier presentation ENGv+f Dossier presentation ENG
v+f Dossier presentation ENG
 
130830 market outlook bulan september 2013
130830 market outlook bulan september 2013130830 market outlook bulan september 2013
130830 market outlook bulan september 2013
 
Rf christmas
Rf christmasRf christmas
Rf christmas
 
Verbos planeacionesme
Verbos planeacionesmeVerbos planeacionesme
Verbos planeacionesme
 

Similar a Windows 10 UWP App Development ebook

gestion de notariat avec openerp
gestion de notariat avec openerpgestion de notariat avec openerp
gestion de notariat avec openerpHORIYASOFT
 
Ado.net base de données
Ado.net   base de donnéesAdo.net   base de données
Ado.net base de donnéesNosnos Nisrine
 
Apache Web Server Index
Apache Web Server IndexApache Web Server Index
Apache Web Server Indexwebhostingguy
 
Configuration des services web sous CentOS
Configuration des services web sous CentOSConfiguration des services web sous CentOS
Configuration des services web sous CentOSSarah
 
Polycop td access 2007 facturation
Polycop td access 2007  facturationPolycop td access 2007  facturation
Polycop td access 2007 facturationgkaterynne
 
Introduction á C-sharp
Introduction á C-sharpIntroduction á C-sharp
Introduction á C-sharpmedfa
 
Informatiques bac scientifiques
Informatiques bac scientifiquesInformatiques bac scientifiques
Informatiques bac scientifiquesHichem Kemali
 
Microsoft Dynamics CRM 2011 - Guide de personnalisation
Microsoft Dynamics CRM 2011 - Guide de personnalisationMicrosoft Dynamics CRM 2011 - Guide de personnalisation
Microsoft Dynamics CRM 2011 - Guide de personnalisationPhilippe LEAL
 
The Ring programming language version 1.7 book - Part 3 of 196
The Ring programming language version 1.7 book - Part 3 of 196The Ring programming language version 1.7 book - Part 3 of 196
The Ring programming language version 1.7 book - Part 3 of 196Mahmoud Samir Fayed
 
Rapport de pfe gestion de parc informatique et Helpdesk
Rapport de pfe gestion de parc informatique et HelpdeskRapport de pfe gestion de parc informatique et Helpdesk
Rapport de pfe gestion de parc informatique et HelpdeskRaef Ghribi
 
Mémoire fin d'étude gestion des interventions
Mémoire fin d'étude gestion des interventionsMémoire fin d'étude gestion des interventions
Mémoire fin d'étude gestion des interventionsMohamed Arar
 
Le Référentiel Nouvelles Plateformes Technologiques
Le Référentiel Nouvelles Plateformes TechnologiquesLe Référentiel Nouvelles Plateformes Technologiques
Le Référentiel Nouvelles Plateformes TechnologiquesGenève Lab
 
réaliser une plateforme d’automatisation et de génération des rapports de test
réaliser une plateforme d’automatisation et de génération des rapports de testréaliser une plateforme d’automatisation et de génération des rapports de test
réaliser une plateforme d’automatisation et de génération des rapports de testahmed oumezzine
 

Similar a Windows 10 UWP App Development ebook (20)

gestion de notariat avec openerp
gestion de notariat avec openerpgestion de notariat avec openerp
gestion de notariat avec openerp
 
Ado.net base de données
Ado.net   base de donnéesAdo.net   base de données
Ado.net base de données
 
Oracle fr
Oracle frOracle fr
Oracle fr
 
Apache Web Server Index
Apache Web Server IndexApache Web Server Index
Apache Web Server Index
 
Configuration des services web sous CentOS
Configuration des services web sous CentOSConfiguration des services web sous CentOS
Configuration des services web sous CentOS
 
Tp sgbd gsi
Tp sgbd gsiTp sgbd gsi
Tp sgbd gsi
 
Rapport pfev7
Rapport pfev7Rapport pfev7
Rapport pfev7
 
Polycop td access 2007 facturation
Polycop td access 2007  facturationPolycop td access 2007  facturation
Polycop td access 2007 facturation
 
Introduction á C-sharp
Introduction á C-sharpIntroduction á C-sharp
Introduction á C-sharp
 
Informatiques bac scientifiques
Informatiques bac scientifiquesInformatiques bac scientifiques
Informatiques bac scientifiques
 
Programmation en-java-api
Programmation en-java-apiProgrammation en-java-api
Programmation en-java-api
 
Microsoft Dynamics CRM 2011 - Guide de personnalisation
Microsoft Dynamics CRM 2011 - Guide de personnalisationMicrosoft Dynamics CRM 2011 - Guide de personnalisation
Microsoft Dynamics CRM 2011 - Guide de personnalisation
 
Manuel i parapheur-v3.1
Manuel i parapheur-v3.1Manuel i parapheur-v3.1
Manuel i parapheur-v3.1
 
The Ring programming language version 1.7 book - Part 3 of 196
The Ring programming language version 1.7 book - Part 3 of 196The Ring programming language version 1.7 book - Part 3 of 196
The Ring programming language version 1.7 book - Part 3 of 196
 
Rapport de pfe gestion de parc informatique et Helpdesk
Rapport de pfe gestion de parc informatique et HelpdeskRapport de pfe gestion de parc informatique et Helpdesk
Rapport de pfe gestion de parc informatique et Helpdesk
 
Mémoire fin d'étude gestion des interventions
Mémoire fin d'étude gestion des interventionsMémoire fin d'étude gestion des interventions
Mémoire fin d'étude gestion des interventions
 
Tutoriel J2EE
Tutoriel J2EETutoriel J2EE
Tutoriel J2EE
 
Le Référentiel Nouvelles Plateformes Technologiques
Le Référentiel Nouvelles Plateformes TechnologiquesLe Référentiel Nouvelles Plateformes Technologiques
Le Référentiel Nouvelles Plateformes Technologiques
 
réaliser une plateforme d’automatisation et de génération des rapports de test
réaliser une plateforme d’automatisation et de génération des rapports de testréaliser une plateforme d’automatisation et de génération des rapports de test
réaliser une plateforme d’automatisation et de génération des rapports de test
 
rapport_stage_TBLB.pdf
rapport_stage_TBLB.pdfrapport_stage_TBLB.pdf
rapport_stage_TBLB.pdf
 

Más de Cheah Eng Soon

Microsoft Defender for Endpoint
Microsoft Defender for EndpointMicrosoft Defender for Endpoint
Microsoft Defender for EndpointCheah Eng Soon
 
Azure Active Directory - Secure and Govern
Azure Active Directory - Secure and GovernAzure Active Directory - Secure and Govern
Azure Active Directory - Secure and GovernCheah Eng Soon
 
MEM for OnPrem Environments
MEM for OnPrem EnvironmentsMEM for OnPrem Environments
MEM for OnPrem EnvironmentsCheah Eng Soon
 
Microsoft Threat Protection Automated Incident Response
Microsoft Threat Protection Automated Incident Response Microsoft Threat Protection Automated Incident Response
Microsoft Threat Protection Automated Incident Response Cheah Eng Soon
 
Azure Penetration Testing
Azure Penetration TestingAzure Penetration Testing
Azure Penetration TestingCheah Eng Soon
 
Azure Penetration Testing
Azure Penetration TestingAzure Penetration Testing
Azure Penetration TestingCheah Eng Soon
 
Penetration Testing Azure for Ethical Hackers
Penetration Testing Azure for Ethical HackersPenetration Testing Azure for Ethical Hackers
Penetration Testing Azure for Ethical HackersCheah Eng Soon
 
Microsoft Threat Protection Automated Incident Response Demo
Microsoft Threat Protection Automated Incident Response DemoMicrosoft Threat Protection Automated Incident Response Demo
Microsoft Threat Protection Automated Incident Response DemoCheah Eng Soon
 
Microsoft Secure Score Demo
Microsoft Secure Score DemoMicrosoft Secure Score Demo
Microsoft Secure Score DemoCheah Eng Soon
 
Microsoft Cloud App Security Demo
Microsoft Cloud App Security DemoMicrosoft Cloud App Security Demo
Microsoft Cloud App Security DemoCheah Eng Soon
 
M365 Attack Simulation Demo
M365 Attack Simulation DemoM365 Attack Simulation Demo
M365 Attack Simulation DemoCheah Eng Soon
 
Azure Active Directory - External Identities Demo
Azure Active Directory - External Identities Demo Azure Active Directory - External Identities Demo
Azure Active Directory - External Identities Demo Cheah Eng Soon
 
Azure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Azure Weekend 2020 Build Malaysia Bus Uncle ChatbotAzure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Azure Weekend 2020 Build Malaysia Bus Uncle ChatbotCheah Eng Soon
 
Microsoft Azure的20大常见安全漏洞与配置错误
Microsoft Azure的20大常见安全漏洞与配置错误Microsoft Azure的20大常见安全漏洞与配置错误
Microsoft Azure的20大常见安全漏洞与配置错误Cheah Eng Soon
 
20 common security vulnerabilities and misconfiguration in Azure
20 common security vulnerabilities and misconfiguration in Azure20 common security vulnerabilities and misconfiguration in Azure
20 common security vulnerabilities and misconfiguration in AzureCheah Eng Soon
 
Integrate Microsoft Graph with Azure Bot Services
Integrate Microsoft Graph with Azure Bot ServicesIntegrate Microsoft Graph with Azure Bot Services
Integrate Microsoft Graph with Azure Bot ServicesCheah Eng Soon
 
Azure Sentinel with Office 365
Azure Sentinel with Office 365Azure Sentinel with Office 365
Azure Sentinel with Office 365Cheah Eng Soon
 

Más de Cheah Eng Soon (20)

Microsoft Defender for Endpoint
Microsoft Defender for EndpointMicrosoft Defender for Endpoint
Microsoft Defender for Endpoint
 
Azure Active Directory - Secure and Govern
Azure Active Directory - Secure and GovernAzure Active Directory - Secure and Govern
Azure Active Directory - Secure and Govern
 
Microsoft Zero Trust
Microsoft Zero TrustMicrosoft Zero Trust
Microsoft Zero Trust
 
MEM for OnPrem Environments
MEM for OnPrem EnvironmentsMEM for OnPrem Environments
MEM for OnPrem Environments
 
Microsoft Threat Protection Automated Incident Response
Microsoft Threat Protection Automated Incident Response Microsoft Threat Protection Automated Incident Response
Microsoft Threat Protection Automated Incident Response
 
Azure Penetration Testing
Azure Penetration TestingAzure Penetration Testing
Azure Penetration Testing
 
Azure Penetration Testing
Azure Penetration TestingAzure Penetration Testing
Azure Penetration Testing
 
Penetration Testing Azure for Ethical Hackers
Penetration Testing Azure for Ethical HackersPenetration Testing Azure for Ethical Hackers
Penetration Testing Azure for Ethical Hackers
 
Microsoft Threat Protection Automated Incident Response Demo
Microsoft Threat Protection Automated Incident Response DemoMicrosoft Threat Protection Automated Incident Response Demo
Microsoft Threat Protection Automated Incident Response Demo
 
Microsoft Secure Score Demo
Microsoft Secure Score DemoMicrosoft Secure Score Demo
Microsoft Secure Score Demo
 
Microsoft Cloud App Security Demo
Microsoft Cloud App Security DemoMicrosoft Cloud App Security Demo
Microsoft Cloud App Security Demo
 
M365 Attack Simulation Demo
M365 Attack Simulation DemoM365 Attack Simulation Demo
M365 Attack Simulation Demo
 
Cloud Security Demo
Cloud Security DemoCloud Security Demo
Cloud Security Demo
 
Azure Active Directory - External Identities Demo
Azure Active Directory - External Identities Demo Azure Active Directory - External Identities Demo
Azure Active Directory - External Identities Demo
 
Azure WAF
Azure WAFAzure WAF
Azure WAF
 
Azure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Azure Weekend 2020 Build Malaysia Bus Uncle ChatbotAzure Weekend 2020 Build Malaysia Bus Uncle Chatbot
Azure Weekend 2020 Build Malaysia Bus Uncle Chatbot
 
Microsoft Azure的20大常见安全漏洞与配置错误
Microsoft Azure的20大常见安全漏洞与配置错误Microsoft Azure的20大常见安全漏洞与配置错误
Microsoft Azure的20大常见安全漏洞与配置错误
 
20 common security vulnerabilities and misconfiguration in Azure
20 common security vulnerabilities and misconfiguration in Azure20 common security vulnerabilities and misconfiguration in Azure
20 common security vulnerabilities and misconfiguration in Azure
 
Integrate Microsoft Graph with Azure Bot Services
Integrate Microsoft Graph with Azure Bot ServicesIntegrate Microsoft Graph with Azure Bot Services
Integrate Microsoft Graph with Azure Bot Services
 
Azure Sentinel with Office 365
Azure Sentinel with Office 365Azure Sentinel with Office 365
Azure Sentinel with Office 365
 

Windows 10 UWP App Development ebook

  • 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 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 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 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 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 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="&#x2261;" 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 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 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 <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 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 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 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 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 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 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 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 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 « 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="&#x03A3;"/> </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 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 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 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 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 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 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 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 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 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 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 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 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  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 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 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 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 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 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 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 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 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 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 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 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 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 <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 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 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 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 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 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 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 <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 <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 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 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 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 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 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 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 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 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 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 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)