Personnalisation de la barre de titre

Windows fournit une barre de titre par défaut pour chaque fenêtre, et vous permet de la personnaliser afin qu’elle corresponde à la personnalité de votre application. La barre de titre par défaut est fournie avec certains composants standard et des fonctionnalités de base telles que le déplacement et le redimensionnement de la fenêtre.

A Windows app showing the title bar

Consultez l’article de conception Barre de titre pour obtenir des conseils sur la personnalisation de la barre de titre de votre application, le contenu acceptable de la zone de la barre de titre, et les modèles d’interface utilisateur recommandés.

Important

Cet article explique comment personnaliser la barre de titre pour les applications qui utilisent le SDK d’application Windows, avec ou sans WinUI 3. Pour les applications qui utilisent UWP et WinUI 2, consultez Personnalisation de la barre de titre pour UWP.

Composants de la barre de titre

Cette liste décrit les composants de la barre de titre standard.

  • Rectangle de barre de titre
  • Texte du titre
  • Icône système
  • Menu système : accessible en cliquant sur l’icône d’application ou en cliquant avec le bouton droit sur la barre de titre
  • Contrôles de légende
    • Bouton Réduire
    • Bouton Agrandir/Restaurer
    • Bouton Fermer

Fenêtrage

La fonctionnalité de fenêtrage dans le SDK d’application Windows s’opère via la classe Microsoft.UI.Windowing.AppWindow, qui est basée sur le modèle Win32 HWND. Il existe un mappage 1:1 entre une AppWindow et un HWND de niveau supérieur dans votre application. AppWindow et ses classes associées fournissent des API qui vous permettent de gérer de nombreux aspects des fenêtres de niveau supérieur de votre application, y compris la personnalisation de la barre de titre. Vous pouvez modifier la barre de titre par défaut fournie par Windows afin qu’elle s’intègre au reste de votre interface utilisateur, ou étendre le canevas de votre application dans la zone de la barre de titre et fournir votre propre contenu de barre de titre.

La fonctionnalité de fenêtrage dans WinUI 3 s’opère via la classe Microsoft.UI.Xaml.Window, qui est également basée sur le modèle HWND Win32. Pour les applications XAML qui utilisent WinUI 3, les API XAML Window offrent un moyen plus simple de personnaliser la barre de titre, tout en vous permettant d’accéder aux API AppWindow si nécessaire.

Comment utiliser AppWindow

Vous pouvez utiliser les API AppWindow avec n’importe quelle infrastructure d’interface utilisateur prise en charge par le SDK d’application Windows (Win32, WPF, WinForms ou WinUI 3), et vous pouvez les adopter de manière incrémentielle, en utilisant uniquement les API dont vous avez besoin.

Si vous utilisez WinUI 3 XAML comme infrastructure d’interface utilisateur de votre application, vous avez à votre disposition les API Window et AppWindow. À compter du SDK d’application Windows 1.4, les API XAML Window et AppWindow utilisent le même objet AppWindowTitleBar pour la personnalisation de la barre de titre. Utilisez la propriété Window.AppWindow pour obtenir un objet AppWindow à partir d’une fenêtre XAML existante. Avec cet objet AppWindow, vous avez accès aux API de personnalisation de la barre de titre. Pour accéder aux fonctionnalités supplémentaires de la barre de titre, vous pouvez utiliser les API AppWindow à partir de votre XAML Window comme suit : AppWindow.TitleBar.ForegroundColor = Colors.White;.

Si vous n’utilisez pas WinUI 3 1.3 ou version ultérieure, utilisez des API d’interopérabilité pour obtenir l’AppWindow et utilisez les API AppWindow pour personnaliser la barre de titre. Pour plus d’informations sur les API d’interopérabilité, consultez Gérer les fenêtres d’application – Infrastructure d’interface utilisateur et interopérabilité HWND et l’exemple de galerie de fenêtrage.

Étendue de la personnalisation de la barre de titre

Vous pouvez appliquer deux niveaux de personnalisation à la barre de titre : appliquer des modifications mineures à la barre de titre par défaut, ou étendre le canevas de votre application dans la zone de la barre de titre et fournir du contenu entièrement personnalisé.

Simple

Pour une personnalisation simple, telle que la modification de la couleur de la barre de titre, vous pouvez définir des propriétés sur l’objet AppWindowTitleBar afin de spécifier les couleurs que vous souhaitez utiliser pour les éléments de barre de titre. Dans ce cas, le système conserve la responsabilité de tous les autres aspects de la barre de titre, tels que le dessin du titre de l’application et la définition des zones de glissement.

Complète

Votre autre option consiste à masquer la barre de titre système par défaut et à la remplacer par votre propre contenu personnalisé. Par exemple, vous pouvez placer du texte, une zone de recherche ou des menus personnalisés dans la zone de la barre de titre. Vous devrez également utiliser cette option pour étendre une toile de fond de matériau, comme Mica, dans la zone de la barre de titre.

Lorsque vous optez pour une personnalisation complète, vous êtes responsable de la mise en place du contenu dans la zone de barre de titre, et vous pouvez définir vos propres régions de glissement. Les contrôles de légende (boutons système Fermer, Réduire et Agrandir) sont toujours disponibles et gérés par le système, mais les éléments tels que le titre de l’application ne le sont pas. Vous devrez créer ces éléments vous-même en fonction des besoins de votre application.

Personnalisation simple

Si vous souhaitez personnaliser uniquement le titre, les couleurs ou l’icône de la barre de titre, vous pouvez définir des propriétés sur l’objet de barre de titre de la fenêtre de votre application.

