MAUI, почему моя строка заголовка серая и как это исправить?

Я сделал пример проекта MAUI в Visual Studio 2022, но когда я запускаю программу, строка заголовка окна становится серой, серой, которая вообще не меняет цвет, когда окно теряет фокус (хотя текст заголовка, который я добавил, изменился с черного на до серого). У меня есть «Показать акцентный цвет на следующих поверхностях» с обоими флажками.

Почему мое окно MAUI не использует мой акцентный цвет и как это исправить?

Примечание. У меня Windows 10, поэтому я не могу использовать эту штуку, которая работает только в Windows 11.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
1 143
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Комментарий Александра Мэя ссылается на документ, в котором описывается полная настройка строки заголовка:

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

Из этого документа / содержимого строки заголовка и областей перетаскивания:

<Grid x:Name = "AppTitleBar">
    <Image Source = "Images/WindowIcon.png"
           HorizontalAlignment = "Left" 
           Width = "16" Height = "16" 
           Margin = "8,0"/>
    <TextBlock x:Name = "AppTitleTextBlock" Text = "App title"
               TextWrapping = "NoWrap"
               Style = "{StaticResource CaptionTextBlockStyle}" 
               VerticalAlignment = "Center"
               Margin = "28,0,0,0"/>
</Grid>
public MainWindow()
{
    this.InitializeComponent();

    ExtendsContentIntoTitleBar = true;
    SetTitleBar(AppTitleBar);

    AppTitleTextBlock.Text = AppInfo.Current.DisplayInfo.DisplayName;
}

ОРИГИНАЛЬНЫЙ ОТВЕТ

Вкратце: вы не можете управлять цветом строки заголовка в Windows 10. По крайней мере, через API-интерфейсы WinUI-3.

ВАЖНО: этот ответ описывает ситуацию с API WinUI-3.
Я оставлю это кому-то другому, чтобы выяснить, как использовать P/Invoke: создайте приложение C# .NET с WinUI 3 и взаимодействием Win32, чтобы получить API-интерфейсы Win32, которые могут работать в Windows 10.
Это может быть невозможно даже таким образом.
Мне неясно, способно ли окно, созданное WinUI-3 в Windows 10, физически изменить цвет своего заголовка.

Существующие API-интерфейсы WinUI-3 не поддерживают это в Windows 10. WinUI-3 — это то, на что Мауи ориентируется в Windows.

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

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

Дополнительные сведения приведены в Библиотеке пользовательского интерфейса Windows в Windows App SDK (WinUI 3).

Существует таблица Feature Window AppWindow, показывающая функции, поддерживаемые в Windows 10.
В этой таблице мы видим, что Window поддерживается в Windows 10, а AppWindow — нет.
Это также показывает, что Window позволяет установить (только) Title. Нужно AppWindow изменить цвета.

