Création d'un contrôle personnalisé à l'aide de XAML et C#

Comme vous le savez peut-être, la plateforme XAML de Windows 8 offre une flexibilité extrême pour créer des contrôles personnalisés. XAML propose des fonctionnalités telles que les propriétés de dépendances et les modèles de contrôles, qui facilitent la création de contrôles riches en fonctionnalités et entièrement personnalisables.

Dans le dernier billet publié, Création d'un contrôle personnalisé à l'aide de la bibliothèque Windows pour JavaScript, Jordan Matthiesen a expliqué comment créer un contrôle « HelloWorld » personnalisé. Dans ce nouveau billet, je vais vous expliquer comment créer ce même contrôle en XAML. Pour cela, j'utilise des techniques et des concepts permettant de créer des contrôles personnalisés réutilisables et je montre comment créer un modèle pour définir le style de ces contrôles. Je présente également des concepts tels que les propriétés de dépendances et l'utilisation d'un fichier Generic.xaml, afin de créer un style implicite définissant un modèle de contrôle par défaut.

Contrôle XAML simple

Commençons par créer le « Hello World » des contrôles : une classe dérivée de Windows.UI.XAML.Controls.Control. Créez un projet dans Visual Studio en utilisant le modèle de projet vide. Appelez votre projet CustomControls. Ajoutez ensuite votre contrôle personnalisé en utilisant l'Assistant Nouveau modèle d'élément.

Visual Studio intègre un modèle d'élément permettant de créer un contrôle à partir d'un modèle. Cliquez avec le bouton droit de la souris sur le projet et sélectionnez Ajouter -> Nouvel élément

Visual Studio intègre un modèle d'élément permettant de créer un contrôle à partir d'un modèle. Cliquez avec le bouton droit de la souris sur le projet et sélectionnez Ajouter -> Nouvel élément

Le modèle d'élément de contrôle contrôle crée les fichiers et un squelette de code, pour faciliter la création de votre contrôle personnalisé

Sélectionnez l'élément Contrôle basé sur un modèle et appelez-le « HelloWorld.cs ».

Ce modèle crée la classe suivante :

 public class HelloWorld : Control
{
    public HelloWorld()    {
        this.DefaultStyleKey = typeof(HelloWorld);
    }
}

Dans ce court extrait de code, nous avons défini deux détails très importants. Tout d'abord, la classe HelloWorld est dérivée de Control. Ensuite, le fait de configurer DefaultStyleKey indique à la plateforme XAML que cette classe utilise un style implicite. Le modèle de contrôle ajoute également un dossier Themes, dans lequel un fichier Generic.xaml est créé. Pour en savoir plus sur ce modèle, consultez l'article Modèles de projets.

Le style par défaut d'un contrôle est défini dans un fichier Generic.xaml personnalisé, que la plateforme charge automatiquement. Dans ce fichier, vous définissez un style implicite, ce qui signifie que nous pouvons définir un style qui sera appliqué par défaut à tous les contrôles du type spécifié. Ajoutez le code XAML mis en évidence au nouveau fichier Generic.xaml qui figure dans le dossier Themes :

 <ResourceDictionary
    xmlns="https://schemas.microsoft.com/winfx/2006/XAML/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/XAML"
    xmlns:local="using:CustomControls">

    <Style TargetType="local:HelloWorld">
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:HelloWorld">
                    <Border
                       Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                        <TextBlock Text="HelloWorld" 
                               FontFamily="Segoe UI Light"
                               FontSize="36"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style> 
</ResourceDictionary>

Ces quelques lignes de code XAML définissent un style qui sera appliqué par défaut à toutes les instances du contrôle HelloWorld. Nous définissons un modèle de contrôle, qui indique que ce contrôle est tout simplement un élément TextBlock au sein de l'élément Text « HelloWorld ».

Accédez maintenant à votre fichier MainPage.xaml et ajoutez le code ci-dessous. Vous devez inclure la désignation « local: » pour indiquer à la plateforme XAML dans quel espace de noms elle doit rechercher la classe HelloWorld. La désignation « local: » est définie au début du fichier XAML.

 <local:HelloWorld />

Lorsque vous exécutez votre application, vous pouvez constater que le contrôle a été chargé et qu'il affiche le texte « Hello, World! ».

Définition des options du contrôle

Si nous ajoutons des options configurables, les contrôles deviennent encore plus utiles et réutilisables. Ajoutons une option permettant de faire clignoter le contrôle.

Pour ajouter cette option, nous ajoutons une propriété de dépendance au contrôle. Pour en savoir plus sur les propriétés de dépendance, consultez l'article Vue d'ensemble des propriétés de dépendance. Un extrait de code Visual Studio facilite grandement l'ajout de propriétés de dépendance. Après avoir placé le curseur sous le constructeur, saisissez « propdp » et appuyez deux fois sur Tabulation. Vous pouvez saisir les caractéristiques en appuyant sur Tabulation pour passer d'un paramètre à l'autre dans l'extrait de code.

 public class HelloWorld : Control
{
    public HelloWorld()    {
        this.DefaultStyleKey = typeof(HelloWorld);
    }

    public bool Blink
    {
        get { return (bool)GetValue(BlinkProperty); }
        set { SetValue(BlinkProperty, value); }
    }