Titre

Par défaut, la barre de titre affiche le type d’application comme titre de la fenêtre (par exemple, « WinUI Desktop »). Vous devez mettre à jour le titre de la fenêtre de façon à afficher un nom d’affichage explicite pour votre application.

Une application XAML a un nom d’affichage défini dans le fichier Package.appxmanifest. Vous pouvez obtenir cette valeur et l’utiliser pour définir la propriété Title comme ceci.

Title = AppInfo.Current.DisplayInfo.DisplayName;

Pour modifier le titre de la fenêtre, affectez à la propriété Window.Title une valeur de texte à une seule ligne, comme indiqué ici.

<Window
    ...
    Title="App title">
    ...
</Window>
public MainWindow()
{
    InitializeComponent();
    Title = "App title";
}

Pour modifier le titre de la fenêtre à l’aide des API AppWindow, affectez à la propriété AppWindow.Title une valeur de texte à une seule ligne, comme illustré ici. Cet exemple montre comment utiliser des API d’interopérabilité pour obtenir AppWindow, qui est nécessaire si votre application n’utilise pas WinUI 3 1.3 ou version ultérieure.

using Microsoft.UI;           // Needed for WindowId.
using Microsoft.UI.Windowing; // Needed for AppWindow.
using WinRT.Interop;          // Needed for XAML/HWND interop.

private AppWindow m_AppWindow;

public MainWindow()
{
    this.InitializeComponent();

    m_AppWindow = GetAppWindowForCurrentWindow();
    m_AppWindow.Title = "App title";
}

private AppWindow GetAppWindowForCurrentWindow()
{
    IntPtr hWnd = WindowNative.GetWindowHandle(this);
    WindowId wndId = Win32Interop.GetWindowIdFromWindow(hWnd);
    return AppWindow.GetFromWindowId(wndId);
}

Couleurs

Pour personnaliser les couleurs de la barre de titre par défaut ou pour modifier l’icône de fenêtre par défaut, vous devez utiliser les API AppWindow ou choisir de personnaliser entièrement votre barre de titre.

Cet exemple montre comment obtenir une instance d’AppWindowTitleBar et définir ses propriétés de couleur.

Important

La personnalisation des couleurs est ignorée lorsque l’application s’exécute sur Windows 10.

// Assumes "this" is a XAML Window. In projects that don't use 
// WinUI 3 1.3 or later, use interop APIs to get the AppWindow.
AppWindow m_AppWindow = this.AppWindow;

private bool SetTitleBarColors()
{
    // Check to see if customization is supported.
    // The method returns true on Windows 10 since Windows App SDK 1.2,
    // and on all versions of Windows App SDK on Windows 11.
    if (AppWindowTitleBar.IsCustomizationSupported())
    {
        AppWindowTitleBar m_TitleBar = m_AppWindow.TitleBar;

        // Set active window colors.
        // Note: No effect when app is running on Windows 10
        // because color customization is not supported.
        m_TitleBar.ForegroundColor = Colors.White;
        m_TitleBar.BackgroundColor = Colors.Green;
        m_TitleBar.ButtonForegroundColor = Colors.White;
        m_TitleBar.ButtonBackgroundColor = Colors.SeaGreen;
        m_TitleBar.ButtonHoverForegroundColor = Colors.Gainsboro;
        m_TitleBar.ButtonHoverBackgroundColor = Colors.DarkSeaGreen;
        m_TitleBar.ButtonPressedForegroundColor = Colors.Gray;
        m_TitleBar.ButtonPressedBackgroundColor = Colors.LightGreen;

        // Set inactive window colors.
        // Note: No effect when app is running on Windows 10
        // because color customization is not supported.
        m_TitleBar.InactiveForegroundColor = Colors.Gainsboro;
        m_TitleBar.InactiveBackgroundColor = Colors.SeaGreen;
        m_TitleBar.ButtonInactiveForegroundColor = Colors.Gainsboro;
        m_TitleBar.ButtonInactiveBackgroundColor = Colors.SeaGreen;
        return true;
    }
    return false;
}

Il existe quelques éléments à prendre en compte lors de la définition des couleurs de la barre de titre :

  • La couleur d’arrière-plan du bouton n’est pas appliquée aux étatshover et pressed du bouton Fermer. Le bouton Fermer utilise toujours la couleur définie par le système pour ces états.
  • L’affectation de la valeur null à une propriété de couleur entraîne sa réinitialisation à la couleur système par défaut.
  • Vous ne pouvez pas définir de couleurs transparentes. Le canal alpha de la couleur est ignoré.

Windows offre à un utilisateur la possibilité d’appliquer sa couleur d’accentuation sélectionnée à la barre de titre. Si vous définissez une couleur de barre de titre, nous vous recommandons de définir explicitement toutes les couleurs. Cela garantit qu’il n’y a pas de combinaisons de couleurs involontaires qui se produisent en raison des paramètres de couleur définis par l’utilisateur.

Icône et menu système

Vous pouvez masquer l’icône système ou la remplacer par une icône personnalisée. L’icône système affiche le menu système en cas de clic droit ou d’appui. Elle ferme la fenêtre en cas de double-clic/appui.

Pour afficher ou masquer l’icône système et les comportements associés, définissez la propriété IconShowOptions de la barre de titre.

m_TitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;

