Personalización de la barra de título

Windows proporciona una barra de título predeterminada para cada ventana y permite personalizarla para que se ajuste a la personalidad de la aplicación. La barra de título predeterminada incluye algunos componentes estándar y funcionalidades básicas, como arrastrar y cambiar el tamaño de la ventana.

A Windows app showing the title bar

Consulta el artículo sobre el diseño de la barra de título para obtener las instrucciones necesarias para personalizar la barra de título de cualquier aplicación, el contenido que se puede incluir en el área de la barra de título y los patrones de interfaz de usuario recomendados.

Importante

En este artículo se muestra cómo personalizar la barra de título en las aplicaciones que usan el SDK de Aplicaciones para Windows, con o sin WinUI 3. En el caso de las aplicaciones que usan UWP y WinUI 2, consulte Personalización de la barra de título para UWP.

Componentes de la barra de título

En esta lista se describen los componentes de la barra de título estándar.

  • Rectángulo de la barra de título
  • Texto de título
  • Icono del sistema
  • Menú del sistema: para acceder a él, es preciso hacer clic en el icono de la aplicación o hacer clic con el botón derecho en la barra de título
  • Controles de título
    • Botón Minimizar
    • Botón Maximizar o restaurar
    • Botón Cerrar

Basado en ventanas

La funcionalidad basada en ventanas del SDK de Aplicaciones para Windows se realiza a través de la clase Microsoft.UI.Windowing.AppWindow, que se basa en el modelo HWND de Win32. En la aplicación, hay una asignación 1:1 entre un AppWindow y un HWND de nivel superior. AppWindow y sus clases relacionadas proporcionan API que permiten administrar muchos aspectos de las ventanas de nivel superior de la aplicación, entre los que se incluye la personalización de la barra de título. La barra de título predeterminada que proporciona Windows se puede modificar para que se combine con el resto de la interfaz de usuario, o bien ampliar el lienzo de la aplicación al área de la barra de título e incluir su propio contenido en la barra de título.

La funcionalidad basada en ventanas de WinUI 3 se realiza mediante la clase Microsoft.UI.Xaml.Window, que también se basa en el modelo HWND de Win32. En el caso de las aplicaciones XAML que usan WinUI 3, las API Window de XAML permiten personalizar de una forma más sencilla la barra de título y, al mismo tiempo, permiten acceder a las API de AppWindow cuando sea necesario.

Uso de AppWindow

Las API de AppWindow se pueden usar con cualquier marco de interfaz de usuario que el SDK de Aplicaciones para Windows admita ( Win32, WPF, WinForms o WinUI 3) y se pueden adoptar de forma incremental, es decir usar solo las API que se necesiten.

Si usa el lenguaje XAML de WinUI 3 como marco de la interfaz de usuario de la aplicación, las API Window y AppWindow estarán disponibles. A partir de la versión 1.4 de SDK de Aplicaciones para Windows, Window y AppWindow de XAML usan el mismo objeto AppWindowTitleBar para la personalización de la barra de título. Use la propiedad Window.AppWindow para obtener un objeto AppWindow de una ventana XAML existente. Con este objeto AppWindow tiene acceso a las API de personalización de la barra de título. Para acceder a más características de la barra de título, puede usar las API de AppWindow desde una ventana de XAML como esta: AppWindow.TitleBar.ForegroundColor = Colors.White;.

Si no usa WinUI 3 1.3, o cualquier versión posterior, utilice las API de interoperabilidad para obtener AppWindow y las API de AppWindow para personalizar la barra de título. Para más información sobre las API de interoperabilidad, consulte la sección Marco de interfaz de usuario e interoperabilidad de HWND del artículo Administrar ventanas de aplicaciones (SDK de Aplicaciones para Windows) y el ejemplo de la galería de ventanas.

Cantidad de personalización de la barra de título

