133

Title bar customization

 3 years ago
source link: https://docs.microsoft.com/en-us/windows/apps/develop/title-bar?WT_mc_id=WD-MVP-4025064&tabs=wasdk
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Title bar customization

  • Article
  • 02/18/2022
  • 37 minutes to read

Windows provides a default title bar for every window and lets you to customize it to match the personality of your app. The default title bar comes with some standard components and core functionality such as dragging and resizing the window.

A Windows app showing the title bar

See the Title bar design article for guidance on customizing your app's title bar, acceptable title bar area content, and recommended UI patterns.

Title bar components

This list describes the components of the standard title bar.

  • Title bar rectangle
  • Title text
  • System icon (except for UWP)
  • System menu - accessed by clicking the app icon or right-clicking the title bar
  • Caption controls
    • Minimize button
    • Maximize/Restore button
    • Close button

Platform options

The exact features of the title bar and the options available to customize it depend on your UI platform and app requirements. This article shows how to customize the title bar for apps that use either the Windows App SDK, WinUI 3, or UWP with WinUI 2.

For a detailed comparison of the windowing models used by the Windows App SDK and UWP, see Windowing functionality migration.

Windowing functionality in the Windows App SDK is through the Microsoft.UI.Windowing.AppWindow class, which is based on the Win32 HWND model. There's a 1:1 mapping between an AppWindow and a top-level HWND in your app. AppWindow and its related classes provide APIs that let you manage many aspects of your app's top-level windows, including customization of the title bar. You can modify the default title bar that Windows provides so that it blends with the rest of your UI, or extend your app canvas into the title bar area and provide your own title bar content.

Important

Title bar customization APIs are currently supported on Windows 11 only. We recommend that you check AppWindowTitleBar.IsCustomizationSupported in your code before you call these APIs to ensure your app doesn't crash on other versions of Windows.

For XAML apps that use WinUI 3, XAML Window APIs provide a simpler way to customize the title bar that also works on Windows 10. These APIs can be used in conjunction with the Windows App SDK APIs (see the WinUI 3 tab).

How to work with AppWindow

You can use AppWindow APIs with any UI framework that the Windows App SDK supports - Win32, WPF, WinForms, or WinUI 3 - and you can adopt them incrementally, using only the APIs you need. You get an AppWindow object from an existing window using the interop APIs. With this AppWindow object you have access to the title bar customization APIs. For more about the interop APIs, see Manage app windows - UI framework and HWND interop and the Windowing gallery sample.

How much to customize the title bar

There are two levels of customization that you can apply to the title bar: apply minor modifications to the default title bar, or extend your app canvas into the title bar area and provide completely custom content.

Simple

Simple customization is only available for Windows App SDK and UWP/WinUI 2.

For simple customization, such as changing the title bar color, you can set properties on your app window's title bar object to specify the colors you want to use for title bar elements. In this case, the system retains responsibility for all other aspects of the title bar, such as drawing the app title and defining drag areas.

Your other option is to hide the default title bar and replace it with your own custom content. For example, you can place text, a search box, or custom menus in the title bar area. You will also need to use this option to extend a material backdrop, like Mica, into the title bar area.

When you opt for full customization, you are responsible for putting content in the title bar area, and you can define your own drag region. The caption controls (system Close, Minimize, and Maximize buttons) are still available and handled by the system, but elements like the app title are not. You will need to create those elements yourself as needed by your app.

Simple customization

If you want only to customize the title bar colors or icon, you can set properties on the title bar object for your app window.

(Windows 11 only. See Platform options for more info.)

These examples show how to get an instance of AppWindow and set its properties.

Title

By default, the title bar shows the app type as the window title (for example, "WinUI Desktop"). To change the window title, set the AppWindow.Title property to a single-line text value.

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

This example shows how to get an instance of AppWindowTitleBar and set its color properties.

