Udostępnij przez


Dostosowywanie paska tytułu

System Windows udostępnia domyślny pasek tytułu dla każdego okna i umożliwia dostosowanie go do osobowości aplikacji. Domyślny pasek tytułu zawiera niektóre standardowe składniki i podstawowe funkcje, takie jak przeciąganie i zmienianie rozmiaru okna.

aplikacja systemu Windows z paskiem tytułu

Zobacz artykuł Projektowanie paska tytułu , aby uzyskać wskazówki dotyczące dostosowywania paska tytułu aplikacji, akceptowalnej zawartości obszaru paska tytułu i zalecanych wzorców interfejsu użytkownika.

Important

W tym artykule pokazano, jak dostosować pasek tytułu dla aplikacji korzystających z zestawu SDK aplikacji systemu Windows z pakietem WinUI 3 lub bez niego. W przypadku aplikacji korzystających z platformy UWP i WinUI 2, zobacz Dostosowywanie paska tytułu dla platformy UWP.

Important

Dodano nową kontrolkę Paska tytułu w zestawie Windows App SDK 1.7. Upraszcza to proces dostosowywania paska tytułu.

Składniki paska tytułu

Ta lista zawiera opis składników standardowego paska tytułu.

  • Prostokąt paska tytułu
  • Tekst tytułu
  • Ikona systemu
  • Menu systemowe — dostępne przez kliknięcie ikony aplikacji lub kliknięcie prawym przyciskiem myszy paska tytułu
  • Kontrolki podpisów
    • Przycisk Minimalizuj
    • Przycisk Maksymalizuj/Przywróć
    • Przycisk Zamknij

Windowing

Funkcjonalność okien w zestawie SDK aplikacji Windows jest zarządzana za pomocą klasy Microsoft.UI.Windowing.AppWindow, która jest oparta na modelu Win32 HWND. Istnieje mapowanie wartości 1:1 między AppWindow a głównym poziomem HWND w Twojej aplikacji. AppWindow i powiązane z nią klasy udostępniają interfejsy API, które umożliwiają zarządzanie wieloma aspektami okien najwyższego poziomu aplikacji, w tym dostosowywanie paska tytułu. Możesz zmodyfikować domyślny pasek tytułu, który zapewnia system Windows, tak aby łączył się z resztą interfejsu użytkownika, lub rozszerzyć kanwę aplikacji na obszar paska tytułu i udostępnić własną zawartość paska tytułu.

Funkcja obsługi okien w systemie WinUI 3 jest oparta na modelu HWND Systemu Windows Microsoft.UI.Xaml.Window . W przypadku aplikacji XAML, które korzystają z interfejsu WinUI 3, interfejsy API okien XAML zapewniają prostszy sposób dostosowywania paska tytułu, jednocześnie umożliwiając dostęp do interfejsów API appWindow w razie potrzeby.

Jak pracować z aplikacją AppWindow

Interfejsy API appWindow można używać z dowolną strukturą interfejsu użytkownika obsługiwaną przez zestaw SDK aplikacji systemu Windows — Win32, WPF, WinForms lub WinUI 3 — i można wdrożyć je przyrostowo przy użyciu tylko potrzebnych interfejsów API.

Jeśli używasz języka XAML WinUI 3 jako struktury interfejsu użytkownika aplikacji, dostępne są zarówno interfejsy API Okna , jak i AppWindow . Począwszy od zestawu Windows App SDK 1.4, okna XAML i appWindow używają tego samego obiektu AppWindowTitleBar do dostosowywania paska tytułu. Użyj właściwości Window.AppWindow , aby uzyskać obiekt AppWindow z istniejącego okna XAML. Ten obiekt AppWindow zapewnia dostęp do API do dostosowywania paska tytułu. Aby uzyskać dostęp do dodatkowych funkcji paska tytułu, możesz użyć interfejsów API AppWindow z okna XAML w następujący sposób: AppWindow.TitleBar.ForegroundColor = Colors.White;.