Se pueden aplicar dos niveles de personalización a la barra de título: aplicar pequeñas modificaciones a la barra de título predeterminada, o bien ampliar el lienzo de la aplicación al área de la barra de título e incluir contenido propio en la barra de título.

Simple

Si va a realizar una personalización sencilla, como cambiar el color de la barra de título, puede establecer propiedades en el objeto AppWindowTitleBar para especificar los colores que desea usar para los elementos de la barra de título. En este caso, el sistema sigue siendo el responsable de los restantes aspectos de la barra de título, como dibujar el título de la aplicación y definir las áreas de arrastre.

Completo

La otra opción es ocultar la barra de título del sistema predeterminada y reemplazarla por su propio contenido personalizado. Por ejemplo, en el área de la barra de título se pueden colocar texto, un cuadro de búsqueda o menús personalizados. También tendrás que usar esta opción para ampliar un fondo de material, como Mica, en el área de la barra de título.

Si decide realizar una personalización completa, es responsabilidad suya colocar contenido en el área de la barra de título y puede definir sus propias regiones de arrastre. Los controles de título (botones Cerrar, Minimizar y Maximizar del sistema) siguen estando disponibles y los controla el sistema, pero los elementos como el título de la aplicación no. Tendrá que crear esos elementos a medida que la aplicación los necesite.

Personalización simple

Si solo quiere personalizar el título, los colores o el icono de la barra de título, puede establecer propiedades en el objeto barra de título de la ventana de la aplicación.

Título

De forma predeterminada, la barra de título muestra el tipo de aplicación como título de la ventana (por ejemplo, "WinUI Desktop"). Debe actualizar el título de la ventana para que muestre un nombre significativo para la aplicación.

Una aplicación XAML tiene un nombre para mostrar que se establece en el archivo Package.appxmanifest. Puede obtener este valor y usarlo para establecer la propiedad Title así.

Title = AppInfo.Current.DisplayInfo.DisplayName;

Para cambiar el título de la ventana, establezca la propiedad Window.Title en un valor de texto de una sola línea, como se muestra aquí.

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

Para cambiar el título de la ventana mediante las API de AppWindow, establezca la propiedad AppWindow.Title en un valor de texto de una sola línea, como se muestra aquí. En este ejemplo se muestra cómo usar las API de interoperabilidad para obtener AppWindow, lo que es necesario si la aplicación no usa WinUI 3 1.3, o cualquier versión posterior.

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);
}

Colores

Para personalizar los colores predeterminados de la barra de título o cambiar el icono de ventana predeterminado, deberá usar las API de AppWindow, o bien personalizar completamente la barra de título.

En este ejemplo se muestra cómo obtener una instancia de AppWindowTitleBar y establecer sus propiedades de color.

Importante

La personalización de colores se omite cuando la aplicación se ejecuta en 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;
}

Hay varias cosas que debe tener en cuenta al establecer los colores de la barra de título:

  • El color de fondo del botón no se aplica a los estados mantener el puntero y presionado del botón Cerrar. El botón Cerrar siempre usa el color definido por el sistema para esos estados.
  • Si se establece una propiedad de color en null, se restablece al color predeterminado del sistema.
  • No se pueden establecer colores transparentes. Se ignora el canal alfa del color.

Windows ofrece a los usuarios la opción de aplicar su color de énfasis seleccionado a la barra de título. Si establece algún color para la barra de título, es aconsejable que establezca explícitamente todos los colores. Esto garantiza que no se produzcan combinaciones de colores no deseadas debido a la configuración de colores definida por el usuario.

Icono y menú del sistema

El icono del sistema no solo se puede ocultar, también se puede reemplazar por un icono personalizado. El icono del sistema muestra el menú del sistema cuando se hace clic con el botón derecho o se pulsa una vez. Cierra la ventana cuando se hace doble clic o pulsa en ella.

Para mostrar u ocultar el icono del sistema y los comportamientos asociados, establezca la propiedad IconShowOptions de la barra de título.