private bool SetTitleBarColors()
{
    // Check to see if customization is supported.
    // Currently only supported on Windows 11.
    if (AppWindowTitleBar.IsCustomizationSupported())
    {
        if (m_AppWindow is null)
        {
            m_AppWindow = GetAppWindowForCurrentWindow();
        }
        var titleBar = m_AppWindow.TitleBar;

        // Set active window colors
        titleBar.ForegroundColor = Colors.White;
        titleBar.BackgroundColor = Colors.Green;
        titleBar.ButtonForegroundColor = Colors.White;
        titleBar.ButtonBackgroundColor = Colors.SeaGreen;
        titleBar.ButtonHoverForegroundColor = Colors.Gainsboro;
        titleBar.ButtonHoverBackgroundColor = Colors.DarkSeaGreen;
        titleBar.ButtonPressedForegroundColor = Colors.Gray;
        titleBar.ButtonPressedBackgroundColor = Colors.LightGreen;

        // Set inactive window colors
        titleBar.InactiveForegroundColor = Colors.Gainsboro;
        titleBar.InactiveBackgroundColor = Colors.SeaGreen;
        titleBar.ButtonInactiveForegroundColor = Colors.Gainsboro;
        titleBar.ButtonInactiveBackgroundColor = Colors.SeaGreen;
        return true;
    }
    return false;
}

Icon and system menu

You can also hide the system icon, or replace it with a custom icon. The system icon shows the system menu when right clicked or tapped once. It closes the window when double clicked/tapped.

To show or hide the system icon and associated behaviors, set the title bar IconShowOptions property.

titleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;

The IconShowOptions enumeration allows the possibility of other options being added in future releases. You can give us feedback on the Windows App SDK repository on GitHub if this is of interest to you.

To use a custom window icon, call one of the AppWindow.SetIcon methods to set the new icon.

  • SetIcon(String)

    The SetIcon(String) method currently works only with .ico files. The string you pass to this method is the fully qualified path to the .ico file.

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

    If you already have a handle to an icon (HICON) from one of the Icon functions like CreateIcon, you can use the GetIconIdFromIcon interop API to get an IconId. You can then pass the IconId to the SetIcon(IconId) method to set your window icon.

    m_AppWindow.SetIcon(iconId));
    

There are a few things to be aware of when setting title bar colors:

  • The button background color is not applied to the close button hover and pressed states. The close button always uses the system-defined color for those states.
  • Setting a color property to null resets it to the default system color.
  • You can't set transparent colors. The color's alpha channel is ignored.

Windows gives a user the option to apply their selected accent color to the title bar. If you set any title bar color, we recommend that you explicitly set all the colors. This ensures that there are no unintended color combinations that occur because of user defined color settings.

Full customization

When you opt-in to full title bar customization, your app's client area is extended to cover the entire window, including the title bar area. You are responsible for drawing and input-handling for the entire window except the caption buttons, which are still provided by the window.

To hide the default title bar and extend your content into the title bar area, set the property that extends app content into the title bar area to true. In a XAML app, this property can be set in your app's OnLaunched method (App.xaml.cs), or in your app's first page.

See the Full customization example section to see all the code at once.

(Windows 11 only. See Platform options for more info.)

This example shows how to get the AppWindowTitleBar and set the ExtendsContentIntoTitleBar property to true.

Important

Title bar customization APIs are not supported on all versions of Windows where your app might run, so be sure to check AppWindowTitleBar.IsCustomizationSupported in your code before you call these APIs. If title bar customization is not supported, you will typically hide your custom title bar UI by setting Visibility to Collapsed.

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();
    // Check to see if customization is supported.
    // Currently only supported on Windows 11.
    if (AppWindowTitleBar.IsCustomizationSupported())
    {
        var titleBar = m_AppWindow.TitleBar;
        // Hide default title bar.
        titleBar.ExtendsContentIntoTitleBar = true;
    }
    else
    {
        // Title bar customization using these APIs is currently
        // supported only on Windows 11. In other cases, hide
        // the custom title bar element.
        AppTitleBar.Visibility = Visibility.Collapsed;
    }
}

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

Title bar content and drag regions

When your app is extended into the title bar area, you're responsible for defining and managing the UI for the title bar. This typically includes, at a minimum, specifying title text and the drag region. The drag region of the title bar defines where the user can click and drag to move the window around. It's also where the user can right-click to show the system menu.