Pour utiliser une icône de fenêtre personnalisée, appelez l’une des méthodes AppWindow.SetIcon afin de définir la nouvelle icône.

  • SetIcon(String)

    La méthode SetIcon(String) fonctionne actuellement uniquement avec les fichiers .ico. La chaîne que vous passez à cette méthode est le chemin complet du fichier .ico.

    m_AppWindow.SetIcon("iconPath/iconName.ico");
    
  • SetIcon(IconId)

    Si vous disposez déjà d’un handle pour une icône (HICON) à partir de l’une des fonctions Icon telles que CreateIcon, vous pouvez utiliser l’API d’interopérabilité GetIconIdFromIcon pour obtenir un IconId. Vous pouvez ensuite passer l’IconId à la méthode SetIcon(IconId) pour définir votre icône de fenêtre.

    m_AppWindow.SetIcon(iconId));
    

Personnalisation complète

Lorsque vous optez pour la personnalisation complète de la barre de titre, la zone cliente de votre application est étendue de façon à couvrir toute la fenêtre, y compris la zone de barre de titre. Vous êtes responsable du dessin et de la gestion des entrées pour l’ensemble de la fenêtre, à l’exception des boutons de légende, qui sont toujours fournis par la fenêtre.

Pour masquer la barre de titre système et étendre votre contenu dans la zone de barre de titre, affectez la valeur true à la propriété qui étend le contenu de l’application dans la zone de barre de titre. Dans une application XAML, cette propriété peut être définie dans la méthode OnLaunched de votre application (App.xaml.cs) ou dans la première page de votre application.

Conseil

Consultez la section Exemple de personnalisation complète pour voir tout le code en même temps.

Cet exemple montre comment affecter la valeur true à la propriété Window.ExtendsContentIntoTitleBar.

public MainWindow()
{
    this.InitializeComponent();

    // Hide system title bar.
    ExtendsContentIntoTitleBar = true;
}

Attention

ExtendsContentIntoTitleBar s’affiche dans le XAML IntelliSense pour Window, mais sa définition en XAML provoque une erreur. Définissez plutôt cette propriété dans le code.

Cet exemple montre comment obtenir l’AppWindowTitleBar et affecter la valeur à la propriété AppWindow.ExtendsContentIntoTitleBartrue. Cet exemple montre comment utiliser des API d’interopérabilité pour obtenir l’AppWindow, qui est nécessaire si votre application n’utilise pas WinUI 3 1.3 ou une version ultérieure.

using Microsoft.UI;           // Needed for WindowId.
using Microsoft.UI.Windowing; // Needed for AppWindow.
using WinRT.Interop;          // Needed for XAML/HWND interop.

private AppWindow m_AppWindow;

public MainWindow()
{
    this.InitializeComponent();

    m_AppWindow = GetAppWindowForCurrentWindow();
    var titleBar = m_AppWindow.TitleBar;
    // Hide system title bar.
    titleBar.ExtendsContentIntoTitleBar = true;
}

private AppWindow GetAppWindowForCurrentWindow()
{
    IntPtr hWnd = WindowNative.GetWindowHandle(this);
    WindowId wndId = Win32Interop.GetWindowIdFromWindow(hWnd);
    return AppWindow.GetFromWindowId(wndId);
}

Contenu de la barre de titre et région de glissement par défaut

Lorsque votre application est étendue à la zone de barre de titre, vous êtes responsable de la définition et de la gestion de l’interface utilisateur pour la barre de titre. Cela inclut généralement, au minimum, la spécification du texte de titre et de la zone de glissement. La zone de glissement de la barre de titre définit l’emplacement où l’utilisateur peut cliquer et faire glisser pour déplacer la fenêtre. C’est également là que l’utilisateur peut cliquer avec le bouton droit pour afficher le menu système.

Pour en savoir plus sur le contenu acceptable de la barre de titre et les modèles d’interface utilisateur recommandés, consultez Conception de barre de titre.

Cet exemple montre le XAML pour une interface utilisateur de barre de titre personnalisée sans contenu interactif.

<Grid x:Name="AppTitleBar"  
      Height="32">
    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
        <ColumnDefinition/>
        <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
    </Grid.ColumnDefinitions>
    <Image x:Name="TitleBarIcon" Source="ms-appx:///Assets/StoreLogo.png"
           Grid.Column="1"
           HorizontalAlignment="Left"
           Width="16" Height="16"
           Margin="8,0,0,0"/>
    <TextBlock x:Name="TitleBarTextBlock" 
               Text="App title" 
               Style="{StaticResource CaptionTextBlockStyle}"
               Grid.Column="1"
               VerticalAlignment="Center"
               Margin="28,0,0,0"/>
</Grid>

Important

LeftPaddingColumn et RightPaddingColumn sont utilisés afin de réserver de l’espace pour les boutons de légende. Les valeurs Width pour ces colonnes sont définies dans le code, qui est affiché plus loin. Pour obtenir le code et l’explication, consultez la section Boutons de légende système.

Une application XAML a un nom d’affichage qui est défini dans le fichier Package.appxmanifest. Vous pouvez obtenir cette valeur et l’utiliser dans votre barre de titre personnalisée comme suit.

TitleBarTextBlock.Text = AppInfo.Current.DisplayInfo.DisplayName;

Lorsque vous étendez votre contenu dans la zone de barre de titre, la barre de titre système est masquée et une AppWindowTitleBar par défaut est créée ; celle-ci fournit des boutons de légende et une zone de glissement sur toute la largeur de l’écran, identique à la barre de titre système. Si vous ne placez pas de contenu interactif dans votre barre de titre, vous pouvez laisser cette région de glissement par défaut en l’état. Si vous placez du contenu interactif dans votre barre de titre, vous devez spécifier les régions interactives, procédure que nous abordons dans la section suivante.