Jeśli nie używasz interfejsu WinUI 3 1.3 lub nowszego, użyj interfejsów API międzyoperacyjności, aby pobrać aplikację AppWindow i użyć interfejsów API appWindow, aby dostosować pasek tytułu. Aby uzyskać więcej informacji na temat interfejsów API interoperacyjności, zobacz Zarządzanie oknami aplikacji — struktura interfejsu użytkownika i interoperacyjność HWND oraz przykładowe rozwiązanie galerii okien .

Jak bardzo dostosować pasek tytułu

Istnieją dwa poziomy dostosowywania, które można zastosować do paska tytułu: stosowanie drobnych modyfikacji do domyślnego paska tytułu lub rozszerzanie kanwy aplikacji na obszar paska tytułu i udostępnianie całkowicie niestandardowej zawartości.

Simple

W przypadku prostego dostosowywania, takiego jak zmiana koloru paska tytułu, można ustawić właściwości w obiekcie AppWindowTitleBar, aby określić kolory, których chcesz użyć dla elementów paska tytułu. W takim przypadku system zachowuje odpowiedzialność za wszystkie inne aspekty paska tytułu, takie jak rysowanie tytułu aplikacji i definiowanie obszarów przeciągania.

Full

Inną opcją jest ukrycie domyślnego paska tytułu systemu i zastąpienie go własną niestandardową zawartością. Możesz na przykład umieścić tekst, pole wyszukiwania lub niestandardowe menu w obszarze paska tytułu. Należy również użyć tej opcji, aby rozszerzyć tło materiału , takie jak Mica, na obszar paska tytułu.

Jeśli zdecydujesz się na pełne dostosowanie, odpowiadasz za umieszczenie zawartości w obszarze paska tytułu i możesz zdefiniować własne regiony przeciągania. Przyciski na belce tytułowej (systemowe Zamknij, Minimalizuj i Maksymalizuj) są nadal dostępne i obsługiwane przez system, ale elementy takie jak tytuł aplikacji nie są dostępne. Musisz utworzyć te elementy samodzielnie zgodnie z potrzebami aplikacji.

Proste dostosowywanie

Jeśli chcesz dostosować tylko tytuł paska tytułu, kolory lub ikonę, możesz ustawić właściwości obiektu paska tytułu dla okna aplikacji.

Title

Domyślnie pasek tytułu wyświetla typ aplikacji jako tytuł okna (na przykład "WinUI Desktop"). Tytuł okna należy zaktualizować, aby wyświetlić zrozumiałą nazwę wyświetlaną aplikacji.

Nazwa wyświetlana aplikacji XAML jest ustawiona w pliku Package.appxmanifest. Możesz uzyskać tę wartość i użyć jej do ustawienia właściwości Title w następujący sposób.

Title = AppInfo.Current.DisplayInfo.DisplayName;

Aby zmienić tytuł okna, ustaw właściwość Window.Title na wartość tekstową jednowierszową, jak pokazano tutaj.

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

Aby zmienić tytuł okna przy użyciu interfejsów API appWindow, ustaw właściwość AppWindow.Title na wartość tekstową jednowierszową, jak pokazano tutaj. W tym przykładzie pokazano, jak za pomocą interfejsów API międzyoperacyjności uzyskać AppWindow, który jest potrzebny, jeśli aplikacja nie korzysta z WinUI 3 1.3 lub nowszego.

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

Colors

Aby dostosować domyślne kolory paska tytułu lub zmienić domyślną ikonę okna, musisz użyć interfejsów API appWindow lub zdecydować się na pełne dostosowanie paska tytułu.

W tym przykładzie pokazano, jak uzyskać wystąpienie elementu AppWindowTitleBar i ustawić jego właściwości koloru.

Important

