Поделиться через


Настройка заголовка окна

Windows предоставляет строку заголовка по умолчанию для каждого окна и позволяет настроить ее в соответствии с личностью приложения. Строка заголовка по умолчанию поставляется с некоторыми стандартными компонентами и основными функциями, такими как перетаскивание и изменение размера окна.

Приложение Windows, показывающее строку заголовка

Рекомендуем обратиться к статье о дизайне строки заголовка для получения руководства по настройке строки заголовка вашего приложения, допустимому содержимому области строки заголовка и рекомендуемым шаблонам пользовательского интерфейса.

Внимание

В этой статье показано, как настроить заголовок для приложений, использующих пакет SDK для приложений Windows, либо без WinUI 3. Сведения о приложениях, использующих UWP и WinUI 2, см. в разделе "Настройка строки заголовка" для UWP.

Компоненты строки заголовка

В этом списке описываются компоненты стандартной строки заголовка.

  • Прямоугольник панели заголовка
  • Текст заголовка
  • Значок системы
  • Системное меню: доступ к ней, щелкнув значок приложения или щелкнув правой кнопкой мыши строку заголовка
  • Элементы управления субтитрами
    • Кнопка "Свернуть"
    • Кнопка "Максимизировать"/ "Восстановить"
    • Кнопка "Закрыть"

Управление окнами

Функции окна в пакете SDK для приложений Windows проходят через класс Microsoft.UI.Windowing.AppWindow , основанный на модели Win32 HWND. В вашем приложении имеется сопоставление 1:1 между AppWindow и HWND верхнего уровня. AppWindow и его связанные классы предоставляют API, которые позволяют управлять многими аспектами окон верхнего уровня приложения, включая настройку строки заголовка. Вы можете изменить строку заголовка по умолчанию, предоставляемую Windows, чтобы она смешивается с остальной частью пользовательского интерфейса, или расширить холст приложения в области заголовков и предоставить собственное содержимое строки заголовка.

Функции окна в WinUI 3 предоставляются через класс Microsoft.UI.Xaml.Window , который также основан на модели Win32 HWND. Для XAML-приложений, использующих WinUI 3, API окна XAML предоставляют более простой способ настройки строки заголовка, при этом позволяя при необходимости получить доступ к API AppWindow.

Как работать с AppWindow

Api AppWindow можно использовать с любой платформой пользовательского интерфейса, поддерживающей пакет SDK для приложений Windows : Win32, WPF, WinForms или WinUI 3, и их можно применять постепенно, используя только необходимые API.

Если вы используете WinUI 3 XAML в качестве платформы пользовательского интерфейса приложения, вам доступны интерфейсы API Window и AppWindow . Начиная с пакета SDK для приложений Windows 1.4, окно XAML и AppWindow используют тот же объект AppWindowTitleBar для настройки строки заголовка. Используйте свойство Window.AppWindow, чтобы получить объект AppWindow из существующего окна XAML. С помощью этого объекта AppWindow у вас есть доступ к API настройки строки заголовка. Чтобы получить доступ к дополнительным функциям строки заголовков, можно использовать API AppWindow из окна XAML, как показано ниже AppWindow.TitleBar.ForegroundColor = Colors.White;.

Если вы не используете WinUI 3 1.3 или более поздней версии, используйте API взаимодействия, чтобы получить AppWindow и использовать API AppWindow для настройки строки заголовка. Дополнительные сведения об API взаимодействия см. в разделе «Управление окнами приложений — платформа пользовательского интерфейса и взаимодействие HWND» и в примере коллекции окон.

Сколько нужно настроить строку заголовка

Существует два уровня настройки, которые можно применить к строке заголовка: применение незначительных изменений к строке заголовка по умолчанию или расширение холста приложения в области заголовка и предоставление полностью настраиваемого содержимого.

Простой

Для простой настройки, например изменение цвета строки заголовка, можно задать свойства объекта AppWindowTitleBar, чтобы указать цвета, которые вы хотите использовать для элементов строки заголовка. В этом случае система несет ответственность за все остальные аспекты строки заголовка, например рисование названия приложения и определение областей перетаскивания.

Полностью