Attention

Lorsque vous définissez des régions de glissement personnalisées, elles n’ont pas besoin d’être en haut de la fenêtre dans la zone de barre de titre par défaut ; vous pouvez définir n’importe quelle partie de votre interface utilisateur en tant que région de glissement. Toutefois, si vous placez des régions de glissement à différents endroits, il sera peut-être compliqué pour vos utilisateurs de les découvrir.

Contenu interactif

Vous pouvez placer des contrôles interactifs, tels que des boutons, des menus ou une zone de recherche, dans la partie supérieure de l’application afin qu’ils apparaissent dans la barre de titre. Toutefois, vous devez spécifier les régions qui sont interactives, afin de vous assurer que vos éléments interactifs reçoivent une entrée utilisateur tout en permettant aux utilisateurs de déplacer votre fenêtre.

A Windows app with a search box in the title bar

Lorsque vous ajoutez du contenu interactif dans la zone de barre de titre, vous devez utiliser la classe InputNonClientPointerSource pour spécifier les zones dans lesquelles l’entrée est transmise au contrôle interactif, plutôt que gérée par la barre de titre. Pour définir les régions interactives, appelez la méthode InputNonClientPointerSource.SetRegionRects. Cette méthode prend une valeur qui spécifie le type de région défini (en l’occurrence Passthrough) et un tableau de rectangles, chacun définissant une région Passthrough. Lorsque la taille de la barre de titre change, vous devez recalculer les régions interactives afin qu’elles correspondent à la nouvelle taille, et appeler SetRegionRects avec les nouvelles valeurs.

Cet exemple montre une interface utilisateur de barre de titre personnalisée avec une zone de recherche et un contrôle de compte PersonPicture. Il montre comment calculer et définir les rectangles interactifs pour ces contrôles afin que l’entrée leur soit transmise.

Voici quelques points importants à noter concernant ce code :

  • Définissez la hauteur de la grille AppTitleBar sur 48 afin de respecter les instructions de conception de barre de titre pour le contenu interactif.
  • Définissez PreferredHeightOption sur Tall pour que les boutons de légende soient de la même hauteur que la barre de titre.
  • Pour faciliter le redimensionnement des contrôles et le calcul des régions, utilisez un objet Grid avec plusieurs colonnes nommées pour la disposition.
  • Utilisez le dimensionnement étoile (*) avec MinWidth pour la colonne qui contient la valeur AutoSuggestBox, afin qu’elle soit automatiquement redimensionnée avec la fenêtre.
  • Définissez MinWidth sur l’objet RightDragColumn afin de réserver une petite zone toujours déplaçable, même lorsque la fenêtre est redimensionnée.
  • Affectez la valeur true à ExtendsContentIntoTitleBar dans le constructeur MainWindow. Si vous définissez cette propriété dans du code appelé ultérieurement, la barre de titre système par défaut peut être affichée en premier, puis masquée.
  • Effectuez l’appel initial pour calculer les régions interactives une fois l’élément AppTitleBar chargé. Sinon, il n’y a aucune garantie que les éléments utilisés pour le calcul auront leurs valeurs correctes.
  • Mettez à jour les calculs du rectangle interactif uniquement après que l’élément AppTitleBar a changé de taille (AppTitleBar_SizeChanged). Si vous dépendez de l’événement de fenêtre Changed, il existe des situations (par exemple, agrandissement/réduction de la fenêtre) où l’événement se produit avant que AppTitleBar soit redimensionné et où les calculs utilisent des valeurs incorrectes.
  • Définissez vos zones interactives/de glissement personnalisées uniquement après avoir vérifié ExtendsContentIntoTitleBar afin de confirmer qu’une barre de titre personnalisée est utilisée.
<Grid x:Name="AppTitleBar"
      Height="48">
    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
        <ColumnDefinition x:Name="IconColumn" Width="Auto"/>
        <ColumnDefinition x:Name="TitleColumn" Width="Auto"/>
        <ColumnDefinition x:Name="LeftDragColumn" Width="*"/>
        <ColumnDefinition x:Name="SearchColumn" Width="4*" MinWidth="220"/>
        <ColumnDefinition x:Name="RightDragColumn" Width="*" MinWidth="48"/>
        <ColumnDefinition x:Name="AccountColumn" Width="Auto"/>
        <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
    </Grid.ColumnDefinitions>
    <Image x:Name="TitleBarIcon" 
           Source="ms-appx:///Assets/StoreLogo.png"
           Grid.Column="1"
           Width="16" Height="16"
           Margin="8,0,4,0"/>
    <TextBlock x:Name="TitleBarTextBlock"
               Text="App title" 
               Style="{StaticResource CaptionTextBlockStyle}"
               Grid.Column="2"
               VerticalAlignment="Center">
    </TextBlock>
    <AutoSuggestBox x:Name="TitleBarSearchBox" 
                    Grid.Column="4" 
                    QueryIcon="Find"
                    PlaceholderText="Search"
                    VerticalAlignment="Center"
                    MaxWidth="600"/>
    <PersonPicture x:Name="PersonPic" 
                   Grid.Column="6" 
                   Height="32" Margin="0,0,16,0"/>
</Grid>