Dostosowywanie kolorów jest ignorowane, gdy aplikacja działa w systemie 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;
}

Istnieje kilka kwestii, o których należy pamiętać podczas ustawiania kolorów paska tytułu:

  • Kolor tła przycisku nie jest stosowany do aktywowania przycisku zamknięcia i naciśniętych stanów. Przycisk zamknij zawsze używa koloru zdefiniowanego przez system dla tych stanów.
  • Ustawienie właściwości color na null resetuje ją do domyślnego koloru systemu.
  • Nie można ustawić przezroczystych kolorów. Kanał alfa koloru jest ignorowany.

System Windows daje użytkownikowi możliwość zastosowania wybranego koloru wyróżniającego do paska tytułu. Jeśli ustawisz dowolny kolor paska tytułu, zalecamy jawne ustawienie wszystkich kolorów. Dzięki temu nie ma niezamierzonych kombinacji kolorów, które występują z powodu ustawień kolorów zdefiniowanych przez użytkownika.

Ikona i menu systemowe

Możesz ukryć ikonę systemu lub zastąpić ją ikoną niestandardową. Ikona systemu wyświetla menu systemowe po kliknięciu prawym przyciskiem myszy lub po jednorazowym stuknięciu. Zamyka okno po dwukrotnym kliknięciu/naciśnięciu.

Aby pokazać lub ukryć ikonę systemu i skojarzone zachowania, ustaw właściwość IconShowOptions paska tytułu.

m_TitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;

Aby użyć niestandardowej ikony okna, wywołaj jedną z metod AppWindow.SetIcon , aby ustawić nową ikonę.

  • SetIcon(String)

    Metoda SetIcon(String) obecnie działa tylko z plikami .ico. Ciąg przekazywany do tej metody jest w pełni kwalifikowaną ścieżką do pliku .ico.

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

    Jeśli masz już dojście do ikony (HICON) z jednej z funkcji Ikona , takich jak CreateIcon, możesz użyć interfejsu API Międzyoperacja GetIconIdFromIcon , aby uzyskać identyfikator IconId. Następnie możesz przekazać element IconId do metody SetIcon(IconId), aby ustawić ikonę okna.

    m_AppWindow.SetIcon(iconId));
    

Pełne dostosowywanie

Po wyrażeniu zgody na pełne dostosowywanie paska tytułu obszar klienta aplikacji zostanie rozszerzony w celu pokrycia całego okna, w tym obszaru paska tytułu. Odpowiadasz za rysowanie i obsługę danych wejściowych dla całego okna z wyjątkiem przycisków podpisów, które są nadal udostępniane przez okno.

Aby ukryć pasek tytułu systemu i rozszerzyć zawartość na obszar paska tytułu, ustaw właściwość rozszerzającą zawartość aplikacji na obszar paska tytułu na true. W aplikacji XAML tę właściwość można ustawić w metodzie aplikacji OnLaunched (App.xaml.cs) lub na pierwszej stronie aplikacji.

Tip

Zobacz sekcję Przykład pełnego dostosowywania, aby zobaczyć cały kod jednocześnie.

W tym przykładzie pokazano, jak ustawić właściwość Window.ExtendsContentIntoTitleBar na truewartość .