To learn more about acceptable title bar content and recommended UI patterns, see Title bar design.

(Windows 11 only. See Platform options for more info.)

When you extend your content into the title bar area, the system by default retains the entire title bar area except for the caption buttons as the drag region. If you don't place interactive content in your title bar, you can leave this default drag region as-is. If you place interactive content in your title bar, you need to specify the drag regions, which we cover in the next section.

This example shows the XAML for a custom title bar UI without interactive content.

<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="/Images/WindowIcon.png"
           Grid.Column="1"
           HorizontalAlignment="Left"
           Width="16" Height="16"
           Margin="8,0,0,0"/>
    <TextBlock x:Name="TitleTextBlock" 
               Text="App title" 
               Style="{StaticResource CaptionTextBlockStyle}"
               Grid.Column="1"
               VerticalAlignment="Center"
               Margin="28,0,0,0"/>
</Grid>

Important

The LeftPaddingColumn and RightPaddingColumn are used to reserve space for the caption buttons. The Width values for these columns are set in code, which is shown later. See the System caption buttons section for the code and explanation.

Interactive content

You can place interactive controls, like buttons, menus, or a search box, in the top part of the app so they appear to be in the title bar. However, there are a few rules you must follow to ensure that your interactive elements receive user input while still allowing users to move your window around.

A Windows app with a search box in the title bar

(Windows 11 only. See Platform options for more info.)

If you add interactive content in the title bar area, you should define explicit drag regions around that content so that users can interact with it. After you set a custom drag region, the default drag region is removed and the system does not reserve any mandatory drag region. You are responsible for ensuring that there is enough space in your title bar for your users to move your window.

To set the drag regions, call the AppWindowTitleBar.SetDragRectangles method. This method takes an array of rectangles, each of which defines a drag region. When the size of the title bar changes, you need to recalculate the drag regions to match the new size, and call SetDragRectangles with the new values.

Your custom title bar will not be shown if it's not supported on the system where it's run. You should provide an alternative UI for any functionality that you placed in your custom title bar.

This example shows a custom title bar UI with a search box and demonstrates how to calculate and set the drag rectangles on either side of the search box. Here are a few important points to notice in the code.

  • Set the AppTitleBar Grid height to 48 to follow the Title bar design guidance for interactive content.
  • To make calculating the drag rectangles easier, use a Grid with multiple named columns for the layout.
  • Set ExtendsContentIntoTitleBar to true in the MainWindow constructor. If you set it in code that gets called later, the default system title bar might be shown first, and then hidden.
  • Make the initial call to calculate drag regions after the AppTitleBar element has loaded (AppTitleBar_Loaded). Otherwise, there's no guarantee that the elements used for the calculation will have their correct values.
  • Update the drag rectangle calculations only after the AppTitleBar element has changed size (AppTitleBar_SizeChanged). If you depend on the window Changed event, there will be situations (like window maximize/minimize) where the event occurs before AppTitleBar is resized and the calculations will use incorrect values.
  • Call SetDragRectangles only after you check IsCustomizationSupported and ExtendsContentIntoTitleBar to confirm that a custom title bar is supported and being used.
<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="Auto"/>
        <ColumnDefinition x:Name="RightDragColumn" Width="*"/>
        <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
    </Grid.ColumnDefinitions>
    <Image x:Name="TitleBarIcon" Source="/Images/WindowIcon.png"
           Grid.Column="1"
           Width="16" Height="16"
           Margin="8,0,0,0"/>
    <TextBlock x:Name="TitleTextBlock" 
               Text="App title" 
               Style="{StaticResource CaptionTextBlockStyle}"
               Grid.Column="2"
               VerticalAlignment="Center"
               Margin="4,0,0,0"/>
    <AutoSuggestBox Grid.Column="4" QueryIcon="Find"
                    PlaceholderText="Search"
                    VerticalAlignment="Center"
                    Width="260" Margin="4,0"/>
</Grid>
using System.Runtime.InteropServices;