Ce code montre comment calculer et définir les régions interactives qui correspondent aux contrôles AutoSuggestBox et PersonPicture.

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();

        // Assumes "this" is a XAML Window. In projects that don't use 
        // WinUI 3 1.3 or later, use interop APIs to get the AppWindow.
        m_AppWindow = this.AppWindow;
        AppTitleBar.Loaded += AppTitleBar_Loaded;
        AppTitleBar.SizeChanged += AppTitleBar_SizeChanged;
        ExtendsContentIntoTitleBar = true;
        TitleBarTextBlock.Text = AppInfo.Current.DisplayInfo.DisplayName;
    }

    private void AppTitleBar_Loaded(object sender, RoutedEventArgs e)
    {
        if (ExtendsContentIntoTitleBar == true)
        {
            // Set the initial interactive regions.
            SetRegionsForCustomTitleBar();
        }
    }

    private void AppTitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (ExtendsContentIntoTitleBar == true)
        {
            // Update interactive regions if the size of the window changes.
            SetRegionsForCustomTitleBar();
        }
    }

    private void SetRegionsForCustomTitleBar()
    {
        // Specify the interactive regions of the title bar.

        double scaleAdjustment = AppTitleBar.XamlRoot.RasterizationScale;

        RightPaddingColumn.Width = new GridLength(m_AppWindow.TitleBar.RightInset / scaleAdjustment);
        LeftPaddingColumn.Width = new GridLength(m_AppWindow.TitleBar.LeftInset / scaleAdjustment);

        GeneralTransform transform = TitleBarSearchBox.TransformToVisual(null);
        Rect bounds = transform.TransformBounds(new Rect(0, 0, 
                                                         TitleBarSearchBox.ActualWidth,
                                                         TitleBarSearchBox.ActualHeight));
        Windows.Graphics.RectInt32 SearchBoxRect = GetRect(bounds, scaleAdjustment);
        
        transform = PersonPic.TransformToVisual(null);
        bounds = transform.TransformBounds(new Rect(0, 0,
                                                    PersonPic.ActualWidth,
                                                    PersonPic.ActualHeight));
        Windows.Graphics.RectInt32 PersonPicRect = GetRect(bounds, scaleAdjustment);

        var rectArray = new Windows.Graphics.RectInt32[] { SearchBoxRect, PersonPicRect };

        InputNonClientPointerSource nonClientInputSrc =
            InputNonClientPointerSource.GetForWindowId(this.AppWindow.Id);
        nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, rectArray);
    }

    private Windows.Graphics.RectInt32 GetRect(Rect bounds, double scale)
    {
        return new Windows.Graphics.RectInt32(
            _X: (int)Math.Round(bounds.X * scale),
            _Y: (int)Math.Round(bounds.Y * scale),
            _Width: (int)Math.Round(bounds.Width * scale),
            _Height: (int)Math.Round(bounds.Height * scale)
        );
    }
}

Avertissement

AppWindow utilise des pixels physiques pour la compatibilité avec les infrastructures d’interface utilisateur qui n’utilisent pas de coordonnées logiques. Si vous utilisez WPF ou WinUI 3, RightInset, LeftInset et les valeurs utilisées pour calculer les régions doivent être ajustées si l’échelle d’affichage n’est pas 100 %. Dans cet exemple, nous obtenons une valeur scaleAdjustment pour tenir compte du paramètre d’échelle d’affichage.

  • Pour WinUI 3, utilisez la propriété XamlRoot.RasterizationScale afin d’obtenir l’ajustement de l’échelle.
  • Pour WPF, vous pouvez gérer l’événement Window.DpiChanged afin d’obtenir la valeur NewDpi et calculer l’ajustement de l’échelle.

Boutons de légende système

Le système réserve le coin supérieur gauche ou supérieur droit de la fenêtre d’application pour les boutons de légende système (réduire, agrandir/restaurer, fermer). Le système conserve le contrôle de la zone de bouton de légende afin de garantir qu’une fonctionnalité minimale est fournie pour le déplacement, la réduction, l’agrandissement et la fermeture de la fenêtre. Le système dessine le bouton Fermer en haut à droite pour les langues de gauche à droite, et en haut à gauche pour les langues de droite à gauche.

Vous pouvez dessiner du contenu sous la zone du contrôle de légende, comme l’arrière-plan de votre application, mais vous ne devez pas y placer d’interface utilisateur avec laquelle vous prévoyez que l’utilisateur soit en mesure d’interagir. Cette zone ne reçoit aucune entrée, car l’entrée pour les contrôles de légende est gérée par le système.

Ces lignes de l’exemple précédent montrent les colonnes de remplissage dans le XAML qui définit la barre de titre. L’utilisation de colonnes de remplissage au lieu de marges garantit que l’arrière-plan peint la zone sous les boutons de contrôle de légende (pour les boutons transparents). L’utilisation de colonnes de remplissage de droite et de gauche garantit que votre barre de titre se comporte correctement à la fois dans les dispositions de droite à gauche et de gauche à droite.

<Grid.ColumnDefinitions>
    <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
    <ColumnDefinition/>
    <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
</Grid.ColumnDefinitions>

Les dimensions et la position de la zone du contrôle de légende sont communiquées par la classe AppWindowTitleBar afin que vous puissiez en tenir compte dans la disposition de votre interface utilisateur de barre de titre. La largeur de la région réservée de chaque côté est donnée par les propriétés LeftInset ou RightInset, et sa hauteur est donnée par la propriété Height.

Voici comment la largeur des colonnes de remplissage est spécifiée lorsque les régions de glissement sont calculées et définies.

RightPaddingColumn.Width = 
    new GridLength(m_AppWindow.TitleBar.RightInset / scaleAdjustment);