    // Using a DependencyProperty enables animation, styling, binding, etc.
    public static readonly DependencyProperty BlinkProperty =
        DependencyProperty.Register(
            "Blink",                  // The name of the DependencyProperty
            typeof(bool),             // The type of the DependencyProperty
            typeof(HelloWorld),       // The type of the owner of the DependencyProperty
            new PropertyMetadata(     // OnBlinkChanged will be called when Blink changes
                false,                // The default value of the DependencyProperty
                new PropertyChangedCallback(OnBlinkChanged)
            )
        );

    private DispatcherTimer __timer = null;
    private DispatcherTimer _timer
    {
        get
        {
            if (__timer == null)
            {
                __timer = new DispatcherTimer();
                __timer.Interval = new TimeSpan(0,0,0,0,500); // 500 ms interval
                __timer.Tick += __timer_Tick;
            }

            return __timer;
        }
    }

    private static void OnBlinkChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e
    )
    {
        var instance = d as HelloWorld;
        if (instance != null)
        {
            if (instance._timer.IsEnabled != instance.Blink)
            {
                if (instance.Blink)
                {
                    instance._timer.Start();
                }
                else
                {
                    instance._timer.Stop();
                }
            }
        }
    }

    private void __timer_Tick(object sender, object e)
    {
        DoBlink();
    }

    private void DoBlink()
    {
        this.Opacity = (this.Opacity + 1) % 2;
    }
}

De retour dans MainPage.xaml, ajoutez l'option de configuration au contrôle.

 <local:HelloWorld Blink="True" />

Exécutez l'application et observez le contrôle qui clignote !

Ajout de la prise en charge des événements

En ajoutant des événements aux contrôles, vous pouvez étendre leurs fonctionnalités. Les événements vous permettent de générer des interruptions à partir du contrôle lorsque des actions se déroulent, puis d'exécuter du code en réponse aux actions. Ajoutons un événement qui se déclenche à chaque fois que le contrôle clignote.

 public class HelloWorld : Control
{
    
    ...
...
    private void __timer_Tick(object sender, object e)
    {
        DoBlink();
    }

    private void DoBlink()
    {
        this.Opacity = (this.Opacity + 1) % 2;
        OnBlinked();
    }

    public event EventHandler Blinked;

    private void OnBlinked()
    {
        EventHandler eh = Blinked;
        if (eh != null)
        {
            eh(this, new EventArgs());
        }
    }
}

De retour dans MainPage.xaml, ajoutez une propriété x:Name à l'élément, pour que nous puissions par la suite retrouver l'instance du contrôle dans le code :

 <local:HelloWorld x:Name="HelloWorldWithEvents" Blink="True" />

Dans MainPage.xaml.cs, ajoutez ensuite un délégué de fonction de détecteur d'événements pour imprimer la sortie de débogage au déclenchement de l'événement.

 HelloWorldWithEvents.Blinked += (object sender, EventArgs args) =>
{
    System.Diagnostics.Debug.WriteLine("HelloWorldWithEvents Blinked");
};

Lorsque vous exécutez ce code, vous voyez un message s'afficher toutes les 500 millisecondes dans la fenêtre de la console de sortie de Visual Studio.

Exposition de méthodes publiques

Nous avons déjà écrit une méthode privée DoBlink pour faire clignoter le contrôle. Nous allons maintenant rendre cette méthode publique, pour vous permettre de faire clignoter le contrôle à tout moment. Pour cela, il vous suffit de modifier l'attribut visibility de la méthode DoBlink et de lui attribuer la valeur public :

 public class HelloWorld : Control
{
    ...

    public void DoBlink()
    {
        this.Opacity = (this.Opacity + 1) % 2;
        OnBlinked();
    }

    ...
}

Vous pouvez maintenant appeler la méthode publique DoBlink avec code-behind en C#, par exemple dans un gestionnaire Button.Click.

 private void Button_Click_1(object sender, RoutedEventArgs e)
{
    HelloWorldWithEvents.DoBlink();
}    

Réutilisation des contrôles

Lorsque vous souhaitez utiliser un contrôle dans un seul projet ou à des fins personnelles, vous n'avez presque rien à faire en termes de package et de distribution. En revanche, si vous souhaitez créer un contrôle réutilisable afin de le partager avec d'autres développeurs, plusieurs options s'offrent à vous. Vous pouvez notamment créer un SDK d'extension Visual Studio, que les autres développeurs pourront installer sur leurs machines et ajouter à leurs projets. Pour en savoir plus sur cette méthode, consultez l'article Création d'un SDK à l'aide de C# ou de Visual Basic.

Pour résumer...

Nous venons de passer en revue les aspects les plus courants liés à l'implémentation d'un contrôle :

  1. Insertion du contrôle dans une page
  2. Transmission d'options de configuration
  3. Distribution et réponse aux événements
  4. Exposition de fonctionnalités par le biais de méthodes publiques

Voici donc les principes de base des contrôles XAML personnalisés. L'étape suivante consiste à trouver les fonctionnalités à ajouter à un contrôle XAML existante, puis à modifier le style ou à insérer le contrôle dans une sous-classe pour ajouter vos propres fonctionnalités. En devenant plus à l'aise avec les contrôles personnalisés, j'ai pu développer des applications XAML avec plus de confiance. J'espère que vous aussi, vous essaierez de développer vos propres contrôles personnalisés et que vous partagerez vos expériences sur le Web.

Nous espérons que ce billet vous aura été utile ! Pour toute question sur la gestion de vos propres contrôles personnalisés, consultez le Centre de développement Windows et posez votre question dans les forums.

--Aaron Wroblewski

Chef de projet, Windows