public MainWindow()
{
    this.InitializeComponent();

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

Caution

ExtendsContentIntoTitleBar pojawia się w funkcji IntelliSense dla XAML w przypadku Window, ale ustawienie go w XAML powoduje błąd. Ustaw tę właściwość w kodzie.

W tym przykładzie pokazano, jak pobrać właściwość AppWindowTitleBar i ustawić właściwość AppWindowTitleBar.ExtendsContentIntoTitleBar na truewartość . W tym przykładzie pokazano, jak za pomocą API międzyoperacyjnych uzyskać AppWindow, które jest potrzebne, jeśli aplikacja nie używa WinUI 3 1.3 lub nowszego.

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

Zawartość paska tytułu i domyślny region przeciągania

Gdy aplikacja zostanie rozszerzona do obszaru paska tytułu, odpowiadasz za definiowanie interfejsu użytkownika paska tytułu i zarządzanie nim. Zazwyczaj obejmuje to co najmniej tekst tytułu i obszar przeciągania. Obszar przeciągania paska tytułu definiuje miejsce, w którym użytkownik może kliknąć i przeciągnąć, aby przenieść okno. Jest to również miejsce, w którym użytkownik może kliknąć prawym przyciskiem myszy, aby wyświetlić menu systemowe.

Aby dowiedzieć się więcej na temat dopuszczalnej zawartości paska tytułu i zalecanych wzorców interfejsu użytkownika, zobacz Projektowanie paska tytułu.

W tym przykładzie pokazano kod XAML dla niestandardowego interfejsu użytkownika paska tytułu bez zawartości interaktywnej.

<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 i RightPaddingColumn są używane do rezerwowania miejsca na przyciski podpisów. Wartości Width dla tych kolumn są ustawiane w kodzie, który jest wyświetlany później. Aby zapoznać się z kodem i wyjaśnieniem, zobacz sekcję przyciski podpisów systemowych.

Aplikacja XAML ma nazwę wyświetlaną ustawioną w pliku Package.appxmanifest. Możesz uzyskać tę wartość i użyć jej na niestandardowym pasku tytułu w następujący sposób.

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

Po rozszerzeniu zawartości na obszar paska tytułu pasek tytułu systemowy jest ukryty, a domyślny pasek tytułu AppWindowTitleBar jest tworzony, który udostępnia przyciski podpisów i region przeciągania po szerokości ekranu, identyczny z paskiem tytułu systemu. Jeśli nie umieścisz zawartości interakcyjnej na pasku tytułu, możesz pozostawić ten domyślny region przeciągania as-is. Jeśli umieścisz zawartość interaktywną na pasku tytułu, musisz określić regiony, które są interakcyjne, które zostaną omówione w następnej sekcji.

Caution

Podczas definiowania niestandardowych regionów przeciągania nie muszą znajdować się w górnej części okna w domyślnym obszarze paska tytułu; Możesz zdefiniować dowolną część interfejsu użytkownika jako region przeciągania. Jednak umieszczenie regionów przeciągania w różnych miejscach może utrudnić użytkownikom ich znajdowanie.

Zawartość interaktywna

Możesz umieścić interaktywne kontrolki, takie jak przyciski, menu lub pole wyszukiwania, w górnej części aplikacji, aby wyglądały one na pasku tytułu. Należy jednak określić, które regiony są interaktywne, aby upewnić się, że elementy interakcyjne odbierają dane wejściowe użytkownika, a jednocześnie umożliwiają użytkownikom przenoszenie okna.

aplikacja systemu Windows z polem wyszukiwania na pasku tytułu

Po dodaniu zawartości interaktywnej w obszarze paska tytułu należy użyć klasy InputNonClientPointerSource , aby określić obszary, w których dane wejściowe są przekazywane do kontrolki interaktywnej, a nie obsługiwane przez pasek tytułu. Aby ustawić regiony interaktywne, wywołaj metodę InputNonClientPointerSource.SetRegionRects . Ta metoda przyjmuje wartość określającą rodzaj ustawianego regionu (w tym przypadku Passthrough) i tablicę prostokątów, z których każdy definiuje region Passthrough. Gdy rozmiar paska tytułu zmieni się, należy ponownie obliczyć regiony interakcyjne, aby dopasować go do nowego rozmiaru, a następnie wywołać SetRegionRects z nowymi wartościami.

W tym przykładzie pokazano niestandardowy interfejs użytkownika paska tytułu z polem wyszukiwania i kontrolką konta PersonPicture. Przedstawia on sposób obliczania i ustawiania interakcyjnych prostokątów dla tych kontrolek, tak aby dane wejściowe były przekazywane do nich.

Oto kilka ważnych kwestii, które należy zauważyć w tym kodzie:

  • AppTitleBar Ustaw wysokość siatki na 48, aby postępować zgodnie ze wskazówkami dotyczącymi projektowania paska tytułu dla zawartości interaktywnej.
  • Ustaw wartość PreferredHeightOption , aby Tall przyciski podpisów są takie same jak na pasku tytułu.
  • Aby ułatwić zmienianie rozmiaru kontrolek i obliczanie regionów, użyj Grid z wieloma nazwanymi kolumnami dla układu.
  • Użyj zmiany rozmiaru MinWidthgwiazdki (*) dla kolumny zawierającej AutoSuggestBox element , aby automatycznie zmienić rozmiar okna.
  • Ustaw MinWidth na RightDragColumn, aby zarezerwować mały obszar, który można zawsze przeciągać, nawet po zmianie rozmiaru okna.
  • Ustaw ExtendsContentIntoTitleBar na true w konstruktorze MainWindow. Jeśli ustawisz go w kodzie, który zostanie wywołany później, domyślny pasek tytułu systemu może być wyświetlany jako pierwszy, a następnie ukryty.
  • Wykonaj początkowe wywołanie w celu obliczenia regionów interaktywnych po załadowaniu elementu AppTitleBar. W przeciwnym razie nie ma gwarancji, że elementy używane do obliczenia będą miały poprawne wartości.
  • Zaktualizuj interakcyjne obliczenia prostokątów dopiero po zmianie rozmiaru elementu AppTitleBar (AppTitleBar_SizeChanged). Jeśli polegasz na zdarzeniu okna Changed, wystąpią sytuacje (jak np. maksymalizowanie/minimalizowanie okna), w których zdarzenie występuje przed zmianą rozmiaru AppTitleBar, a obliczenia będą używać nieprawidłowych wartości.
  • Ustaw niestandardowe obszary przeciągania/interakcyjne tylko po zaznaczeniu ExtendsContentIntoTitleBar, aby potwierdzić, że jest używany niestandardowy pasek tytułu.
<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>

Ten kod przedstawia sposób obliczania i ustawiania regionów interaktywnych odpowiadających kontrolkom AutoSuggestBox i 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)
        );
    }
}