LeftPaddingColumn.Width = 
    new GridLength(m_AppWindow.TitleBar.LeftInset / scaleAdjustment);

Important

Consultez les informations importantes dans la section Contenu interactif sur la façon dont la mise à l’échelle de l’affichage affecte ces valeurs.

Prise en charge des barres de titre hautes pour les barres de titre personnalisées

Lorsque vous ajoutez du contenu interactif comme une zone de recherche ou une image de personne dans la barre de titre, nous vous recommandons d’augmenter la hauteur de votre barre de titre afin d’offrir davantage d’espace à ces éléments. Une barre de titre plus haute facilite également la manipulation tactile. La propriété AppWindowTitleBar.PreferredHeightOption vous permet d’augmenter la hauteur de la barre de titre de la hauteur standard, qui est la valeur par défaut, à une hauteur plus grande. Lorsque vous sélectionnez le mode barre de titre Tall, les boutons de légende dessinés par le système en tant que superposition dans la zone cliente sont affichés plus hauts avec leurs glyphes min/max/fermé centrés. Si vous n’avez pas spécifié de zone de glissement, le système en dessine une qui étend la largeur de votre fenêtre et la hauteur déterminée par la valeur PreferredHeightOption que vous avez définie.

Cet exemple montre comment définir la propriété PreferredHeightOption.

// A taller title bar is only supported when drawing a fully custom title bar.
if (ExtendsContentIntoTitleBar == true)
{
    m_AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
}

Attention

La propriété AppWindowTitleBar.ExtendsContentIntoTitleBar doit être true avant que vous définissiez la propriété PreferredHeightOption. Si vous tentez de définir PreferredHeightOption pendant que ExtendsContentIntoTitleBar est false, une exception est levée.

Couleur et transparence dans les boutons de légende

Lorsque vous étendez le contenu de votre application dans la zone de barre de titre, vous pouvez rendre l’arrière-plan des boutons de légende transparents afin que l’arrière-plan de votre application soit visible. Vous définissez généralement l’arrière-plan sur Colors.Transparent pour une transparence totale. Pour une transparence partielle, définissez le canal alpha de la couleur (Color) sur laquelle vous définissez la propriété.

Ces propriétés de barre de titre peuvent être transparentes :

Toutes les autres propriétés de couleur continueront à ignorer le canal alpha. Si ExtendsContentIntoTitleBar a la valeur false, le canal alpha est toujours ignoré pour toutes les propriétés de couleur AppWindowTitleBar.

La couleur d’arrière-plan du bouton n’est pas appliquée aux étatshover et pressed du bouton Fermer. Le bouton Fermer utilise toujours la couleur définie par le système pour ces états.

Conseil

Mica est un excellent matériau qui permet de distinguer la fenêtre qui a le focus. Nous le recommandons comme arrière-plan pour les fenêtres à longue durée de vie dans Windows 11. Si vous avez appliqué Mica dans la zone cliente de votre fenêtre, vous pouvez l’étendre dans la zone de barre de titre et rendre vos boutons de légende transparents afin que le Mica soit visible. Pour plus d’informations, consultez Matériau Mica.

Estomper la barre de titre lorsque la fenêtre est inactive

Le caractère actif ou inactif de votre fenêtre doit être une évidence. Au minimum, vous devez modifier la couleur du texte, des icônes et des boutons dans votre barre de titre.

Pour les applications XAML, gérez l’événement Window.Activated pour déterminer l’état d’activation de la fenêtre et mettez à jour l’interface utilisateur de la barre de titre en fonction des besoins.

public MainWindow()
{
    ...
    Activated += MainWindow_Activated;
}

private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
    if (args.WindowActivationState == WindowActivationState.Deactivated)
    {
        TitleBarTextBlock.Foreground =
            (SolidColorBrush)App.Current.Resources["WindowCaptionForegroundDisabled"];
    }
    else
    {
        TitleBarTextBlock.Foreground =
            (SolidColorBrush)App.Current.Resources["WindowCaptionForeground"];
    }
}

Pour d’autres infrastructures d’interface utilisateur, gérez un événement afin de déterminer l’état d’activation de la fenêtre et mettez à jour l’interface utilisateur de la barre de titre en fonction des besoins. La façon dont vous déterminez l’état de la fenêtre dépend de l’infrastructure d’interface utilisateur que vous utilisez pour votre application.

Réinitialiser la barre de titre

Pour procéder à une réinitialisation ou basculer vers la barre de titre système pendant l’exécution de votre application, vous pouvez appeler AppWindowTitleBar.ResetToDefault.

m_AppWindow.TitleBar.ResetToDefault();

Pour les applications XAML, vous pouvez également réinitialiser la barre de titre des manières suivantes :

  • Appelez SetTitleBar pour basculer vers un nouvel élément de barre de titre pendant l’exécution de votre application.
  • Appelez SetTitleBar avec null comme paramètre pour rétablir les régions de glissement AppWindowTitleBar par défaut.
  • Appelez SetTitleBar avec null comme paramètre et affectez la valeur false à ExtendsContentIntoTitleBar pour revenir à la barre de titre système par défaut.

Afficher et masquer la barre de titre

Si vous ajoutez la prise en charge des modes plein écran etsuperposition compacte à votre application, vous devrez peut-être apporter des modifications à votre barre de titre lorsque votre application bascule entre ces modes. La fenêtre XAML ne fournit aucune API pour prendre en charge le mode plein écran ; vous pouvez utiliser les API AppWindow pour cela.