private AppWindow m_AppWindow;

public MainWindow()
{
    this.InitializeComponent();

    m_AppWindow = GetAppWindowForCurrentWindow();

    // Check to see if customization is supported.
    // Currently only supported on Windows 11.
    if (AppWindowTitleBar.IsCustomizationSupported())
    {
        var titleBar = m_AppWindow.TitleBar;
        titleBar.ExtendsContentIntoTitleBar = true;
        AppTitleBar.Loaded += AppTitleBar_Loaded;
        AppTitleBar.SizeChanged += AppTitleBar_SizeChanged;
    }
    else
    {
        // Title bar customization using these APIs is currently
        // supported only on Windows 11. In other cases, hide
        // the custom title bar element.
        AppTitleBar.Visibility = Visibility.Collapsed;

        // Show alternative UI for any functionality in
        // the title bar, such as search.
    }

}

private void AppTitleBar_Loaded(object sender, RoutedEventArgs e)
{
    // Check to see if customization is supported.
    // Currently only supported on Windows 11.
    if (AppWindowTitleBar.IsCustomizationSupported())
    {
        SetDragRegionForCustomTitleBar(m_AppWindow);
    }
}

private void AppTitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
{
    // Check to see if customization is supported.
    // Currently only supported on Windows 11.
    if (AppWindowTitleBar.IsCustomizationSupported()
        && m_AppWindow.TitleBar.ExtendsContentIntoTitleBar)
    {
        // Update drag region if the size of the title bar changes.
        SetDragRegionForCustomTitleBar(m_AppWindow);
    }
}

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

[DllImport("Shcore.dll", SetLastError = true)]
internal static extern int GetDpiForMonitor(IntPtr hmonitor, Monitor_DPI_Type dpiType, out uint dpiX, out uint dpiY);

internal enum Monitor_DPI_Type : int
{
    MDT_Effective_DPI = 0,
    MDT_Angular_DPI = 1,
    MDT_Raw_DPI = 2,
    MDT_Default = MDT_Effective_DPI
}

private double GetScaleAdjustment()
{
    IntPtr hWnd = WindowNative.GetWindowHandle(this);
    WindowId wndId = Win32Interop.GetWindowIdFromWindow(hWnd);
    DisplayArea displayArea = DisplayArea.GetFromWindowId(wndId, DisplayAreaFallback.Primary);
    IntPtr hMonitor = Win32Interop.GetMonitorFromDisplayId(displayArea.DisplayId);

    // Get DPI.
    int result = GetDpiForMonitor(hMonitor, Monitor_DPI_Type.MDT_Default, out uint dpiX, out uint _);
    if (result != 0)
    {
        throw new Exception("Could not get DPI for monitor.");
    }

    uint scaleFactorPercent = (uint)(((long)dpiX * 100 + (96 >> 1)) / 96);
    return scaleFactorPercent / 100.0;
}

private void SetDragRegionForCustomTitleBar(AppWindow appWindow)
{
    // Check to see if customization is supported.
    // Currently only supported on Windows 11.
    if (AppWindowTitleBar.IsCustomizationSupported()
        && appWindow.TitleBar.ExtendsContentIntoTitleBar)
    {
        double scaleAdjustment = GetScaleAdjustment();

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

        List<Windows.Graphics.RectInt32> dragRectsList = new();

        Windows.Graphics.RectInt32 dragRectL;
        dragRectL.X = (int)((LeftPaddingColumn.ActualWidth) * scaleAdjustment);
        dragRectL.Y = 0;
        dragRectL.Height = (int)(AppTitleBar.ActualHeight * scaleAdjustment);
        dragRectL.Width = (int)((IconColumn.ActualWidth
                                + TitleColumn.ActualWidth
                                + LeftDragColumn.ActualWidth) * scaleAdjustment);
        dragRectsList.Add(dragRectL);

        Windows.Graphics.RectInt32 dragRectR;
        dragRectR.X = (int)((LeftPaddingColumn.ActualWidth
                            + IconColumn.ActualWidth
                            + TitleTextBlock.ActualWidth
                            + LeftDragColumn.ActualWidth
                            + SearchColumn.ActualWidth) * scaleAdjustment);
        dragRectR.Y = 0;
        dragRectR.Height = (int)(AppTitleBar.ActualHeight * scaleAdjustment);
        dragRectR.Width = (int)(RightDragColumn.ActualWidth * scaleAdjustment);
        dragRectsList.Add(dragRectR);

        Windows.Graphics.RectInt32[] dragRects = dragRectsList.ToArray();

        appWindow.TitleBar.SetDragRectangles(dragRects);
    }
}