Другой вариант — скрыть строку заголовка системы по умолчанию и заменить ее собственным пользовательским содержимым. Например, можно разместить текст, поле поиска или настраиваемые меню в области заголовка. Кроме того, вам потребуется использовать этот параметр для расширения материального фона, например Мика, в область заголовка.

При выборе полной настройки вы несете ответственность за размещение содержимого в области заголовков, и вы можете определить собственные области перетаскивания. Элементы управления заголовком (системные кнопки Закрыть, Свернуть и Развернуть) по-прежнему доступны и обрабатываются системой, но такие элементы, как название приложения, не обрабатываются. Вам потребуется создать эти элементы самостоятельно для вашего приложения по мере необходимости.

Простая настройка

Если вы хотите настроить только заголовок, цвета или значок заголовка, можно задать свойства в объекте строки заголовка для окна приложения.

Заголовок

По умолчанию в строке заголовка отображается тип приложения в качестве заголовка окна (например, "WinUI Desktop"). Необходимо обновить заголовок окна, чтобы отобразить понятное отображаемое имя приложения.

Отображаемое имя приложения XAML задается в файле \Package.appxmanifest. Это значение можно получить и использовать для задания Title свойства следующим образом.

Title = AppInfo.Current.DisplayInfo.DisplayName;

Чтобы изменить заголовок окна, задайте свойству Window.Title значение однострочного текста, как показано ниже.

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

Чтобы изменить заголовок окна с помощью API AppWindow, задайте свойству AppWindow.Title значение однострочного текста, как показано здесь. В этом примере показано, как использовать API взаимодействия для получения AppWindow, необходимого, если ваше приложение не использует WinUI 3 версии 1.3 или более поздней.

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

Цвета

Чтобы настроить цвета строк заголовка по умолчанию или изменить значок окна по умолчанию, необходимо использовать API AppWindow или полностью настроить строку заголовка.

В этом примере показано, как получить экземпляр AppWindowTitleBar и задать его свойства цвета.

Внимание

Настройка цвета игнорируется при запуске приложения в 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;
}

При настройке цветов строки заголовка следует учитывать несколько действий.

  • Цвет фона кнопки не применяется к состояниям наведения указателя и нажатия кнопки закрытия. Кнопка закрытия всегда использует системный цвет для этих состояний.
  • Установка свойства цвета на null сбрасывает его на системный цвет по умолчанию.
  • Не удается задать прозрачные цвета. Альфа-канал цвета игнорируется.

Windows предоставляет пользователю возможность применить выбранный цвет акцента к строке заголовка. Если вы задаете любой цвет строки заголовка, рекомендуется явно задать все цвета. Это гарантирует отсутствие непреднамеренных сочетаний цветов из-за пользовательских параметров цвета.

Значок и системное меню

Вы можете скрыть значок системы или заменить его пользовательским значком. Значок системы отображает системное меню при щелчке правой кнопкой мыши или касании один раз. Он закрывает окно при двойном щелчке или нажатии.

Чтобы отобразить или скрыть системный значок и связанные с ним действия, задайте свойство панели заголовка IconShowOptions .

m_TitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;

Чтобы использовать пользовательский значок окна, вызовите один из методов AppWindow.SetIcon , чтобы задать новый значок.

Полная настройка

При выборе полной настройки строки заголовка клиентская область приложения расширяется, чтобы охватывать все окно, включая область заголовка. Вы несете ответственность за рисование и обработку входных данных для всего окна, кроме кнопок заголовка, которые по-прежнему предоставляются окном.

Чтобы скрыть строку заголовка системы и расширить содержимое приложения в область строки заголовка, установите свойство, которое расширяет содержимое приложения в область true строки заголовка. В приложении XAML это свойство можно задать в методе приложения OnLaunched (App.xaml.cs) или на первой странице приложения.

Совет

Ознакомьтесь с разделом "Полный пример настройки", чтобы просмотреть весь код одновременно.

В этом примере показано, как задать для свойства Window.ExtendsContentIntoTitleBar значение true.