Lorsque votre application s’exécute en mode plein écran, le système masque la barre de titre et les boutons de contrôle de légende. Vous pouvez gérer l’événement AppWindow.Changed et vérifier la propriété DidPresenterChange des arguments d’événement pour déterminer si vous devez afficher, masquer ou modifier la barre de titre en réponse à une nouvelle présentation de fenêtre.

Cet exemple montre comment gérer l’événement Changed pour afficher et masquer l’élément AppTitleBar des exemples précédents. Si la fenêtre est mise en mode superposition compacte, la barre de titre système par défaut est rétablie (ou vous pouvez fournir une barre de titre personnalisée optimisée pour la superposition compacte).

public MainWindow()
{
    this.InitializeComponent();

    m_AppWindow = this.AppWindow;
    m_AppWindow.Changed += AppWindow_Changed;
}

private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
    if (args.DidPresenterChange)
    {
        switch (sender.Presenter.Kind)
        {
            case AppWindowPresenterKind.CompactOverlay:
                // Compact overlay - hide custom title bar
                // and use the default system title bar instead.
                AppTitleBar.Visibility = Visibility.Collapsed;
                sender.TitleBar.ResetToDefault();
                break;

            case AppWindowPresenterKind.FullScreen:
                // Full screen - hide the custom title bar
                // and the default system title bar.
                AppTitleBar.Visibility = Visibility.Collapsed;
                sender.TitleBar.ExtendsContentIntoTitleBar = true;
                break;

            case AppWindowPresenterKind.Overlapped:
                // Normal - hide the system title bar
                // and use the custom title bar instead.
                AppTitleBar.Visibility = Visibility.Visible;
                sender.TitleBar.ExtendsContentIntoTitleBar = true;
                break;

            default:
                // Use the default system title bar.
                sender.TitleBar.ResetToDefault();
                break;
        }
    }
}

Remarque

Les modes plein écran et superposition compacte sont possibles uniquement s’ils sont pris en charge par votre application. Pour plus d’informations, consultez Gérer les fenêtres d’application, FullScreenPresenter et CompactOverlayPresenter.

Pratiques conseillées et déconseillées

  • Le caractère actif ou inactif de votre fenêtre doit être une évidence. Au minimum, modifiez la couleur du texte, des icônes et des boutons dans votre barre de titre.
  • Définissez une zone de glissement le long du bord supérieur du canevas de l’application. La mise en correspondance de l’emplacement des barres de titre système permet aux utilisateurs de les trouver plus facilement.
  • Définissez une zone de glissement qui correspond à la barre de titre visuelle (le cas échéant) sur le canevas de l’application.

Exemple de personnalisation complète

Cet exemple montre tout le code décrit dans la section Personnalisation complète.

<Window
    x:Class="WinUI3_CustomTitleBar.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Grid x:Name="AppTitleBar"
      Height="48">
            <Grid.ColumnDefinitions>
                <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
                <ColumnDefinition x:Name="IconColumn" Width="Auto"/>
                <ColumnDefinition x:Name="TitleColumn" Width="Auto"/>
                <ColumnDefinition x:Name="LeftDragColumn" Width="*"/>
                <ColumnDefinition x:Name="SearchColumn" Width="4*" MinWidth="220"/>
                <ColumnDefinition x:Name="RightDragColumn" Width="*" MinWidth="48"/>
                <ColumnDefinition x:Name="AccountColumn" Width="Auto"/>
                <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
            </Grid.ColumnDefinitions>
            <Image x:Name="TitleBarIcon" 
           Source="ms-appx:///Assets/StoreLogo.png"
           Grid.Column="1"
           Width="16" Height="16"
           Margin="8,0,4,0"/>
            <TextBlock x:Name="TitleBarTextBlock"
                       Text="App title" 
                       Style="{StaticResource CaptionTextBlockStyle}"
                       Grid.Column="2"
                       VerticalAlignment="Center">
            </TextBlock>
            <AutoSuggestBox x:Name="TitleBarSearchBox" 
                            Grid.Column="4" 
                            QueryIcon="Find"
                            PlaceholderText="Search"
                            VerticalAlignment="Center"
                            MaxWidth="600"/>
            <PersonPicture x:Name="PersonPic" 
                           Grid.Column="6" 
                           Height="32" Margin="0,0,16,0"/>
        </Grid>

        <NavigationView Grid.Row="1"
                        IsBackButtonVisible="Collapsed"
                        IsSettingsVisible="False">
            <StackPanel>
                <TextBlock Text="Content" 
                           Style="{ThemeResource TitleTextBlockStyle}"
                           Margin="32,0,0,0"/>
                <StackPanel Grid.Row="1" VerticalAlignment="Center">
                    <Button Margin="4" x:Name="CompactoverlaytBtn"
                            Content="Enter CompactOverlay"
                            Click="SwitchPresenter"/>
                    <Button Margin="4" x:Name="FullscreenBtn" 
                            Content="Enter FullScreen"
                            Click="SwitchPresenter"/>
                    <Button Margin="4" x:Name="OverlappedBtn"
                            Content="Revert to default (Overlapped)"
                            Click="SwitchPresenter"/>
                </StackPanel>
            </StackPanel>
        </NavigationView>
    </Grid>
</Window>
using Microsoft.UI.Input;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System;
using Windows.ApplicationModel;
using Rect = Windows.Foundation.Rect;

public sealed partial class MainWindow : Window
{
    private AppWindow m_AppWindow;