Warning

AppWindow używa pikseli fizycznych w celu zachowania zgodności ze strukturami interfejsu użytkownika, które nie używają współrzędnych logicznych. Jeśli używasz WPF lub WinUI 3, RightInset, LeftInsetoraz wartości wykorzystywane do obliczania regionów należy dostosować, gdy skala wyświetlania nie jest równa 100%. W tym przykładzie uzyskujemy wartość scaleAdjustment do uwzględnienia ustawienia skalowania wyświetlania.

Przyciski podpisu systemowego

System rezerwuje lewy górny lub prawy górny róg okna aplikacji dla systemowych przycisków tytułowych (minimalizuj, maksymalizuj/przywróć, zamknij). System zachowuje kontrolę nad obszarem przycisku napisów, aby zagwarantować, że minimalna funkcjonalność jest zapewniana do przeciągania, minimalizowania, maksymalizacji i zamykania okna. System umieszcza przycisk Zamknij w prawym górnym rogu dla języków od lewej do prawej i w lewym górnym rogu dla języków od prawej do lewej.

Możesz rysować zawartość pod obszarem kontrolki tytułu, takim jak tło aplikacji, ale nie powinieneś umieszczać tam żadnych elementów interfejsu użytkownika, z którymi użytkownik miałby wchodzić w interakcję. Nie otrzymuje żadnych danych wejściowych, ponieważ dane wejściowe dla kontrolek podpisów są obsługiwane przez system.

