xamlwinui-3titlebarwinuiwindows-app-sdk

FlowDirection.RightToLeft does not apply to custom TitleBar or caption buttons


I'm building a WinUI 3 desktop application with a custom TitleBar. I've applied FlowDirection="RightToLeft" to the main grid in my MainWindow.xaml, but the caption buttons (minimize, maximize, close) and the title alignment remain in left-to-right layout.

<Window
    x:Class="YourNamespace.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:YourNamespace">
    
    <Window.SystemBackdrop>
        <MicaBackdrop />
    </Window.SystemBackdrop>

    <Grid x:Name="MainGrid" RowDefinitions="auto,*">
        <TitleBar
            Title="Custom Title Bar"
            IsBackButtonEnabled="True"
            IsBackButtonVisible="True"
            Subtitle="Beta">
            <TitleBar.RightHeader>
                <PersonPicture Height="25" Initials="JD" />
            </TitleBar.RightHeader>
        </TitleBar>
        <StackPanel Grid.Row="1">
            <Button Content="Click" />
        </StackPanel>
    </Grid>
</Window>

Code behind file:

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

        MainGrid.FlowDirection = FlowDirection.RightToLeft;

        ExtendsContentIntoTitleBar = true;

        this.AppWindow.TitleBar.IconShowOptions = Microsoft.UI.Windowing.IconShowOptions.ShowIconAndSystemMenu;
        this.AppWindow.TitleBar.ButtonBackgroundColor = Microsoft.UI.Colors.Transparent;
        this.AppWindow.TitleBar.PreferredHeightOption = Microsoft.UI.Windowing.TitleBarHeightOption.Tall;
    }
} 

What I expect:

The custom TitleBar content and caption buttons should appear mirrored, with caption buttons on the left side when FlowDirection.RightToLeft is applied.

What happens instead: Only the internal elements (MainGrid) respect RightToLeft.

The caption buttons and title remain in LeftToRight layout.

Question: How can I force the caption buttons and title bar in a WinUI 3 custom title bar to follow FlowDirection.RightToLeft? Is this a known limitation or am I missing a specific setting?

Thanks!


Solution

  • I tried the following code from the GitHub repo, and it seems to be working:

    <Window
        x:Class="WinUIApp3.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:local="using:WinUIApp3"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid x:Name="TitleBarPageWindowGrid" RowDefinitions="Auto,*">
            <TitleBar x:Name="AppTitleBar"
                Title="TITLE"
                Grid.Row="0"
                Subtitle="SUBTITLE" />
        </Grid>
    
    </Window>
    
    public sealed partial class MainWindow : Window
    {
        const int GWL_EXSTYLE = -20;
    
        const int WS_EX_LAYOUTRTL = 0x00400000;
    
        public MainWindow()
        {
            InitializeComponent();
            ExtendsContentIntoTitleBar = true;
            SetTitleBar(AppTitleBar);
            TitleBarPageWindowGrid.FlowDirection = FlowDirection.RightToLeft;
            UpdateCaptionButtonDirection(FlowDirection.RightToLeft);
        }
    
        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
        internal static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, nint newProc);
    
        [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
        internal static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
    
        private static nint GetWindowHandleForCurrentWindow(object target) =>
            WinRT.Interop.WindowNative.GetWindowHandle(target);
        private void UpdateCaptionButtonDirection(FlowDirection direction)
        {
            var hwnd = GetWindowHandleForCurrentWindow(this);
    
            if (hwnd != 0)
            {
                var exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
    
                if (direction == FlowDirection.RightToLeft)
                {
                    exStyle |= WS_EX_LAYOUTRTL;
                }
                else
                {
                    exStyle &= ~WS_EX_LAYOUTRTL;
                }
    
                SetWindowLongPtr(hwnd, GWL_EXSTYLE, exStyle);
            }
        }
    }