public MainWindow()
{
    this.InitializeComponent();

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

Внимание

ExtendsContentIntoTitleBar отображается в XAML IntelliSense для Window, но при использовании в XAML возникает ошибка. Задайте это свойство в коде.

В этом примере показано, как получить AppWindowTitleBar и задать для свойства AppWindowTitleBar.ExtendsContentIntoTitleBar значениеtrue. В этом примере показано, как использовать API взаимодействия для получения AppWindow, который необходим, если приложение не использует WinUI 3 1.3 или более поздней версии.

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

Содержимое строки заголовка и область перетаскивания по умолчанию

Когда ваше приложение распространяется на область строки заголовка, вы отвечаете за определение и управление пользовательским интерфейсом для строки заголовка. Обычно это включает в себя, как минимум, указание текста заголовка и области перетаскивания. Область перетаскивания в строке заголовка определяет, где пользователь может щелкнуть и перетащить его, чтобы переместить окно вокруг. Кроме того, пользователь может щелкнуть правой кнопкой мыши, чтобы отобразить системное меню.

Дополнительные сведения о допустимом содержимом строки заголовка и рекомендуемых шаблонах пользовательского интерфейса см. в разделе "Конструктор заголовков".

В этом примере показан XAML для пользовательского пользовательского интерфейса строки заголовка без интерактивного содержимого.

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

Внимание

LeftPaddingColumn и RightPaddingColumn используются для резервирования места для кнопок заголовка. Значения Width этих столбцов задаются в коде, который отображается позже. См. раздел "Системные кнопки заголовка" для кода и объяснения.

Приложение XAML имеет отображаемое имя, заданное в файле Package.appxmanifest. Это значение можно получить и использовать в пользовательской строке заголовка, как показано ниже.

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

При расширении содержимого в области заголовка строка заголовка скрыта, и создается панель заголовка по умолчанию AppWindowTitleBar , которая предоставляет кнопки заголовка и область перетаскивания по ширине экрана, идентичной строке заголовка системы. Если в строке заголовка не помещается интерактивное содержимое, можно оставить этот регион перетаскивания по умолчанию как есть. Если вы размещаете интерактивное содержимое в строке заголовка, необходимо указать регионы, которые являются интерактивными, которые мы рассмотрим в следующем разделе.

Внимание

При определении настраиваемых областей перетаскивания они не должны находиться в верхней части окна в области заголовка по умолчанию; Вы можете определить любую часть пользовательского интерфейса в виде области перетаскивания. Однако размещение областей для перетаскивания в разных местах может затруднить их обнаружение пользователями.

Интерактивное содержимое

Интерактивные элементы управления, такие как кнопки, меню или поле поиска, можно разместить в верхней части приложения, чтобы они отображались в строке заголовка. Однако необходимо указать, какие регионы являются интерактивными, чтобы интерактивные элементы получали входные данные пользователей, позволяя пользователям перемещать окно вокруг.

Приложение Windows с полем поиска в строке заголовка

При добавлении интерактивного содержимого в область заголовка необходимо использовать класс InputNonClientPointerSource , чтобы указать области, в которых входные данные передаются в интерактивный элемент управления, а не обрабатываются строкой заголовков. Чтобы задать интерактивные регионы, вызовите метод InputNonClientPointerSource.SetRegionRects . Этот метод принимает значение, указывающее тип заданного региона (в данном случае Passthrough), а также массив прямоугольников, каждый из которых определяет Passthrough регион. При изменении размера строки заголовка необходимо пересчитывать интерактивные регионы, чтобы соответствовать новому размеру, и вызывать SetRegionRects с новыми значениями.

В этом примере показан пользовательский пользовательский интерфейс строки заголовка с полем поиска и элементом управления учетной записью PersonPicture. В нем показано, как вычислить и задать интерактивные прямоугольники для этих элементов управления так, чтобы к ним передавались входные данные.

Ниже приведены некоторые важные моменты, которые следует заметить об этом коде:

  • Установите высоту сетки в 48, чтобы следовать рекомендациям по дизайну строки заголовка для интерактивного контента.
  • Задайте значение Tall, чтобы кнопки заголовка были той же высотой, что и строка заголовка.
  • Чтобы упростить изменение размера элементов управления и вычисление регионов, используйте Grid с несколькими именованными столбцами для макета.
  • Используйте звездочный (*) размер для столбца, содержащего AutoSuggestBox, чтобы он автоматически изменялся вместе с окном.
  • Установите MinWidth на RightDragColumn для резервирования небольшой области, чтобы она всегда оставалась доступной для перетаскивания, даже при изменении размера окна.
  • Установите ExtendsContentIntoTitleBar на true в конструкторе MainWindow. Если вы задали это в коде, который вызывается позже, сначала может отображаться системная панель заголовка по умолчанию, а затем скрываться.
  • Выполните начальный вызов для вычисления интерактивных регионов после загрузки элемента AppTitleBar. В противном случае не гарантируется, что элементы, используемые для вычисления, будут иметь правильные значения.
  • Обновите вычисления для интерактивного прямоугольника только после изменения размера элемента AppTitleBar(AppTitleBar_SizeChanged). Если вы рассчитываете на событие окна Changed, будут ситуации, такие как максимизация или сворачивание окна, когда событие происходит раньше, чем изменяется размер AppTitleBar, и вычисления будут использовать неверные значения.
  • Установите настраиваемые области перетаскивания и интерактивные зоны только после того, как проверите ExtendsContentIntoTitleBar, что используется настраиваемая строка заголовка.
<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>

В этом коде показано, как вычислить и задать интерактивные регионы, соответствующие элементам управления AutoSuggestBox и 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)
        );
    }
}