Te wiersze z poprzedniego przykładu pokazują kolumny wypełnienia w języku XAML, które definiują pasek tytułu. Użycie kolumn wypełniających zamiast marginesów zapewnia, że tło pokrywa obszar pod przyciskami kontrolnymi podpisu (w przypadku przezroczystych przycisków). Użycie kolumn dopełniania zarówno po prawej, jak i po lewej stronie gwarantuje, że pasek tytułu działa prawidłowo w układach od prawej do lewej oraz od lewej do prawej.

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

Wymiary i położenie obszaru sterowania podpisami są przekazywane przez klasę AppWindowTitleBar , dzięki czemu można je uwzględnić w układzie interfejsu użytkownika paska tytułu. Szerokość regionu zarezerwowanego po każdej stronie jest podawana przez właściwości LeftInset lub RightInset , a jego wysokość jest podana przez właściwość Height .

Poniżej przedstawiono, jak szerokość kolumn dopełnienia jest określana podczas obliczania i ustawiania regionów przeciągania.

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

Important

Zapoznaj się z ważnymi informacjami w sekcji Zawartość interaktywna na temat wpływu skalowania wyświetlania na te wartości.

Obsługa wysokich pasków tytułu dla niestandardowych pasków tytułu

Podczas dodawania interaktywnej zawartości, takiej jak pole wyszukiwania lub obraz osoby na pasku tytułu, zalecamy zwiększenie wysokości paska tytułu w celu zapewnienia większej ilości miejsca na te elementy. Wyższy pasek tytułu ułatwia również manipulowanie dotykiem. Właściwość AppWindowTitleBar.PreferredHeightOption umożliwia zwiększenie wysokości paska tytułu ze standardowej wysokości, która jest domyślna, do wysokości wyższej. Po wybraniu trybu paska tytułu Tall, system rysuje przyciski podpisu jako nakładkę w obszarze klienta, które są renderowane na większą wysokość z wyśrodkowanymi symbolami minimalizuj/maksymalizuj/zamknij. Jeśli nie określono regionu przeciągania, system narysuje taki, który rozszerza szerokość okna i wysokość określoną przez ustawioną wartość PreferredHeightOption.

W tym przykładzie pokazano, jak można ustawić właściwość PreferredHeightOption.

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

Caution

Przed ustawieniem PreferredHeightOption właściwości musi znajdować true się właściwość AppWindowTitleBar.ExtendsContentIntoTitleBar. Jeśli spróbujesz ustawić PreferredHeightOption, gdy ExtendsContentIntoTitleBar jest false, zostanie zgłoszony wyjątek.

Kolor i przezroczystość przycisków podpisów

Po rozszerzeniu zawartości aplikacji na obszar paska tytułu możesz ustawić tło przycisków podpisów jako przezroczyste, aby umożliwić wyświetlanie tła aplikacji. Zazwyczaj tło jest ustawiane na Kolory.Przezroczyste w celu uzyskania pełnej przezroczystości. Aby uzyskać częściową przezroczystość, ustaw kanał alfa dla właściwości Color na wartość .

Te właściwości paska tytułu mogą być przezroczyste:

Wszystkie inne właściwości koloru będą nadal ignorować kanał alfa. Jeśli ExtendsContentIntoTitleBar jest ustawiona na false, kanał alfa jest zawsze ignorowany dla wszystkich właściwości koloru AppWindowTitleBar.

Kolor tła przycisku nie jest stosowany do aktywowania przycisku Zamknij i naciśniętych stanów. Przycisk zamknij zawsze używa koloru zdefiniowanego przez system dla tych stanów.

Tip

Mica to wspaniały materiał , który pomaga odróżnić okno, które jest w centrum uwagi. Zalecamy jako tło dla trwałych okien w systemie Windows 11. Jeśli zastosowano micę w obszarze klienta okna, możesz rozszerzyć micę na obszar paska tytułu i ustawić przyciski nagłówka przezroczyste, aby Mica była widoczna. Aby uzyskać więcej informacji , zobacz materiał Mica .