Warning

AppWindow uses physical pixels for compatibility with UI frameworks that don't use logical coordinates. If you use WPF or WinUI 3, RightInset, LeftInset, and the values passed to SetDragRectangles need to be adjusted if the display scale is not 100%. In this example, we calculate a scaleAdjustment value to account for the display scale setting.

For WPF, you can handle the Window.DpiChanged event to get the NewDpi value.

For WinUI 3, use Platform Invoke (P/Invoke) to call the native GetDpiForMonitor function, as shown in the preceding example.

You can get the height of the system TitleBar (int titleBarHeight = appWindow.TitleBar.Height;) and use that to set the height of your custom title bar and drag regions. However, the design guidance recommends setting the title bar height to 48px if you add other controls. In this case, the height of the system title bar will not match your content, so instead, use the ActualHeight of the title bar element to set the drag region height.

System caption buttons

(Windows 11 only. See Platform options for more info.)

The system reserves the upper-left or upper-right corner of the app window for the system caption buttons (minimize, maximize/restore, close). The system retains control of the caption button area to guarantee that minimum functionality is provided for dragging, minimizing, maximizing, and closing the window. The system draws the Close button in the upper-right for left-to-right languages and the upper-left for right-to-left languages.

You can draw content underneath the caption control area, such as your app background, but you should not put any UI that you expect the user to be able to interact with. It does not receive any input because input for the caption controls is handled by the system.

These lines from the previous example show the padding columns in the XAML that defines the title bar. Using padding columns instead of margins ensures that the background paints the area under the caption control buttons (for transparent buttons). Using both right and left padding columns ensures that your title bar behaves correctly in both right-to-left and left-to-right layouts.

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

The dimensions and position of the caption control area is communicated by the AppWindowTitleBar class so that you can account for it in the layout of your title bar UI. The width of the reserved region on each side is given by the LeftInset or RightInset properties, and its height is given by the Height property.

Here's how the width of the padding columns is specified when the drag regions are calculated and set.

// Get caption button occlusion information.
int CaptionButtonOcclusionWidthRight = appWindow.TitleBar.RightInset;
int CaptionButtonOcclusionWidthLeft = appWindow.TitleBar.LeftInset;

// Set the width of padding columns in the UI.
RightPaddingColumn.Width = new GridLength(CaptionButtonOcclusionWidthRight);
LeftPaddingColumn.Width = new GridLength(CaptionButtonOcclusionWidthLeft);

Important

See important information in the Interactive content section about how display scaling affects these values.

Color and transparency in caption buttons

When you extend your app content into the title bar area, you can make the background of the caption buttons transparent to let your app background show through. You typically set the background to Colors.Transparent for full transparency. For partial transparency, set the alpha channel for the Color you set the property to.

(Windows 11 only. See Platform options for more info.)

These title bar properties can be transparent:

All other color properties will continue to ignore the alpha channel. If ExtendsContentIntoTitleBar is set to false, the alpha channel is always ignored for all AppWindowTitleBar color properties.

Reference: Colors.Transparent, ColorHelper

The button background color is not applied to the Close button hover and pressed states. The close button always uses the system-defined color for those states.

Mica is a delightful material that helps distinguish the window that's in focus. We recommend it as the background for long-lived windows in Windows 11. If you have applied Mica in the client area of your window, you can extend it into the titlebar area and make your caption buttons transparent for the Mica to show through. See Mica material for more info.

Dim the title bar when the window is inactive

You should make it obvious when your window is active or inactive. At a minimum, you should change the color of the text, icons, and buttons in your title bar.

