Menu

A

|

A

SurfaceItemsControl comment ça marche ?

Il arrive souvent lors d’un développement Microsoft Surface, de devoir créer des contrôles personnalisés pour affiner l’expérience NUI (Natural User Interface) de notre application. La plupart du temps on va créer un SurfaceUserControl ou un CustomControl, mais d’expérience il arrive souvent de devoir créer un contrôle de contenu qui fonctionne à la manière d’une ListBox, ComboBoxou ScatterView. Je vais donc expliquer comment créer ce genre de contrôle en héritant d’un SurfaceItemsControl (ItemsControl en WPF, la démarche est identique).

Concept de base :

Un SurfaceItemsControl n’est qu’un container qui permet d’afficher et de gérer des éléments de diverses nature et de les afficher et les traiter d’une façon particulière. Comme pour tout container Surface (ou WPF), la propriété ItemsSource permet, par Binding notamment, de lui adresser la liste des éléments à traiter. Au moment de traiter la liste de ses éléments, l’ItemsControl va encapsuler chaque élément de la liste (chaque élément de l’arbre logique) dans un container de type ContentControl afin de les traiter de façon unifiée dans son arbre visuel, cependant il restera accessible en l’état dans l’arbre logique. Là ou ça deviens intéréssant, c’est que l’on peut redéfinir ce container et lui donner des comportements que nous jugeons utiles.

Voici les différentes étapes pour implémenter correctement ce type de contrôle.

Créer un CustomControl :

Commencez par créer un CustomControl et faites le hériter de SurfaceItemsControl. A ce moment précis vous avez déjà les mécanismes de base, à savoir tout le comportement lié à la gestion des objets (ItemsSource) et vous avez un affichage basique. Ce que l’on aimerai bien, c’est pouvoir redéfinir toute la gestion du SurfaceItemsControl. La première étape est de redéfinir le contrôle lui même, pour cela il faut redéfinir son style. Modifier le fichier generic.xaml afin de lui donner l’aspect que vous souhaitez.

using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;

namespace MonAssembly
{
    public class MonControle: SurfaceItemsControl
    {
        public MonControle()
        {
        }
    }
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        xmlns:local="clr-namespace:MonAssembly">
<Style TargetType="{x:Type local:MonControle}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MonControle}">
                <Canvas IsItemsHost="True"
                        Width="{TemplateBinding Width}"
                        Height="{TemplateBinding Height}"
                        Background="{TemplateBinding Background}"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                </Canvas>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Ensuite il faut créer un ContentControl pour jouer le rôle du container utilisé dans le SurfaceItemsControl. Pour cela il suffit de refaire un CustomControl et d’y mettre un ContentPresenter.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:MonAssembly">
<Style TargetType="{x:Type controls:MonContainer}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:MonContainer}">
                <ContentPresenter />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</ResourceDictionary>

Une fois la base créée il reste à surcharger les différentes méthodes qui permettront au contrôle de bien fonctionner.

Implémenter les surcharges :

De base le SurfaceItemsControl gère de façon automatique l’encapsulation des Items dans un ContentControl, il faut donc lui indiquer que vous voulez changer ce ContentControl de base pour le remplacer par le vôtre. Pour ce faire il fait surcharger une série de fonction.

ClearContainerForItemOverride

protected override void ClearContainerForItemOverride(DependencyObject element, object item)

Permet d’exécuter des actions au moment ou un objet dans la source est supprimée de celle-ci.

GetContainerForItemOverride

protected override DependencyObject GetContainerForItemOverride()

Permet d’instancier votre propre container, pour remplacer celui par défaut.

protected override DependencyObject GetContainerForItemOverride()
{
    return (new MonContainer());
}

IsItemItsOwnContainerOverride

protected override bool IsItemItsOwnContainerOverride(object item)

Permet à au SurfaceItemsControl de vérifier si une instance est du même type que votre container.

protected override bool IsItemItsOwnContainerOverride(object item)
{
    return (item is MonContainer);
}

PrepareContainerForItemOverride

protected override void PrepareContainerForItemOverride(DependencyObject p_oElement, object p_oItem)

Permet d’effectuer des traitements après que notre objet est été associé à notre container personnalisé. Il est à noter qu’il est important de bien faire le base.PrepareContainerForItemOverride avant d’effectuer ses traitements. En effet, c’est dans l’exécution du code en amont que le SurfaceItemsControl fait son association, si vous ne l’appelez pas, l’association ne sera pas faite.

protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
    // Appel au parent
    base.PrepareContainerForItemOverride(element, item);

    // Mettre ici votre code de préparation
}

Conclusion :

Si vous suivez bien ces étapes il est très facile d’implémenter ce type de contrôle, entraînez-vous sur des exemples simples et tester divers possibilités, au final ce sera toujours à peu près la même chose.
Ce type de contrôle a vraiment une réelle valeur ajoutée et permet de multiple représentations (verticale, surface plane, liste, etc.)