m_TitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;

Para usar un icono de ventana personalizado, llame a uno de los métodos AppWindow.SetIcon para establecer el nuevo icono.

  • SetIcon(String)

    Actualmente, el método SetIcon(String) solo funciona con archivos .ico. La cadena que se pasa a este método es la ruta de acceso completa al archivo .ico.

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

    Si ya tiene un controlador para un icono (HICON) de una de las funciones Icon como CreateIcon, puede usar la API de interoperabilidad GetIconIdFromIcon para obtener IconId. Luego, puede pasar IconId al método SetIcon(IconId) para establecer el icono de la ventana.

    m_AppWindow.SetIcon(iconId));
    

Personalización completa

Si realiza una personalización completa de la barra de título, el área cliente de la aplicación se extiende para cubrir toda la ventana, incluido el área de la barra de título. Usted es responsable tanto del dibujo como del control de entrada en toda la ventana, excepto los botones de título, que los proporciona la ventana.

Para ocultar la barra de título del sistema y extender el contenido al área de la barra de título, establezca la propiedad que extiende el contenido de la aplicación al área de la barra de título en true. En las aplicaciones de XAML, esta propiedad se puede establecer en el método de la OnLaunched aplicación (App.xaml.cs) o en la primera página de la aplicación.

Sugerencia

Consulte la sección Ejemplo de personalización completa para ver todo el código a la vez.

En este ejemplo se muestra cómo establecer la propiedad Window.ExtendsContentIntoTitleBar en true.

public MainWindow()
{
    this.InitializeComponent();

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

Precaución

ExtendsContentIntoTitleBar se muestra en XAML IntelliSense para Window, pero si se establece en XAML, se produce un error. Establezca esta propiedad en el código.

En este ejemplo se muestra cómo obtener AppWindowTitleBar y establecer la propiedad AppWindow.ExtendsContentIntoTitleBar en true. En este ejemplo se muestra cómo usar las API de interoperabilidad para obtener AppWindow, algo que es necesario si la aplicación no usa WinUI 3 1.3, o cualquier versión posterior.

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);
}

Contenido de la barra de título y la región de arrastre predeterminada

Cuando la aplicación se extiende al área de la barra de título, usted es el responsable de definir y administrar la interfaz de usuario de la barra de título. Normalmente, esto incluye, como mínimo, especificar el texto del título y la región de arrastre. La región de arrastre de la barra de título define el lugar o lugares en que el usuario puede hacer clic y arrastrar para mover la ventana. También es donde el usuario puede hacer clic con el botón derecho para mostrar el menú del sistema.

Para más información sobre el contenido aceptable de la barra de título y los patrones de interfaz de usuario recomendados, consulte Diseño de la barra de título.

En este ejemplo se muestra el XAML de la interfaz de usuario de una barra de título personalizada sin contenido interactivo.

<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>

Importante

LeftPaddingColumn y RightPaddingColumn se usan para reservar espacio para los botones de título. Los valores Width de estas columnas se establecen en el código, lo que se muestra más adelante. En la sección Botones de título del sistema encontrará el código y una explicación.

Las aplicaciones XAML tienen un nombre para mostrar que se establece en el archivo Package.appxmanifest. Así puede obtener este valor y usarlo en la barra de título personalizada.

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

Al extender el contenido al área de la barra de título, la barra de título del sistema se oculta y se crea una clase AppWindowTitleBar predeterminada que proporciona botones de título y una región de arrastre a lo largo de todo el ancho de la pantalla que es idéntica a la barra de título del sistema. Si no coloca contenido interactivo en la barra de título, puede dejar esta región de arrastre predeterminada tal como está. Si coloca contenido interactivo en la barra de título, debe especificar qué regiones son interactivas, lo que se tratará en la siguiente sección.

Precaución

Cuando se definen regiones de arrastre personalizadas, estas no tienen que estar en la parte superior de la ventana, en el área de la barra de título predeterminada; cualquier parte de la interfaz de usuario se puede definir como región de arrastre. Sin embargo, la colocación de regiones de arrastre en diferentes lugares podría dificultar su detección a los usuarios.

Contenido interactivo

Se pueden colocar controles interactivos, como botones, menús o un cuadro de búsqueda, en la parte superior de la aplicación para que parezca que están en la barra de título. Sin embargo, es preciso especificar qué regiones son interactivas, con el fin de tener la certeza de que los elementos interactivos reciben la entrada del usuario y, al mismo tiempo, permiten a los usuarios mover la ventana.

A Windows app with a search box in the title bar

Cuando se agrega contenido interactivo al área de la barra de título, es preciso usar la clase InputNonClientPointerSource para especificar aquellas áreas en las que la entrada se pasa al control interactivo, en lugar de que la controle la barra de título. Para establecer las regiones interactivas, llame al método InputNonClientPointerSource.SetRegionRects. Este método toma un valor que especifica el tipo de región que se establece (en este caso, Passthrough) y una matriz de rectángulos, cada uno de los cuales define una región Passthrough. Cuando cambie el tamaño de la barra de título, es preciso volver a calcular las regiones interactivas para que se ajusten al nuevo tamaño y llamar a SetRegionRects con los nuevos valores.

En este ejemplo se muestra una interfaz de usuario de una barra de título personalizada con un cuadro de búsqueda y un control de cuenta PersonPicture. Muestra cómo calcular y establecer los rectángulos interactivos de estos controles para que la entrada pase por ellos.

Hay algunas aspectos de este código que se deben tener en cuenta:

  • Establezca el valor de Alto de la cuadrícula de AppTitleBar en 48 para seguir las instrucciones de diseño de la barra de título para incluir contenido interactivo.
  • Establezca PreferredHeightOption en Tall para que los botones de título tengan el mismo alto que la barra de título.
  • Para facilitar el cambio de tamaño de los controles y el cálculo de regiones, use Grid con varias columnas con nombre para el diseño.
  • Use el tamaño de estrella (*) con MinWidth para la columna que contiene AutoSuggestBox, con el fin de que cambie automáticamente de tamaño cuando lo haga la ventana.
  • Establezca MinWidth en RightDragColumn para reservar un área pequeña que siempre se pueda arrastrar, incluso cuando se cambia el tamaño de la ventana.
  • Establezca ExtendsContentIntoTitleBar en true en el constructor MainWindow. Si lo establece en el código al que se va a llamar más adelante, es posible que la barra de título del sistema predeterminada se muestre primero y luego se oculte.
  • Realice la llamada inicial para calcular las regiones interactivas después de que se haya cargado el elemento AppTitleBar. De lo contrario, no hay ninguna garantía de que los elementos usados para el cálculo tengan sus valores correctos.
  • No actualice los cálculos del rectángulo interactivo hasta que el elemento AppTitleBar haya cambiado de tamaño (AppTitleBar_SizeChanged). Si depende del evento Changed de la ventana, habrá situaciones (como maximizar o minimizar la ventana) en las que el evento se produzca antes de se cambia el tamaño de AppTitleBar y los cálculos usarán valores incorrectos.
  • No establezca las áreas interactivas o de arrastre personalizadas hasta que compruebe ExtendsContentIntoTitleBar para confirmar que se usa una barra de título personalizada.
<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>

Este código muestra cómo calcular y establecer las regiones interactivas que corresponden a los controles AutoSuggestBox y 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)
        );
    }
}

Advertencia

AppWindow usa píxeles físicos para lograr compatibilidad con marcos de interfaz de usuario que no usan coordenadas lógicas. Si usa WPF o WinUI 3, RightInset, LeftInsety los valores usados para calcular regiones deben ajustarse si la escala de pantalla no es del 100 %. En este ejemplo, se obtiene un valor scaleAdjustment para tener cuenta la configuración de la escala de visualización.