Handle an event to determine the activation state of the window, and update your title bar UI as needed. How you determine the state of the window depends on the UI framework you use for your app.

Reset the title bar

(Windows 11 only. See Platform options for more info.)

To reset or switch to the system title bar while your app is running, you can call AppWindowTitleBar.ResetToDefault.

m_AppWindow.TitleBar.ResetToDefault();

Show and hide the title bar

If you add support for full screen or compact overlay modes to your app, you might need to make changes to your title bar when your app switches between these modes.

(Windows 11 only. See Platform options for more info.)

When your app runs in full screen mode, the system hides the title bar and caption control buttons. You can handle the AppWindow.Changed event and check the event args DidPresenterChange property to determine if you should show, hide, or change the title bar in response to a new window presentation.

This example shows how to handle the Changed event to show and hide the AppTitleBar element from previous examples. If the window is put in compact overlay mode, the title bar is reset to the default system title bar (or you could provide a custom title bar optimized for compact overlay).

public MainWindow()
{
    this.InitializeComponent();

    m_AppWindow = GetAppWindowForCurrentWindow();
    m_AppWindow.Changed += AppWindow_Changed;
}

private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
    // Check to see if customization is supported.
    // Currently only supported on Windows 11.
    if (args.DidPresenterChange
        && AppWindowTitleBar.IsCustomizationSupported())
    {
        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;
                SetDragRegionForCustomTitleBar(sender);
                break;

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

Full screen and compact overlay modes can be entered only if supported by your app. See Manage app windows, FullScreenPresenter, and CompactOverlayPresenter for more info.

Do's and don'ts

  • Do make it obvious when your window is active or inactive. At a minimum, change the color of the text, icons, and buttons in your title bar.
  • Do define a drag region along the top edge of the app canvas. Matching the placement of system title bars makes it easier for users to find.
  • Do define a drag region that matches the visual title bar (if any) on the app's canvas.

Full customization example

This examples shows all the code described in the Full customization section.

(Windows 11 only. See Platform options for more info.)

<Window
    x:Class="WASDK_ExtendedTitleBar.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WASDK_ExtendedTitleBar"
    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="Auto"/>
                <ColumnDefinition x:Name="RightDragColumn" Width="*"/>
                <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
            </Grid.ColumnDefinitions>
            <Image x:Name="TitleBarIcon" Source="/Images/WindowIcon.png"
           Grid.Column="1"
           Width="16" Height="16"
           Margin="8,0,0,0"/>
            <TextBlock x:Name="TitleTextBlock" 
               Text="App title" 
               Style="{StaticResource CaptionTextBlockStyle}"
               Grid.Column="2"
               VerticalAlignment="Center"
               Margin="4,0,0,0"/>
            <AutoSuggestBox Grid.Column="4" QueryIcon="Find"
                    PlaceholderText="Search"
                    VerticalAlignment="Center"
                    Width="260" Margin="4,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;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using WinRT.Interop;

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

        public MainWindow()
        {
            this.InitializeComponent();

            m_AppWindow = GetAppWindowForCurrentWindow();
            m_AppWindow.Changed += AppWindow_Changed;

            // Check to see if customization is supported.
            // Currently only supported on Windows 11.
            if (AppWindowTitleBar.IsCustomizationSupported())
            {
                var titleBar = m_AppWindow.TitleBar;
                titleBar.ExtendsContentIntoTitleBar = true;
                AppTitleBar.Loaded += AppTitleBar_Loaded;
                AppTitleBar.SizeChanged += AppTitleBar_SizeChanged;
            }
            else
            {
                // Title bar customization using these APIs is currently
                // supported only on Windows 11. In other cases, hide
                // the custom title bar element.
                AppTitleBar.Visibility = Visibility.Collapsed;

                // Show alternative UI for any functionality in
                // the title bar, such as search.
            }

        }

        private void AppTitleBar_Loaded(object sender, RoutedEventArgs e)
        {
            if (AppWindowTitleBar.IsCustomizationSupported())
            {
                SetDragRegionForCustomTitleBar(m_AppWindow);
            }
        }

        private void AppTitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (AppWindowTitleBar.IsCustomizationSupported()
                && m_AppWindow.TitleBar.ExtendsContentIntoTitleBar)
            {
                // Update drag region if the size of the title bar changes.
                SetDragRegionForCustomTitleBar(m_AppWindow);
            }
        }

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

        [DllImport("Shcore.dll", SetLastError = true)]
        internal static extern int GetDpiForMonitor(IntPtr hmonitor, Monitor_DPI_Type dpiType, out uint dpiX, out uint dpiY);

        internal enum Monitor_DPI_Type : int
        {
            MDT_Effective_DPI = 0,
            MDT_Angular_DPI = 1,
            MDT_Raw_DPI = 2,
            MDT_Default = MDT_Effective_DPI
        }

        private double GetScaleAdjustment()
        {
            IntPtr hWnd = WindowNative.GetWindowHandle(this);
            WindowId wndId = Win32Interop.GetWindowIdFromWindow(hWnd);
            DisplayArea displayArea = DisplayArea.GetFromWindowId(wndId, DisplayAreaFallback.Primary);
            IntPtr hMonitor = Win32Interop.GetMonitorFromDisplayId(displayArea.DisplayId);

            // Get DPI.
            int result = GetDpiForMonitor(hMonitor, Monitor_DPI_Type.MDT_Default, out uint dpiX, out uint _);
            if (result != 0)
            {
                throw new Exception("Could not get DPI for monitor.");
            }

            uint scaleFactorPercent = (uint)(((long)dpiX * 100 + (96 >> 1)) / 96);
            return scaleFactorPercent / 100.0;
        }

        private void SetDragRegionForCustomTitleBar(AppWindow appWindow)
        {
            if (AppWindowTitleBar.IsCustomizationSupported()
                && appWindow.TitleBar.ExtendsContentIntoTitleBar)
            {
                double scaleAdjustment = GetScaleAdjustment();

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

                List<Windows.Graphics.RectInt32> dragRectsList = new();

                Windows.Graphics.RectInt32 dragRectL;
                dragRectL.X = (int)((LeftPaddingColumn.ActualWidth) * scaleAdjustment);
                dragRectL.Y = 0;
                dragRectL.Height = (int)(AppTitleBar.ActualHeight * scaleAdjustment);
                dragRectL.Width = (int)((IconColumn.ActualWidth
                                        + TitleColumn.ActualWidth
                                        + LeftDragColumn.ActualWidth) * scaleAdjustment);
                dragRectsList.Add(dragRectL);

                Windows.Graphics.RectInt32 dragRectR;
                dragRectR.X = (int)((LeftPaddingColumn.ActualWidth
                                    + IconColumn.ActualWidth
                                    + TitleTextBlock.ActualWidth
                                    + LeftDragColumn.ActualWidth
                                    + SearchColumn.ActualWidth) * scaleAdjustment);
                dragRectR.Y = 0;
                dragRectR.Height = (int)(AppTitleBar.ActualHeight * scaleAdjustment);
                dragRectR.Width = (int)(RightDragColumn.ActualWidth * scaleAdjustment);
                dragRectsList.Add(dragRectR);

                Windows.Graphics.RectInt32[] dragRects = dragRectsList.ToArray();

                appWindow.TitleBar.SetDragRectangles(dragRects);
            }
        }

        private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
        {
            if (args.DidPresenterChange
                && AppWindowTitleBar.IsCustomizationSupported())
            {
                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;
                        SetDragRegionForCustomTitleBar(sender);
                        break;

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

        private void SwitchPresenter(object sender, RoutedEventArgs e)
        {
            if (m_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 == m_AppWindow.Presenter.Kind)
                {
                    m_AppWindow.SetPresenter(AppWindowPresenterKind.Default);
                }
                else
                {
                    // Else request a presenter of the selected kind
                    // to be created and applied to the window.
                    m_AppWindow.SetPresenter(newPresenterKind);
                }
            }
        }
    }
}

Related articles


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK