Les DataTemplate WPF – Partie 2

Dans l’article précédent nous avons vu comment faire une popin. Le principe est simple, dans le ViewModel nous avons une propriété qui contiendra le ViewModel de notre popin, puis dans la vue, nous avons un DataTemplate qui permet l’affichage de la vue lorsque le ViewModel est renseigné.

Dans cet article nous allons voir comment créer une pile de popin, c’est à dire plusieurs popin qui se superposent. Voila le résultat :

Sur cette image on peut voir une première Popin (verte) puis une seconde (bleu) par dessus la première. Nous allons voir comment cela se fait.

Voila l’arborescence de la solution :

arborescence de la solution

Je vais présenter les fichiers qui compose la solution et expliquer l’utilité de chacun d’eux.

PopinViewModel. cs

public class PopinViewModel: BaseViewModel
{
	public CommandBase QuitCommand {get; set; }
	public CommandBase OpenCommand {get; set; }
	public Action CloseHandler {get; set; }
	public Action OpenHandler {get; set; }
	public PopinViewModel()
	{
		QuitCommand = new CommandBase(onQuit);
		OpenCommand = new CommandBase(onOpen);
	}
	private void onOpen(object obj)
	{
		if (OpenHandler != null)
			OpenHandler();
	}
	private void onQuit(object obj)
	{
		if (CloseHandler != null)
			CloseHandler();
	}
}

Ce viewmodel est celui de la première popin, il permet donc de fermer la popin et d’ouvrir une nouvelle popin. Pour cela, il dispose de deux commandes et de deux handler qui seront appelés quand les commandes seront exécutées.

Popin2ViewModel. cs

Il s’agit du même que le précédent sauf qu’il n’y a qu’une commande pour fermer la popin.

public class Popin2ViewModel
{
	public CommandBase QuitCommand { get; set; }
	public Action CloseHandler  { get; set; }
	public Popin2ViewModel()
	{
		QuitCommand = new CommandBase(onQuit);
	}
	
	private void onQuit(object obj)
	{
		if (CloseHandler != null)
			CloseHandler();
	}
}

MainViewModel. cs

public class MainViewModel: BaseViewModel
{
	public ObservableCollection < Object > Popins { get; set; }
	public CommandBase PopinCommand { get; set; }
	public MainViewModel()
	{
		PopinCommand = new CommandBase(onPopin);
		Popins = new ObservableCollection < Object > ();
	}
	
	private void onPopin(object obj)
	{
		var p = new PopinViewModel();
		p.CloseHandler = closePopin;
		p.OpenHandler = openHandler;
		Popins.Add(p);
	}
	
	private void openHandler()
	{
		var p = new Popin2ViewModel();
		p.CloseHandler = closePopin;
		Popins.Add(p);
	}
	
	private void closePopin()
	{
		if (Popins.Any())
			Popins.Remove(Popins.Last());
	}
}

Ce ViewModel est le principal de notre application. Il contient une liste de Popin typé en ObservableCollection<Object>. On retrouve une commande qui permet d’ouvrir une popin. La méthode appelée lors de cette action (onPopin) instancie un PopinViewModel puis set ses propriétés CloseHandler et OpenHandler.

Le handler CloseHandler du PopinViewModel est setté avec la méthode ClosePopin. Cette méthode permet simplement de fermer la dernière popin ouverte.

Le handler OpenHandler est setté avec la méthode openHandler. Cette méthode ajoute un Popin2ViewModel à la liste des popins. Elle set aussi le handler CloseHandler avec la méthode closeHandler qui ferme la dernière popin ouverte.

Passons maintenant aux vues.

PopinView. xaml

<UserControl x:Class="DataTemplateTest2.View.PopinView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" Width="200" Height="100" Background="#50408040"> 
     <Grid> 
         <Button Content="Open" Command="{Binding OpenCommand}" Width="50" Height="30" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10,10,70,10" /> 
         <Button Content="Close" Command="{Binding QuitCommand}" Width="50" Height="30" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10" /> 
     </Grid> 
 </UserControl>

Cette vue représente la vu de notre première popin. Elle contient juste deux boutons, un premier qui appel la commande OpenCommand et un second qui appel la commande QuitCommand.