Botones de título del sistema

El sistema se reserva la esquina superior izquierda o superior derecha de la ventana de la aplicación para los botones de título del sistema (minimizar, maximizar o restaurar, cerrar). El sistema conserva el control del área de botones de título para garantizar que se proporciona la funcionalidad mínima para arrastrar, minimizar, maximizar y cerrar la ventana. El sistema dibuja el botón Cerrar en la esquina superior derecha para los idiomas que se leen de izquierda a derecha y en la esquina superior izquierda para los idiomas que se leen de derecha a izquierda.

Puede dibujar contenido debajo del área de control de título, como el fondo de la aplicación, pero no debe colocar ninguna interfaz de usuario con la que espere que el usuario interactúe. No recibe ninguna entrada porque el sistema controla la entrada de los controles de título.

Estas líneas del ejemplo anterior muestran las columnas de relleno en el XAML que define la barra de título. El uso de columnas de relleno en lugar de márgenes garantiza que el fondo pinta el área debajo de los botones de control de título (para botones transparentes). El uso de columnas de relleno a la derecha e izquierda garantiza que la barra de título se comporta correctamente en diseños tanto de derecha a izquierda como de izquierda a derecha.

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

La clase AppWindowTitleBar comunica las dimensiones y la posición del área de control de título para que pueda tenerla en cuenta en el diseño de la interfaz de usuario de la barra de título. Las propiedades LeftInset o RightInset proporcionan el ancho de la región reservada en cada lado, mientras que la propiedad Height proporciona su alto.

Aquí se muestra cómo se especifica el ancho de las columnas de relleno cuando se calculan y establecen las regiones de arrastre.

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

Importante

En la sección Contenido interactivo puede encontrar información importante sobre cómo afecta el escalado de pantalla a estos valores.

Compatibilidad de las barras de título altas con las barras de título personalizadas

Si se va a agregar contenido interactivo, como un cuadro de búsqueda o una imagen de una persona, a la barra de título, es aconsejable aumentar el alto de la barra, con el fin de que haya más espacio para estos elementos. Las barras de título altas también facilitan la manipulación táctil. La propiedad AppWindowTitleBar.PreferredHeightOption le ofrece la opción de aumentar el alto estándar, que es el valor predeterminado, a un valor más elevado. Al seleccionar el modo de barra de título Tall, los botones de subtítulo que el sistema dibuja superpuestos en el área de cliente se representan más altos con sus glifos mín./máx./cerrar centrados. Si no ha especificado ninguna región de arrastre, el sistema dibujará una que aumente el ancho de la ventana y el alto determinado por el valor de PreferredHeightOption que haya establecido.

En este ejemplo se muestra cómo se puede establecer la propiedad PreferredHeightOption.

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

Precaución

La propiedad AppWindowTitleBar.ExtendsContentIntoTitleBar debe ser true antes de establecer la propiedad PreferredHeightOption. Si intenta establecer PreferredHeightOption mientras ExtendsContentIntoTitleBar sea false, se producirá una excepción.

Color y transparencia en los botones de título

Al extender el contenido de la aplicación al área de la barra de título, puedes hacer que el fondo de los botones de título sea transparente, con el fin de que se vea el fondo de la aplicación. Para lograr una transparencia completa, lo habitual es establecer el fondo en Colors.Transparent. Si lo que se desea es una transparencia parcial, en el canal alfa seleccione el color que establezca para la propiedad.

Estas propiedades de la barra de título pueden ser transparentes:

Las restantes propiedades de color seguirán ignorando el canal alfa. Si ExtendsContentIntoTitleBar se establece en false, el canal alfa siempre se omite en todas las propiedades de color de AppWindowTitleBar.

El color de fondo del botón no se aplica a los estados mantener el puntero y presionado del botón Cerrar. El botón Cerrar siempre usa el color definido por el sistema para esos estados.