SurfaceItemsControl comment ça marche ?

Il arrive souvent lors d’un développement Microsoft Surface, de devoir créer des contrôles personnalisés pour affiner l’expérience NUI (Natural User Interface) de notre application. La plupart du temps on va créer un SurfaceUserControl ou un CustomControl, mais d’expérience il arrive souvent de devoir créer un contrôle de contenu qui fonctionne à la manière d’une ListBox, ComboBoxou ScatterView. Je vais donc expliquer comment créer ce genre de contrôle en héritant d’un SurfaceItemsControl (ItemsControl en WPF, la démarche est identique).

Concept de base :

Un SurfaceItemsControl n’est qu’un container qui permet d’afficher et de gérer des éléments de diverses nature et de les afficher et les traiter d’une façon particulière. Comme pour tout container Surface (ou WPF), la propriété ItemsSource permet, par Binding notamment, de lui adresser la liste des éléments à traiter. Au moment de traiter la liste de ses éléments, l’ItemsControl va encapsuler chaque élément de la liste (chaque élément de l’arbre logique) dans un container de type ContentControl afin de les traiter de façon unifiée dans son arbre visuel, cependant il restera accessible en l’état dans l’arbre logique. Là ou ça deviens intéréssant, c’est que l’on peut redéfinir ce container et lui donner des comportements que nous jugeons utiles.

Voici les différentes étapes pour implémenter correctement ce type de contrôle.

Créer un CustomControl :

Commencez par créer un CustomControl et faites le hériter de SurfaceItemsControl. A ce moment précis vous avez déjà les mécanismes de base, à savoir tout le comportement lié à la gestion des objets (ItemsSource) et vous avez un affichage basique. Ce que l’on aimerai bien, c’est pouvoir redéfinir toute la gestion du SurfaceItemsControl. La première étape est de redéfinir le contrôle lui même, pour cela il faut redéfinir son style. Modifier le fichier generic.xaml afin de lui donner l’aspect que vous souhaitez.

using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;

namespace MonAssembly
{
    public class MonControle: SurfaceItemsControl
    {
        public MonControle()
        {
        }
    }
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        xmlns:local="clr-namespace:MonAssembly">
<Style TargetType="{x:Type local:MonControle}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MonControle}">
                <Canvas IsItemsHost="True"
                        Width="{TemplateBinding Width}"
                        Height="{TemplateBinding Height}"
                        Background="{TemplateBinding Background}"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                </Canvas>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Ensuite il faut créer un ContentControl pour jouer le rôle du container utilisé dans le SurfaceItemsControl. Pour cela il suffit de refaire un CustomControl et d’y mettre un ContentPresenter.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:MonAssembly">
<Style TargetType="{x:Type controls:MonContainer}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:MonContainer}">
                <ContentPresenter />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</ResourceDictionary>

Une fois la base créée il reste à surcharger les différentes méthodes qui permettront au contrôle de bien fonctionner.

Implémenter les surcharges :

De base le SurfaceItemsControl gère de façon automatique l’encapsulation des Items dans un ContentControl, il faut donc lui indiquer que vous voulez changer ce ContentControl de base pour le remplacer par le vôtre. Pour ce faire il fait surcharger une série de fonction.

ClearContainerForItemOverride

protected override void ClearContainerForItemOverride(DependencyObject element, object item)

Permet d’exécuter des actions au moment ou un objet dans la source est supprimée de celle-ci.

GetContainerForItemOverride

protected override DependencyObject GetContainerForItemOverride()

Permet d’instancier votre propre container, pour remplacer celui par défaut.

protected override DependencyObject GetContainerForItemOverride()
{
    return (new MonContainer());
}

IsItemItsOwnContainerOverride

protected override bool IsItemItsOwnContainerOverride(object item)

Permet à au SurfaceItemsControl de vérifier si une instance est du même type que votre container.

protected override bool IsItemItsOwnContainerOverride(object item)
{
    return (item is MonContainer);
}

PrepareContainerForItemOverride

protected override void PrepareContainerForItemOverride(DependencyObject p_oElement, object p_oItem)

Permet d’effectuer des traitements après que notre objet est été associé à notre container personnalisé. Il est à noter qu’il est important de bien faire le base.PrepareContainerForItemOverride avant d’effectuer ses traitements. En effet, c’est dans l’exécution du code en amont que le SurfaceItemsControl fait son association, si vous ne l’appelez pas, l’association ne sera pas faite.

protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
    // Appel au parent
    base.PrepareContainerForItemOverride(element, item);

    // Mettre ici votre code de préparation
}

Conclusion :

Si vous suivez bien ces étapes il est très facile d’implémenter ce type de contrôle, entraînez-vous sur des exemples simples et tester divers possibilités, au final ce sera toujours à peu près la même chose.
Ce type de contrôle a vraiment une réelle valeur ajoutée et permet de multiple représentations (verticale, surface plane, liste, etc.)