:-( Большой вопрос в том, почему они считают необходимым переопределить обычную строку заголовка Windows, чтобы заменить ее этой серой полосой?

Medinoc 29.11.2022 09:29

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

Alexandar May - MSFT 01.12.2022 10:18

Отлично, поэтому нам нужно расширить App Canvas, чтобы нарисовать фальшивую строку заголовка, чтобы обойти проблему... И из того, что мне удалось собрать, это именно то, что MAUI/WinUI3 уже делает, чтобы вызвать проблему. в первую очередь! Таким образом, мы должны нарисовать фальшивую строку заголовка, чтобы заменить собственную фальшивую строку заголовка WinUI3, чтобы исправить ущерб! Расскажите об инверсии абстракции...

Medinoc 02.12.2022 09:55

К сожалению, приведенный выше код предназначен для работы непосредственно в окне WinUI3, он не совместим со структурой MAUI на основе Shell.

Medinoc 03.01.2023 21:50

кажется не совместимым" - может быть, может и нет. Это действительно код для работы непосредственно в окне WinUI3, которое Мауи создает и отображает при работе в Windows. Код должен быть только на WinUI3, а не в кроссплатформенном коде. Если вам нужна помощь в этом, добавьте новый раздел к вашему вопросу. Покажите испробованный код, где вы добавили этот код и что пошло не так.

ToolmakerSteve 05.01.2023 04:37
Ответ принят как подходящий

В конце концов, я использовал код, основанный на этом, чтобы скрыть большую часть серой строки заголовка в Windows, а также зависимость только для Windows от библиотеки Windows Forms, которую я использую для дочернего создания и заключения окна WinUI3 внутри окна Windows. Форма (которая изменяет размер своего дочернего элемента при изменении размера).

Сторона WinForms

Я начинаю с создания базовой формы и добавляю код P/Invoke для дочернего элемента и изменения размера (а также упрощенную форму события FormClosed).

    public partial class Form1 : Form, IContainerForm
    {
        public Form1()
        {
            InitializeComponent();
        }

        static class NativeMethods
        {
            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr GetParent(IntPtr hWnd);

            public const int GWL_STYLE = -16;

            [DllImport("User32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]
            public extern static uint GetWindowLongU(IntPtr hwnd, int nIndex);
            [DllImport("User32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
            public extern static uint SetWindowLongU(IntPtr hwnd, int nIndex, uint dwNewLong);

            [DllImport("user32.dll", SetLastError = true)]
            internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
        }

        [Flags]
        public enum WindowStyles : uint
        {
            WS_BORDER = 0x800000,
            WS_CAPTION = 0xc00000,
            WS_CHILD = 0x40000000,
            WS_CLIPCHILDREN = 0x2000000,
            WS_CLIPSIBLINGS = 0x4000000,
            WS_DISABLED = 0x8000000,
            WS_DLGFRAME = 0x400000,
            WS_GROUP = 0x20000,
            WS_HSCROLL = 0x100000,
            WS_MAXIMIZE = 0x1000000,
            WS_MAXIMIZEBOX = 0x10000,
            WS_MINIMIZE = 0x20000000,
            WS_MINIMIZEBOX = 0x20000,
            WS_OVERLAPPED = 0x0,
            WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_SIZEFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
            WS_POPUP = 0x80000000u,
            WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
            WS_SIZEFRAME = 0x40000,
            WS_SYSMENU = 0x80000,
            WS_TABSTOP = 0x10000,
            WS_VISIBLE = 0x10000000,
            WS_VSCROLL = 0x200000
        }

        IntPtr hwndContained;

        #region IContainerForm members
        public IntPtr EnclosedHandle { get => hwndContained; }

        public void Enclose(IntPtr hWndToContain)
        {
            if (Handle==IntPtr.Zero)
                throw new InvalidOperationException("Cannot enclose window because current object's window is not created.");
            var windowStyles = (WindowStyles)NativeMethods.GetWindowLongU(hWndToContain, NativeMethods.GWL_STYLE);
            windowStyles &= ~WindowStyles.WS_OVERLAPPEDWINDOW;
            windowStyles &= ~WindowStyles.WS_POPUP;
            windowStyles &= ~WindowStyles.WS_CAPTION;
            windowStyles |= WindowStyles.WS_CHILD;
            NativeMethods.SetWindowLongU(hWndToContain, NativeMethods.GWL_STYLE, (uint)windowStyles);

            NativeMethods.SetParent(hWndToContain, Handle);
            hwndContained = hWndToContain;
            OnSizeChanged(EventArgs.Empty);
        }

        public string TitleText { get => this.Text; set => this.Text=value; }

        private event EventHandler? formClosedSimple;
        public event EventHandler FormClosedSimple { add => formClosedSimple+=value; remove => formClosedSimple-=value; }
        private void FireFormClosedSimple()
        {
            if (formClosedSimple != null)
                formClosedSimple(this, EventArgs.Empty);
        }
        protected override void OnFormClosed(FormClosedEventArgs e)
        {
            base.OnFormClosed(e);
            FireFormClosedSimple();
        }

        #endregion

        public static bool HasParent(IntPtr hWnd)
        {
            return NativeMethods.GetParent(hWnd)!=IntPtr.Zero;
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            int width = Width;
            int height = Height;
            if (width==0 || height==0 || WindowState == FormWindowState.Minimized)
                return;
            if (hwndContained!=IntPtr.Zero)
            {
                NativeMethods.MoveWindow(hwndContained, 0, 0, ClientSize.Width, ClientSize.Height, true);
            }
        }
    }

Чтобы вызывающему коду не приходилось напрямую манипулировать типами WinForms, я изолирую его за интерфейсом:

    public class Class1
    {
        public static IContainerForm CreateContainerForm()
        {
            var form = new Form1();
            form.Show();
            return form;
        }
        public static IContainerForm CreateContainerForm(int width, int height)
        {
            var form = new Form1();
            form.Width = width;
            form.Height = height;
            form.Show();
            return form;
        }
        public static bool HasParent(IntPtr hWnd) => Form1.HasParent(hWnd);
    }

    public interface IContainerForm
    {
        void Enclose(IntPtr hWnd);
        IntPtr EnclosedHandle { get; }
        string TitleText { get; set; }
        event EventHandler FormClosedSimple;
    }

Вот и все для проекта библиотеки управления Windows Forms.

Сторона МАУИ

Для начала добавлю условную ссылку на библиотеку управления Windows Forms:

    <ItemGroup Condition = "$(TargetFramework.Contains('-windows')) != false">
        <ProjectReference Include = "..\WinFormsLibrary1\WinFormsLibrary1.csproj" />
    </ItemGroup>

Тогда это просто вызов его (но только в Windows) из кода MAUI:

using Microsoft.Extensions.Logging;
using Microsoft.Maui.LifecycleEvents;
#if WINDOWS
using Microsoft.UI;
using Microsoft.UI.Windowing;
#endif

namespace MySecondMauiApp
{
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                });

#if DEBUG
            builder.Logging.AddDebug();
#endif
            AddTitleBarCodeOnWindows(builder, new System.Drawing.Size(640, 640));

            return builder.Build();
        }

        public static void AddTitleBarCodeOnWindows(MauiAppBuilder builder, System.Drawing.Size size)
        {
#if WINDOWS
            builder.ConfigureLifecycleEvents(events =>
            {
                events.AddWindows(wndLifeCycleBuilder =>
                {
                    wndLifeCycleBuilder.OnWindowCreated(window =>
                    {
                        IntPtr nativeWindowHandle = WinRT.Interop.WindowNative.GetWindowHandle(window);
                        WindowId win32WindowsId = Win32Interop.GetWindowIdFromWindow(nativeWindowHandle);
                        AppWindow winuiAppWindow = AppWindow.GetFromWindowId(win32WindowsId);
                        if (winuiAppWindow.Presenter is OverlappedPresenter p)
                        {
                            window.ExtendsContentIntoTitleBar = false;
                            p.SetBorderAndTitleBar(false, false);
                        }
                        var containerForm = WinFormsLibrary1.Class1.CreateContainerForm(size.Width, size.Height);
                        containerForm.Enclose(nativeWindowHandle);
                        containerForm.TitleText = window.Title;
                        //Apparently necessary since app doesn't close on its own.
                        //I would have thought closing the form, and therefore destroying the child MAUI Window, would do the trick.
                        containerForm.FormClosedSimple += (sender, args) => Application.Current.Quit();
                    });
                });
            });
#endif
        }

    }//class
}

Вот и все, теперь у вас есть новое блестящее приложение MAUI, в строке заголовка которого правильно используется ваш цвет акцента, когда он сфокусирован (и становится белым, когда он не сфокусирован):

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

вы можете изменить платформы/Windows/App.xaml

<maui:MauiWinUIApplication
x:Class = "MauiApp1.WinUI.App"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:maui = "using:Microsoft.Maui"
xmlns:local = "using:MauiApp1.WinUI">
<Application.Resources>
    <ResourceDictionary>
        <SolidColorBrush x:Key = "WindowCaptionBackground">#fff</SolidColorBrush>
        <SolidColorBrush x:Key = "WindowCaptionBackgroundDisabled">#fff</SolidColorBrush>
        <SolidColorBrush x:Key = "WindowCaptionForeground">#000</SolidColorBrush>
        <SolidColorBrush x:Key = "WindowCaptionForegroundDisabled">#000</SolidColorBrush>
    </ResourceDictionary>
</Application.Resources>
</maui:MauiWinUIApplication>

если вы хотите изменить цвет строки заголовка во время выполнения

1.Установите этот пакет

 <PackageReference Include = "PInvoke.User32" Version = "0.7.124" Condition = "$(TargetFramework.Contains('-windows')) == true" />

2.Добавьте следующий код

using Microsoft.Maui.Platform;
using WinRT.Interop;
using System.Runtime.Versioning;
using PInvoke;
using static PInvoke.User32;

namespace MauiApp1;

[SupportedOSPlatform("Windows")]
public static class TitleBar
{
    static Microsoft.UI.Xaml.Window? NativeWindow =>
        (Microsoft.UI.Xaml.Window?)Application.Current?.Windows.FirstOrDefault()?.Handler?.PlatformView;
    static Microsoft.UI.Xaml.ResourceDictionary Resources =>
        Microsoft.UI.Xaml.Application.Current.Resources;

    public static void SetColor(Color color)
    {
        Resources["WindowCaptionBackground"] = color.ToWindowsColor();
        Resources["WindowCaptionBackgroundDisabled"] = color.ToWindowsColor();
        TriggertTitleBarRepaint();
    }

    public static void SetStyle(TitleBarStyle style)
    {
        var color = style switch
        {
            TitleBarStyle.Default => Colors.Black,
            TitleBarStyle.LightContent => Colors.White,
            TitleBarStyle.DarkContent => Colors.Black,
            _ => throw new NotSupportedException($"{nameof(TitleBarStyle)} {style} is not yet supported on iOS")
        };
        Resources["WindowCaptionForeground"] = color.ToWindowsColor();
        Resources["WindowCaptionForegroundDisabled"] = color.ToWindowsColor();
        TriggertTitleBarRepaint();
    }

    static void TriggertTitleBarRepaint()
    {
        if (NativeWindow is null)
        {
            return;
        }

        var hWnd = WindowNative.GetWindowHandle(NativeWindow);
        var activeWindow = User32.GetActiveWindow();
        if (hWnd == activeWindow)
        {
            User32.PostMessage(hWnd, WindowMessage.WM_ACTIVATE, new IntPtr((int)0x00), IntPtr.Zero);
            User32.PostMessage(hWnd, WindowMessage.WM_ACTIVATE, new IntPtr((int)0x01), IntPtr.Zero);
        }
        else
        {
            User32.PostMessage(hWnd, WindowMessage.WM_ACTIVATE, new IntPtr((int)0x01), IntPtr.Zero);
            User32.PostMessage(hWnd, WindowMessage.WM_ACTIVATE, new IntPtr((int)0x00), IntPtr.Zero);
        }
    }
}
public enum TitleBarStyle
{
    Default = 0,
    LightContent = 1,
    DarkContent = 2
}

3.использовать

#if Windows
    TitleBar.SetColor(titleBarColor);
    TitleBar.SetStyle(TitleBarStyle.DarkContent);
#endif

Действует как на Win10, так и на Win11

Другие вопросы по теме