Popin2View. xaml

<UserControl x:Class="DataTemplateTest2.View.Popin2View" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" Width="150" Height="80" Background="#FF404080"> 
     <Grid> 
         <Button Content="Close" Command="{Binding QuitCommand}" Width="50" Height="30" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10" /> 
     </Grid> 
 </UserControl>

Cette vue représente la vue de notre Popin2, elle contient simplement un bouton qui permet de fermer la popin.

MainView. xaml

<UserControl x:Class="DataTemplateTest2.View.MainView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:ViewModel="clr-namespace:DataTemplateTest2.ViewModel" 
     xmlns:View="clr-namespace:DataTemplateTest2.View"> 
     <UserControl.Resources> 
         <DataTemplate DataType="{x:Type ViewModel:PopinViewModel}"> 
             <Border Background="#50808080" > 
                 <View:PopinView /> 
             </Border> 
         </DataTemplate> 
         <DataTemplate DataType="{x:Type ViewModel:Popin2ViewModel}"> 
             <Border Background="#50808080"> 
                 <View:Popin2View /> 
             </Border> 
         </DataTemplate> 
     </UserControl.Resources> 
     <Grid> 
         <Grid.ColumnDefinitions> 
             <ColumnDefinition /> 
             <ColumnDefinition /> 
         </Grid.ColumnDefinitions> 
         <Grid.RowDefinitions> 
             <RowDefinition /> 
             <RowDefinition /> 
         </Grid.RowDefinitions> 
         <Button Content="Test" Grid.Column="0" Grid.Row="0" Command="{Binding PopinCommand}" Width="50" Height="20" /> 
         <ItemsControl ItemsSource="{Binding Popins}" Grid.ColumnSpan="2" Grid.RowSpan="2"> 
             <ItemsControl.ItemsPanel> 
                 <ItemsPanelTemplate> 
                     <Grid /> 
                 </ItemsPanelTemplate> 
             </ItemsControl.ItemsPanel> 
         </ItemsControl> 
     </Grid> 
 </UserControl>

Voila la vue principal qui contient deux éléments. Le premier est un bouton pour ouvrir la première popin.

La vue contient aussi un ItemsControl qui va nous permettre d’afficher toutes nos popins. Sa propriété ItemsSource est bindé sur la propriété Popins de notre MainViewModel. Par défaut, la présentation des items dans un ItemsSource se fait de manière vertical, nous ne voulons pas de cette présentation pour notre exemple. Il est possible de modifier le conteneur d’un ItemsSource en spécifiant la propriété ItemsPanel. Dans notre cas, une grid fera l’affaire. un WrapPanel peut être intéressant dans certains cas. Pour notre exemple, une Grid est parfaite car tout les éléments vont se superposer, c’est bien le comportement attendu pour des popins. L’utilisation d’une Grid permet aussi de remplir tout l’espace. On voit aussi que l’ItemsControl remplie toute la Grid parente (avec les propriétés Grid. RowSpan et Grid. ColumnsSpan).

Maintenant que tout est fait, testons un peu notre application.

Si on lance notre application, et que l’on clique sur le bouton “Test”, un nouveau PopinViewModel est instancié, il est ajouté dans la liste des popins, la liste est de type ObservableCollection, elle va donc notifier la vue de la modification de son contenu. La vue affiche ensuite le PopinViewModel en choisissant le DataTemplate qui correspond à son type.

Une fois la première popin affichée, on peut la fermer simplement en supprimant l’élément dans la liste Popins. Pour en ouvrir une nouvelle, il suffit de cliquer sur le bouton open de la première popin. Ce bouton va ajouter un Popin2ViewModel dans la liste (par l’intermédiaire du handler). Les deux popins vont se superposer car elle sont toutes les deux dans une Grid qui remplie tout l’espace de la fenêtre.

Afin de mieux comprendre le fonctionnement, je vous invite à tester la solution complète jointe à cet article. DataTemplateTest2.rar (19,50 kb)

Ne ratez plus aucune actualité avec la newsletter mensuelle de SoftFluent

Newsletter SoftFluent