Предупреждение

AppWindow использует физические пиксели для совместимости с платформами пользовательского интерфейса, которые не используют логические координаты. Если используется WPF или WinUI 3, RightInset, LeftInset, и значения, используемые для вычисления регионов, необходимо настроить, если шкала отображения не составляет 100 %. В этом примере мы получаем scaleAdjustment значение для учета параметра масштабирования отображения.

Кнопки заголовка системы

Система резервирует левый или правый верхний угол окна приложения для кнопок заголовка системы (свернуть, развернуть или восстановить, закрыть). Система сохраняет контроль над областью кнопки заголовка, чтобы гарантировать, что минимальные функциональные возможности предоставляются для перетаскивания, минимизации, максимизации и закрытия окна. Система рисует кнопку "Закрыть" в правом верхнем углу для языков слева направо и в левом верхнем углу для языков справа налево.

Содержимое можно нарисовать под областью управления заголовками, например фон приложения, но не следует помещать пользовательский интерфейс, с которым пользователь сможет взаимодействовать. Он не получает никаких входных данных, так как входные данные для элементов управления заголовками обрабатываются системой.

В этих строках из предыдущего примера показаны столбцы заполнения в XAML, определяющие строку заголовка. Использование столбцов заливки вместо полей гарантирует, что фон закрашивает область под кнопками элемента управления заголовком (для прозрачных кнопок). Использование полей отступов слева и справа обеспечивает корректную работу строки заголовка в макетах слева направо и справа налево.

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

Размеры и положение области управления заголовками передаются классом AppWindowTitleBar , чтобы вы могли учесть его в макете пользовательского интерфейса строки заголовка. Ширина зарезервированной области на каждой стороне определяется свойствами LeftInset или RightInset , а высота — свойством Height .

Ниже показано, как ширина столбцов заполнения указывается при вычислении и установке областей перетаскивания.

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

Внимание

См. важные сведения в разделе интерактивного содержимого о том, как отображение масштабирования влияет на эти значения.

Поддержка высокой строки заголовка для настраиваемых строк заголовков

При добавлении интерактивного содержимого, например поля поиска или изображения человека в строке заголовка, рекомендуется увеличить высоту строки заголовка, чтобы обеспечить больше места для этих элементов. Более высокая строка заголовка ещё упрощает управление с помощью касания. Свойство AppWindowTitleBar.PreferredHeightOption позволяет увеличить высоту строки заголовка с обычной до более высокой. При выборе режима панели заголовка Tall, кнопки заголовка, которые система рисует поверх клиентской области, отображаются выше, причем глифы 'минимизация', 'максимизация' и 'закрытие' выровнены по центру. Если вы не указали область перетаскивания, система нарисует область, занимающую ширину вашего окна и высоту, определяемую значением PreferredHeightOption, которое вы задали.

В этом примере показано, как задать PreferredHeightOption свойство.

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

Внимание

Свойство AppWindowTitleBar.ExtendsContentIntoTitleBar должно быть true перед настройкой PreferredHeightOption свойства. Если вы пытаетесь задать PreferredHeightOption, пока ExtendsContentIntoTitleBar равно false, будет выброшено исключение.