    public MainWindow()
    {
        this.InitializeComponent();

        // Assumes "this" is a XAML Window. In projects that don't use 
        // WinUI 3 1.3 or later, use interop APIs to get the AppWindow.
        m_AppWindow = this.AppWindow;
        m_AppWindow.Changed += AppWindow_Changed;
        Activated += MainWindow_Activated;
        AppTitleBar.SizeChanged += AppTitleBar_SizeChanged;
        AppTitleBar.Loaded += AppTitleBar_Loaded;

        ExtendsContentIntoTitleBar = true;
        if (ExtendsContentIntoTitleBar == true)
        {
            m_AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
        }
        TitleBarTextBlock.Text = AppInfo.Current.DisplayInfo.DisplayName;
    }

    private void AppTitleBar_Loaded(object sender, RoutedEventArgs e)
        {
            if (ExtendsContentIntoTitleBar == true)
            {
                // Set the initial interactive regions.
                SetRegionsForCustomTitleBar();
            }
        }

    private void AppTitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (ExtendsContentIntoTitleBar == true)
            {
                // Update interactive regions if the size of the window changes.
                SetRegionsForCustomTitleBar();
            }
        }

    private void SetRegionsForCustomTitleBar()
    {
        // Specify the interactive regions of the title bar.

        double scaleAdjustment = AppTitleBar.XamlRoot.RasterizationScale;

        RightPaddingColumn.Width = new GridLength(m_AppWindow.TitleBar.RightInset / scaleAdjustment);
        LeftPaddingColumn.Width = new GridLength(m_AppWindow.TitleBar.LeftInset / scaleAdjustment);

        // Get the rectangle around the AutoSuggestBox control.
        GeneralTransform transform = TitleBarSearchBox.TransformToVisual(null);
        Rect bounds = transform.TransformBounds(new Rect(0, 0,
                                                         TitleBarSearchBox.ActualWidth,
                                                         TitleBarSearchBox.ActualHeight));
        Windows.Graphics.RectInt32 SearchBoxRect = GetRect(bounds, scaleAdjustment);

        // Get the rectangle around the PersonPicture control.
        transform = PersonPic.TransformToVisual(null);
        bounds = transform.TransformBounds(new Rect(0, 0,
                                                    PersonPic.ActualWidth,
                                                    PersonPic.ActualHeight));
        Windows.Graphics.RectInt32 PersonPicRect = GetRect(bounds, scaleAdjustment);

        var rectArray = new Windows.Graphics.RectInt32[] { SearchBoxRect, PersonPicRect };

        InputNonClientPointerSource nonClientInputSrc =
            InputNonClientPointerSource.GetForWindowId(this.AppWindow.Id);
        nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, rectArray);
    }

    private Windows.Graphics.RectInt32 GetRect(Rect bounds, double scale)
    {
        return new Windows.Graphics.RectInt32(
            _X: (int)Math.Round(bounds.X * scale),
            _Y: (int)Math.Round(bounds.Y * scale),
            _Width: (int)Math.Round(bounds.Width * scale),
            _Height: (int)Math.Round(bounds.Height * scale)
        );
    }

    private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
    {
        if (args.WindowActivationState == WindowActivationState.Deactivated)
        {
            TitleBarTextBlock.Foreground =
                (SolidColorBrush)App.Current.Resources["WindowCaptionForegroundDisabled"];
        }
        else
        {
            TitleBarTextBlock.Foreground =
                (SolidColorBrush)App.Current.Resources["WindowCaptionForeground"];
        }
    }

    private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
    {
        if (args.DidPresenterChange)
        {
            switch (sender.Presenter.Kind)
            {
                case AppWindowPresenterKind.CompactOverlay:
                    // Compact overlay - hide custom title bar
                    // and use the default system title bar instead.
                    AppTitleBar.Visibility = Visibility.Collapsed;
                    sender.TitleBar.ResetToDefault();
                    break;

                case AppWindowPresenterKind.FullScreen:
                    // Full screen - hide the custom title bar
                    // and the default system title bar.
                    AppTitleBar.Visibility = Visibility.Collapsed;
                    sender.TitleBar.ExtendsContentIntoTitleBar = true;
                    break;

                case AppWindowPresenterKind.Overlapped:
                    // Normal - hide the system title bar
                    // and use the custom title bar instead.
                    AppTitleBar.Visibility = Visibility.Visible;
                    sender.TitleBar.ExtendsContentIntoTitleBar = true;
                    break;

                default:
                    // Use the default system title bar.
                    sender.TitleBar.ResetToDefault();
                    break;
            }
        }
    }

    private void SwitchPresenter(object sender, RoutedEventArgs e)
    {
        if (AppWindow != null)
        {
            AppWindowPresenterKind newPresenterKind;
            switch ((sender as Button).Name)
            {
                case "CompactoverlaytBtn":
                    newPresenterKind = AppWindowPresenterKind.CompactOverlay;
                    break;

                case "FullscreenBtn":
                    newPresenterKind = AppWindowPresenterKind.FullScreen;
                    break;

                case "OverlappedBtn":
                    newPresenterKind = AppWindowPresenterKind.Overlapped;
                    break;

                default:
                    newPresenterKind = AppWindowPresenterKind.Default;
                    break;
            }

            // If the same presenter button was pressed as the
            // mode we're in, toggle the window back to Default.
            if (newPresenterKind == AppWindow.Presenter.Kind)
            {
                AppWindow.SetPresenter(AppWindowPresenterKind.Default);
            }
            else
            {
                // Else request a presenter of the selected kind
                // to be created and applied to the window.
                AppWindow.SetPresenter(newPresenterKind);
            }
        }
    }
}