Przyciemnienie paska tytułu, gdy okno jest nieaktywne

Powinno być oczywiste, gdy okno jest aktywne lub nieaktywne. Co najmniej należy zmienić kolor tekstu, ikon i przycisków na pasku tytułu.

W przypadku aplikacji XAML obsłuż zdarzenie Window.Activated , aby określić stan aktywacji okna, i zaktualizuj interfejs użytkownika paska tytułu zgodnie z potrzebami.

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

W przypadku innych struktur interfejsu użytkownika należy obsługiwać zdarzenie w celu określenia stanu aktywacji okna i zaktualizować interfejs użytkownika paska tytułu zgodnie z potrzebami. Sposób określania stanu okna zależy od struktury interfejsu użytkownika używanej dla aplikacji.

Resetowanie paska tytułu

Aby zresetować lub przełączyć się na pasek tytułu systemu podczas działania aplikacji, możesz wywołać metodę AppWindowTitleBar.ResetToDefault.

m_AppWindow.TitleBar.ResetToDefault();

W przypadku aplikacji XAML można również zresetować pasek tytułu na następujące sposoby:

  • Wywołaj metodę SetTitleBar , aby przełączyć się na nowy element paska tytułu podczas działania aplikacji.
  • Wywołaj SetTitleBar z null jako parametrem, aby zresetować do domyślnych obszarów przeciągania AppWindowTitleBar.
  • Wywołaj SetTitleBar metodę z parametrem null jako parametrem i ustaw parametr ExtendsContentIntoTitleBar , aby false przywrócić domyślny pasek tytułu systemu.

Pokaż i ukryj pasek tytułu

W przypadku dodawania obsługi trybów pełnoekranowych lub kompaktowych nakładek do aplikacji może być konieczne wprowadzenie zmian na pasku tytułu, gdy aplikacja przełącza się między tymi trybami. Okno XAML nie udostępnia żadnych interfejsów API do obsługi trybu pełnoekranowego; W tym celu można użyć interfejsów API systemu AppWindow.

Gdy aplikacja działa w trybie pełnoekranowym , system ukrywa pasek tytułu i przyciski sterowania podpisami. Możesz obsłużyć zdarzenie AppWindow.Changed i sprawdzić właściwość DidPresenterChange zdarzenia, aby określić, czy chcesz pokazać, ukryć lub zmienić pasek tytułu w odpowiedzi na nową prezentację okna.

W tym przykładzie pokazano, jak obsługiwać zdarzenie Changed w celu pokazania i ukrycia elementu AppTitleBar z poprzednich przykładów. Jeśli okno zostanie umieszczone w trybie kompaktowej nakładki , pasek tytułu zostanie zresetowany do domyślnego paska tytułu systemu (lub można podać niestandardowy pasek tytułu zoptymalizowany pod kątem kompaktowej nakładki).

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

Note

Tryby pełnoekranowe i kompaktowe nakładki można wprowadzać tylko wtedy, gdy aplikacja jest obsługiwana. Aby uzyskać więcej informacji, zobacz Zarządzanie oknami aplikacji, FullScreenPresenteri CompactOverlayPresenter.

Zalecenia i zakazy

  • Upewnij się, że jest wyraźnie widoczne, kiedy okno jest aktywne lub nieaktywne. Zmień kolor tekstu, ikon i przycisków na pasku tytułu.
  • Zdefiniuj region przeciągania wzdłuż górnej krawędzi kanwy aplikacji. Dopasowanie położenia pasków tytułu systemu ułatwia użytkownikom znajdowanie.
  • Zdefiniuj region przeciągania zgodny z paskiem tytułu wizualizacji (jeśli istnieje) na kanwie aplikacji.

Przykład pełnego dostosowywania

W tych przykładach pokazano cały kod opisany w sekcji Pełne dostosowywanie.

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