Цвет и прозрачность кнопок заголовка

Когда вы расширяете содержимое приложения в область заголовка, вы можете сделать фон кнопок заголовка прозрачным, чтобы был виден фон вашего приложения. Обычно фон устанавливается на Colors.Transparent для полной прозрачности. Для частичной прозрачности установите альфа-канал для цвета, которому вы назначаете свойство.

Эти свойства строки заголовка могут быть прозрачными:

Все остальные свойства цвета будут игнорировать альфа-канал. Если ExtendsContentIntoTitleBar задано значение false, альфа-канал всегда игнорируется для всех AppWindowTitleBar свойств цвета.

Цвет фона кнопки не применяется к состояниям наведения и нажатия кнопки "Закрыть". Кнопка закрытия всегда использует системный цвет для этих состояний.

Совет

Мика — восхитительный материал, который помогает отличить окно в фокусе. Мы рекомендуем его в качестве фона для долгосрочных окон в Windows 11. Если вы применили Mica в клиентской области окна, вы можете расширить её в область заголовка и сделать кнопки заголовка прозрачными, чтобы Mica была видна. См. материал Mica для получения дополнительной информации.

Затемните заголовок окна, когда окно неактивно

Вы должны сделать так, чтобы было очевидно, активно ваше окно или нет. Как минимум, необходимо изменить цвет текста, значков и кнопок в строке заголовка.

Для приложений XAML обработайте событие Window.Activated , чтобы определить состояние активации окна и обновить пользовательский интерфейс строки заголовка по мере необходимости.

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

Для других платформ пользовательского интерфейса обработайте событие, чтобы определить состояние активации окна и обновить пользовательский интерфейс строки заголовка по мере необходимости. Определение состояния окна зависит от платформы пользовательского интерфейса, используемой для приложения.

Сбросить панель заголовка

Чтобы во время работы приложения сбросить строку заголовка системы до значения по умолчанию или переключиться на неё, можно вызвать AppWindowTitleBar.ResetToDefault.

m_AppWindow.TitleBar.ResetToDefault();

Для приложений XAML можно также сбросить заголовок следующими способами:

  • Вызовите SetTitleBar , чтобы переключиться на новый элемент строки заголовка во время работы приложения.
  • Вызовите SetTitleBar с использованием null в качестве параметра для сброса в области перетаскивания AppWindowTitleBar по умолчанию.
  • Вызовите SetTitleBar в качестве параметра null, и установите ExtendsContentIntoTitleBar в false, чтобы вернуть строку заголовка системы к значению по умолчанию.

Отображение и скрытие строки заголовка

Если в приложение добавлена поддержка полноэкранных или компактных режимов наложения , может потребоваться внести изменения в строку заголовка, когда приложение переключается между этими режимами. Окно XAML не предоставляет интерфейсы API для поддержки полноэкранного режима; Для этого можно использовать API AppWindow.

Когда приложение работает в полноэкранном режиме, система скрывает заголовок окна и кнопки управления окна. Вы можете обработать событие AppWindow.Change и проверить свойство event args DidPresenterChange , чтобы определить, следует ли отображать, скрывать или изменять заголовок в ответ на новую презентацию окна.

В этом примере показано, как обрабатывать Changed событие для отображения и скрытия AppTitleBar элемента из предыдущих примеров. Если окно помещается в компактный режим наложения, строка заголовка возвращается к заголовку системы по умолчанию (или можно предоставить настраиваемую строку заголовка, оптимизированную для компактного наложения).

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

Примечание.

Режимы полноэкранного и компактного наложения можно вводить только в том случае, если оно поддерживается приложением. Дополнительные сведения см. в разделе "Управление окнами приложений", FullScreenPresenter и CompactOverlayPresenter.

Что рекомендуется и что не рекомендуется делать

  • Убедитесь, что окно активно или неактивно. Как минимум, измените цвет текста, значков и кнопок в строке заголовка.
  • Определите область перетаскивания вдоль верхнего края холста приложения. Унификация расположения системных заголовков облегчает пользователям поиск нужной информации.
  • Определите область перетаскивания, соответствующую визуальной строке заголовка (при наличии) на холсте приложения.

Полный пример настройки

В этом примере показан весь код, описанный в разделе полной настройки.

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