Sugerencia

Mica es un excelente material que ayuda a distinguir la ventana más importante en cada momento. Se recomienda usarlo como fondo para ventanas de larga duración en Windows 11. Si ha aplicado Mica en el área de cliente de la ventana, puede extenderla al área de la barra de título y hacer que los botones de título sean transparentes para que se vea la Mica. Para más información, consulte el artículo Material Mica.

Atenuación de la barra de título cuando la ventana está inactiva

Debe ser obvio en qué momento la ventana está activa o inactiva. Para ello, como mínimo, debe cambiar el color del texto, los iconos y los botones de la barra de título.

En el caso de las aplicaciones XAML, controle el evento Window.Activated para determinar el estado de activación de la ventana y actualice la interfaz de usuario de la barra de título según sea necesario.

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"];
    }
}

En otros marcos de la interfaz de usuario, controle un evento para determinar el estado de activación de la ventana y actualizar la interfaz de usuario de la barra de título según sea necesario. La forma de determinar el estado de la ventana depende del marco de la interfaz de usuario que se use para la aplicación.

Restablecimiento de la barra de título

Para restablecer la barra de titulo o cambiar a la barra de título del sistema mientras la aplicación está en funcionamiento, puede llamar a AppWindowTitleBar.ResetToDefault.

m_AppWindow.TitleBar.ResetToDefault();

En el caso de las aplicaciones XAML, la barra de título también se puede restablecer de las siguientes maneras:

  • Llame a SetTitleBar para cambiar a un nuevo elemento de la barra de título mientras se ejecuta la aplicación.
  • Llame a SetTitleBar con null como parámetro para restablecer a las regiones de arrastre predeterminadas de AppWindowTitleBar.
  • Llame a SetTitleBar con null como parámetro y establezca ExtendsContentIntoTitleBar en false para revertir a la barra de título del sistema predeterminada.

Mostrar y ocultar la barra de título

Si agrega a cualquier aplicación compatibilidad con los modos de pantalla completa o superposición compacta, es posible que tenga que realizar cambios en la barra de título cuando la aplicación cambie de uno de estos modos al otro. La ventana de XAML no proporciona ninguna API para admitir el modo a pantalla completa; puede usar las API de AppWindow para ello.

Cuando la aplicación se ejecuta en modo a pantalla completa, el sistema oculta la barra de título y los botones de control de título. Puede controlar el evento AppWindow.Changed y comprobar la propiedad DidPresenterChange de su argumentos para determinar si debe mostrar, ocultar o cambiar la barra de título en respuesta a la presentación de una nueva ventana.

En este ejemplo se muestra cómo controlar el evento Changed para mostrar y ocultar el elemento AppTitleBar de los ejemplos anteriores. Si la ventana se coloca en modo superposición compacta, la barra de título se restablece a la barra de título del sistema predeterminada (o bien se puede especificar una barra de título personalizada optimizada para la superposición compacta).

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;
        }
    }
}

Nota:

Los modos a pantalla completa y superposición compacta solo se pueden especificar si la aplicación los admite. Para más información, consulte Administración de ventanas de aplicaciones, FullScreenPresenter y CompactOverlayPresenter.

Cosas que hacer y cosas que evitar

  • Haga que sea evidente en qué momento una ventana está activa o inactiva. Como mínimo, cambie el color del texto, los iconos y los botones de la barra de título.
  • Defina una región de arrastre a lo largo del borde superior del lienzo de la aplicación. La coincidencia con la ubicación de las barras de título del sistema facilita la búsqueda por parte de los usuarios.
  • Defina una región de arrastre que coincida con la barra de título visual (si existe) en el lienzo de la aplicación.

Ejemplo de personalización completa

En este ejemplo se muestra todo el código descrito en la sección Personalización completa.

<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);
            }
        }
    }
}