Новые возможности .NET MAUI для .NET 9

Основное внимание в пользовательском интерфейсе приложений .NET Multi-platform (.NET MAUI) в .NET 9 уделяется улучшению качества продукта. Это включает расширение охвата тестов, завершение тестирования сценариев и исправление ошибок. Дополнительные сведения о улучшении качества продукта в .NET MAUI 9 см. в следующих заметках о выпуске:

Внимание

Из-за работы с внешними зависимостями, такими как средства Xcode или Android SDK, политика поддержки .NET MAUI отличается от политики поддержки .NET и .NET Core. Дополнительные сведения см. в политике поддержки .NET MAUI.

В .NET 9 MAUI .NET поставляется в виде рабочей нагрузки .NET и нескольких пакетов NuGet. Преимущество этого подхода заключается в том, что он позволяет легко закреплять проекты в определенных версиях, а также позволяет легко просматривать нераспроданные или экспериментальные сборки. При создании проекта .NET MAUI необходимые пакеты NuGet автоматически добавляются в проект.

Новые элементы управления

.NET MAUI 9 включает два новых элемента управления.

HybridWebView

HybridWebView включает размещение произвольного содержимого HTML/JS/CSS в WebView и обеспечивает обмен данными между кодом в WebView (JavaScript) и кодом, на котором размещается WebView (C#/.NET). Например, если у вас есть существующее приложение React JS, его можно разместить в кроссплатформенной машинном приложении .NET MAUI и создать серверную часть приложения с помощью C# и .NET.

Чтобы создать приложение .NET MAUI, HybridWebView необходимо:

  • Веб-содержимое приложения, состоящее из статического HTML, JavaScript, CSS, изображений и других файлов.
  • Элемент HybridWebView управления в составе пользовательского интерфейса приложения. Это можно сделать, ссылаясь на него в XAML приложения.
  • Код в веб-содержимом и в C#/.NET, который использует HybridWebView API для отправки сообщений между двумя компонентами.

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

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

  1. Откройте существующий проект приложения .NET MAUI или создайте проект приложения .NET MAUI.

  2. Добавьте веб-содержимое в проект приложения .NET MAUI.

    Веб-содержимое приложения должно быть включено в проект .NET MAUI в качестве необработанных ресурсов. Необработанный ресурс — это любой файл в папке Resources\Raw приложения и включает вложенные папки. HybridWebViewДля веб-содержимого следует поместить в папку Resources\Raw\wwwroot с основным файлом с именем index.html.

    Простое приложение может содержать следующие файлы и содержимое:

    • Resources\Raw\wwwroot\index.html с содержимым для основного пользовательского интерфейса:

      <!DOCTYPE html>
      
      <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
      <head>
          <meta charset="utf-8" />
          <title></title>
          <link rel="icon" href="data:,">
          <script src="scripts/HybridWebView.js"></script>
          <script>
              window.addEventListener(
                  "HybridWebViewMessageReceived",
                  function (e) {
                      var messageFromCSharp = document.getElementById("messageFromCSharp");
                      messageFromCSharp.value += '\r\n' + e.detail.message;
                  });
          </script>
      </head>
      <body>
          <h1>HybridWebView app!</h1>
          <div>
              <button onclick="window.HybridWebView.SendRawMessage('Message from JS!')">Send message to C#</button>
          </div>
          <div>
              Messages from C#: <textarea readonly id="messageFromCSharp" style="width: 80%; height: 300px;"></textarea>
          </div>
      </body>
      </html>
      
    • Resources\Raw\wwwroot\scripts\HybridWebView.js со стандартной HybridWebView библиотекой JavaScript:

      function HybridWebViewInit() {
      
          function DispatchHybridWebViewMessage(message) {
              const event = new CustomEvent("HybridWebViewMessageReceived", { detail: { message: message } });
              window.dispatchEvent(event);
          }
      
          if (window.chrome && window.chrome.webview) {
              // Windows WebView2
              window.chrome.webview.addEventListener('message', arg => {
                  DispatchHybridWebViewMessage(arg.data);
              });
          }
          else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
              // iOS and MacCatalyst WKWebView
              window.external = {
                  "receiveMessage": message => {
                      DispatchHybridWebViewMessage(message);
                  }
              };
          }
          else {
              // Android WebView
              window.addEventListener('message', arg => {
                  DispatchHybridWebViewMessage(arg.data);
              });
          }
      }
      
      window.HybridWebView = {
          "SendRawMessage": function (message) {
      
              if (window.chrome && window.chrome.webview) {
                  // Windows WebView2
                  window.chrome.webview.postMessage(message);
              }
              else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
                  // iOS and MacCatalyst WKWebView
                  window.webkit.messageHandlers.webwindowinterop.postMessage(message);
              }
              else {
                  // Android WebView
                  hybridWebViewHost.sendRawMessage(message);
              }
          }
      }
      
      HybridWebViewInit();
      

    Затем добавьте в проект любое дополнительное веб-содержимое.

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

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

  3. Добавьте элемент управления в HybridWebView приложение:

    <Grid RowDefinitions="Auto,*"
          ColumnDefinitions="*">
        <Button Text="Send message to JavaScript"
                Clicked="OnSendMessageButtonClicked" />
        <HybridWebView x:Name="hybridWebView"
                       RawMessageReceived="OnHybridWebViewRawMessageReceived"
                       Grid.Row="1" />
    </Grid>
    
  4. HybridWebView Используйте API для отправки сообщений между кодом JavaScript и C#:

    private void OnSendMessageButtonClicked(object sender, EventArgs e)
    {
        hybridWebView.SendRawMessage($"Hello from C#!");
    }
    
    private async void OnHybridWebViewRawMessageReceived(object sender, HybridWebViewRawMessageReceivedEventArgs e)
    {
        await DisplayAlert("Raw Message Received", e.Message, "OK");
    }
    

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

Заголовок панели для Windows

Элемент TitleBar управления предоставляет возможность добавить настраиваемую строку заголовка в приложение в Windows:

Обзор панели заголовков .NET MAUI.

Можно TitleBar задать как значение свойства в любом Windowиз следующих значенийWindow.TitleBar:

<Window.TitleBar>
    <TitleBar x:Name="TeamsTitleBar"
              Title="Hello World"
              Icon="appicon.png"
              HeightRequest="46">
        <TitleBar.Content>
            <Entry x:Name="SearchTitleBar"
                   Placeholder="Search"
                   VerticalOptions="Center"
                   MinimumWidthRequest="300"
                   MaximumWidthRequest="450"
                   HeightRequest="32"/>
        </TitleBar.Content>
    </TitleBar>
</Window.TitleBar>

Пример его использования в C#:

Window.TitleBar = new TitleBar
{
    Title = "MAUI App",
    Icon = "appicon.png",
    HeightRequest = 46,
    LeadingContent = new AvatarButton()
};

A TitleBar очень настраивается с помощью его Contentсвойств LeadingContentи TrailingContent свойств:

<TitleBar Title="My App"
          BackgroundColor="#512BD4"
          HeightRequest="48">
    <TitleBar.Content>
        <SearchBar Placeholder="Search"
                   MaximumWidthRequest="300"
                   HorizontalOptions="FillAndExpand"
                   VerticalOptions="Center" />
    </TitleBar.Content>
    <TitleBar.TrailingContent>
        <ImageButton HeightRequest="36"
                     WidthRequest="36"
                     BorderWidth="0"
                     Background="Transparent">
            <ImageButton.Source>
                <FontImageSource Size="16"
                                 Glyph="&#xE713;"
                                 FontFamily="SegoeMDL2"/>
            </ImageButton.Source>
        </ImageButton>
    </TitleBar.TrailingContent>
</TitleBar>

На следующем снимок экрана показан полученный внешний вид:

Снимок экрана панели заголовка .NET MAUI.

Примечание.

Поддержка Mac Catalyst для TitleBar элемента управления будет добавлена в будущий выпуск.

Усовершенствования элементов управления

.NET MAUI 9 включает усовершенствования элементов управления.

Режим привязки BackButtonBehavior OneWay

Теперь вместо режима привязки для IsVisible приложения оболочки и IsEnabled BackButtonBehavior в приложении BindingMode.OneWay оболочки BindingMode.OneTime. Это позволяет более легко управлять поведением кнопки "Назад" во время выполнения с привязками данных:

<ContentPage ...>    
    <Shell.BackButtonBehavior>
        <BackButtonBehavior Command="{Binding BackCommand}"
                            IsVisible="{Binding IsBackButtonVisible}"
                            IconOverride="back.png" />   
    </Shell.BackButtonBehavior>
    ...
</ContentPage>

BlazorWebView

В iOS и Mac Catalyst 18 .NET MAUI 9 изменяет поведение по умолчанию для размещения содержимого в a BlazorWebView localhost. Внутренний 0.0.0.0 адрес, используемый для размещения содержимого, больше не работает и приводит к тому, BlazorWebView что не загружается содержимое и отрисовка в виде пустого прямоугольника.

Чтобы воспользоваться адресом 0.0.0.0 , добавьте следующий код в MauiProgram класс:

// Set this switch to use the LEGACY behavior of always using 0.0.0.0 to host BlazorWebView
AppContext.SetSwitch("BlazorWebView.AppHostAddressAlways0000", true);

CollectionView и CarouselView

.NET MAUI 9 включает два необязательных новых обработчика в iOS и Mac Catalyst, которые позволяют повысить CollectionView CarouselViewпроизводительность и стабильность. Эти обработчики основаны на UICollectionView API.

Чтобы воспользоваться этими обработчиками, добавьте следующий код в MauiProgram класс:

#if IOS || MACCATALYST
builder.ConfigureMauiHandlers(handlers =>
{
    handlers.AddHandler<Microsoft.Maui.Controls.CollectionView, Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2>();
    handlers.AddHandler<Microsoft.Maui.Controls.CarouselView, Microsoft.Maui.Controls.Handlers.Items2.CarouselViewHandler2>();
});
#endif

Поддержка ввода обратимой клавиатуры

.NET MAUI 9 добавляет новую поддержку ввода обратимой клавиатуры для Password, Dateи Time. Их можно включить Editor и Entry элементы управления:

<Entry Keyboard="Date" />

TimePicker

TimePickerTimeSelected получает событие, которое возникает при изменении выбранного времени. Объект TimeChangedEventArgs , сопровождающий TimeSelected событие, имеет NewTime и OldTime свойства, которые указывают новое и старое время соответственно.

WebView

WebViewProcessTerminated добавляет событие, которое возникает при неожиданном WebView завершении процесса. Объект WebViewProcessTerminatedEventArgs , сопровождающий это событие, определяет свойства конкретной платформы, указывающие, почему процесс завершился сбоем.

Жизненный цикл приложения

.NET MAUI 9 добавляет следующие методы удаленного жизненного цикла уведомлений в iOS и Mac Catalyst:

  • RegisteredForRemoteNotifications, который вызывается, когда приложение успешно зарегистрировано для удаленных уведомлений.
  • ReceivedRemoteNotifications, который вызывается при получении удаленного уведомления.

В следующем примере показано, как использовать эти методы жизненного цикла:

using Microsoft.Maui.LifecycleEvents;

namespace PlatformLifecycleDemo;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureLifecycleEvents(events =>
            {
#if IOS || MACCATALYST
                events.AddiOS(ios => ios
                    .ReceivedRemoteNotifications((app, dictionary) => LogEvent(nameof(iOSLifecycle.OnReceivedRemoteNotifications)))
                    .RegisteredForRemoteNotifications((app, data) => LogEvent(nameof(iOSLifecycle.OnRegisteredForRemoteNotifications)));
#endif
                static bool LogEvent(string eventName, string type = null)
                {
                    System.Diagnostics.Debug.WriteLine($"Lifecycle event: {eventName}{(type == null ? string.Empty : $" ({type})")}");
                    return true;
                }
            });

        return builder.Build();
    }
}

Отключение обработчика

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

В .NET MAUI 9 обработчики автоматически отключают от своих элементов управления, например при переходе назад в приложении. В некоторых сценариях это поведение может не потребоваться. Поэтому .NET MAUI 9 добавляет присоединенное HandlerProperties.DisconnectPolicy свойство для управления при отключении обработчиков от их элементов управления. Для этого свойства требуется HandlerDisconnectPolicy аргумент с HandlerDisconnectPolicy перечислением, определяющим следующие значения:

  • Automatic, указывающее, что обработчики будут отключены автоматически. Это значение по умолчанию для присоединенного свойства HandlerProperties.DisconnectPolicy.
  • Manual, указывающее, что обработчики должны быть отключены вручную путем вызова DisconnectHandler реализации.

В следующем примере показано задание присоединенного HandlerProperties.DisconnectPolicy свойства:

<controls:Video x:Name="video"
                HandlerProperties.DisconnectPolicy="Manual"
                Source="video.mp4"
                AutoPlay="False" />

Эквивалентный код на C# выглядит так:

Video video = new Video
{
    Source = "video.mp4",
    AutoPlay = false
};
HandlerProperties.SetDisconnectPolicy(video, HandlerDisconnectPolicy.Manual);

Кроме того, существует DisconnectHandlers метод расширения, который отключает обработчики от заданного:IView

video.DisconnectHandlers();

При отключении метод будет распространяться по дереву управления до тех пор, DisconnectHandlers пока он не завершится или не появится в элементе управления, настроив политику вручную.

Поддержка нескольких окон

.NET MAUI 9 добавляет возможность перенести определенное окно на передний план в Mac Catalyst и Windows с Application.Current.ActivateWindow помощью метода:

Application.Current?.ActivateWindow(windowToActivate);

Встроенное внедрение

.NET MAUI 9 включает полные API для собственных сценариев внедрения, которые ранее пришлось вручную добавить в проект:

var mauiApp = MauiProgram.CreateMauiApp();

#if ANDROID
var mauiContext = new MauiContext(mauiApp.Services, window);
#else
var mauiContext = new MauiContext(mauiApp.Services);
#endif

var mauiView = new MyMauiContent();
var nativeView = mauiView.ToPlatform(mauiContext);

Кроме того, можно использовать ToPlatformEmbedded метод, передавая Window платформу, на которой выполняется приложение:

var mauiApp = MauiProgram.CreateMauiApp();
var mauiView = new MyMauiContent();
var nativeView = mauiView.ToPlatformEmbedded(mauiApp, window);

В обоих примерах — это версия mauiViewдля конкретной платформыnativeView.

Чтобы загрузить собственное внедренное приложение в .NET MAUI 9, вызовите метод расширения в объекте UseMauiEmbeddedApp MauiAppBuilder :

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();

        builder
            .UseMauiEmbeddedApp<App>();

        return builder.Build();
    }
}

Шаблоны проектов

.NET MAUI 9 добавляет шаблон проекта гибридного и веб-приложения .NET MAUI Blazor в Visual Studio, который создает решение с гибридным приложением .NET MAUI Blazor с приложением Blazor Web, которое предоставляет общий код в проекте библиотеки классов Razor.

Шаблон также можно использовать из dotnew new:

dotnet new maui-blazor-web -n AllTheTargets

Синхронизация Xcode

.NET MAUI 9 включает синхронизацию Xcode (xcsync), которая позволяет использовать Xcode для управления конкретными файлами Apple с проектами .NET, включая каталоги активов, plist-файлы, раскадровки и XIB-файлы. Средство содержит две основные команды для создания временного проекта Xcode из проекта .NET и синхронизации изменений из файлов Xcode обратно в проект .NET.

Вы используете dotnet build для xcsync-generate xcsync-sync создания или синхронизации этих файлов и передачи в файл проекта и дополнительных аргументов:

dotnet build /t:xcsync-generate
    /p:xcSyncProjectFile=<PROJECT>
    /p:xcSyncXcodeFolder=<TARGET_XCODE_DIRECTORY>
    /p:xcSyncTargetFrameworkMoniker=<FRAMEWORK>
    /p:xcSyncVerbosity=<LEVEL>

Дополнительные сведения см. в разделе синхронизации Xcode.

Устаревшие интерфейсы API

.NET MAUI 9 не рекомендует некоторые API, которые будут полностью удалены в будущем выпуске.

Кадр

Элемент Frame управления помечен как устаревший в .NET MAUI 9 и будет полностью удален в будущем выпуске. Элемент Border управления должен использоваться на его месте. Дополнительные сведения см. в разделе Border.

MainPage

Вместо определения первой страницы приложения с помощью MainPage свойства в Application объекте необходимо задать Page свойство на Window первой странице приложения. Это то, что происходит внутри .NET MAUI при установке MainPage свойства, поэтому изменение поведения, введенное MainPage свойством, помеченным как устаревшее.

В следующем примере показано задание Page свойства на объекте WindowCreateWindow с помощью переопределения:

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }

    protected override Window CreateWindow(IActivationState? activationState)
    {
        return new Window(new AppShell());
    }
}

Свойство MainPage сохраняется для .NET MAUI 9, но будет полностью удалено в будущем выпуске.

Макеты совместимости

Классы макета совместимости в Microsoft.Maui.Controls.Compatibility пространстве имен устарели.

Вызовы устаревших мер

VisualElement Устаревшие методы мер устарели:

  • protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint).
  • public virtual SizeRequest Measure(double widthConstraint, double heightConstraint, MeasureFlags flags = MeasureFlags.None) из VisualElement.

В качестве замены был введен следующий метод:

  • public size Measure(double widthConstraint, double heightConstraint)

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

Кроме того, структуру Microsoft.Maui.SizeRequest устарела. Вместо этого Microsoft.Maui.Size следует использовать.

.NET для Android

.NET для Android 9, который добавляет поддержку API 35, включает в себя работу, чтобы сократить время сборки, а также повысить обрезку приложений для уменьшения размера и повышения производительности. Дополнительные сведения о .NET для Android 9 см. в следующих заметках о выпуске:

Пакеты ресурсов

.NET для Android 9 представляет возможность размещать ресурсы в отдельный пакет, известный как пакет ресурсов. Это позволяет отправлять игры и приложения, которые обычно будут больше базового размера пакета, разрешенного Google Play. Поместив эти ресурсы в отдельный пакет, вы получаете возможность отправить пакет размером до 2 ГБ, а не базовый размер пакета 200 Мб.

Внимание

Пакеты ресурсов могут содержать только ресурсы. В случае с .NET для Android это означает элементы, которые имеют действие сборки AndroidAsset .

Приложения .NET MAUI определяют ресурсы с помощью действия сборки MauiAsset . Пакет ресурсов можно указать с помощью атрибута AssetPack :

<MauiAsset
    Include="Resources\Raw\**"
    LogicalName="%(RecursiveDir)%(Filename)%(Extension)"
    AssetPack="myassetpack" />

Примечание.

Дополнительные метаданные будут игнорироваться другими платформами.

Если у вас есть определенные элементы, которые вы хотите разместить в пакете ресурсов, можно использовать Update атрибут для определения AssetPack метаданных:

<MauiAsset Update="Resources\Raw\MyLargeAsset.txt" AssetPack="myassetpack" />

Пакеты ресурсов могут иметь различные варианты доставки, которые управляют установкой ресурсов на устройстве:

  • Установка пакетов времени устанавливается одновременно с приложением. Этот тип пакета может быть размером до 1 ГБ, но у вас может быть только один из них. Этот тип доставки указан с метаданными InstallTime .
  • Быстрые пакеты будут устанавливаться в какой-то момент вскоре после завершения установки приложения. Приложение сможет запуститься, пока установлен этот тип пакета, поэтому перед попыткой использовать ресурсы необходимо проверить, что оно завершено. Этот тип пакета ресурсов может быть размером до 512 Мб. Этот тип доставки указан с метаданными FastFollow .
  • Пакеты по запросу никогда не будут загружаться на устройство, если приложение не запрашивает его. Общий размер всех пакетов ресурсов не может превышать 2 Гб, и вы можете иметь до 50 отдельных пакетов активов. Этот тип доставки указан с метаданными OnDemand .

В приложениях .NET MAUI тип доставки можно указать с помощью атрибута DeliveryType в MauiAsset:

<MauiAsset Update="Resources\Raw\myvideo.mp4" AssetPack="myassetpack" DeliveryType="FastFollow" />

Дополнительные сведения о пакетах активов Android см. в разделе "Пакеты активов Android".

Поддержка Android 15

.NET для Android 9 добавляет привязки .NET для Android 15 (API 35). Чтобы создать эти API, обновите целевую платформу проекта:

<TargetFramework>net9.0-android35</TargetFramework>

Маршалированные методы LLVM

Методы маршаллированных низкоуровневых виртуальных машин (LLVM) теперь включены по умолчанию в .NET для Android 9 в приложениях, отличных от Blazor. Это привело к улучшению производительности тестового приложения на 10 % .

Маршаллированные методы LLVM можно отключить в файле проекта (CSPROJ):

<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0-android'">
    <AndroidEnableLLVM>false</AndroidEnableLLVM>
    <AndroidEnableLLVMOptimizations>false</AndroidEnableLLVMOptimizations>
</PropertyGroup>

Усовершенствования обрезки

.NET для Android 9 включает исправления при использовании полной обрезки для уменьшения размера приложения. Полная обрезка обычно включена только для сборок выпуска приложения и может быть настроена в файле проекта (CSPROJ):

<PropertyGroup Condition="'$(Configuration)' == 'Release' And '$(TargetFramework)' == 'net9.0-android'">
    <TrimMode>Full</TrimMode>
</PropertyGroup>

.NET для iOS

.NET 9 в iOS, tvOS, Mac Catalyst и macOS используют Xcode 15.2 для следующих версий платформы:

  • iOS: 17.2
  • tvOS: 17.2
  • Mac Catalyst: 17.2
  • macOS: 14.2

Дополнительные сведения о .NET 9 в iOS, tvOS, Mac Catalyst и macOS см. в следующих заметках о выпуске:

Привязки

.NET для iOS 9 представляет возможность многоцелевой версии .NET для привязок iOS. Например, проект библиотеки может потребоваться создать для двух разных версий iOS:

<TargetFrameworks>net9.0-ios17.0;net9.0-ios17.2</TargetFrameworks>

При этом будут создаваться две библиотеки, одна с помощью привязок iOS 17.0 и одна с помощью привязок iOS 17.2.

Внимание

Проект приложения всегда должен ориентироваться на последний пакет SDK для iOS.

Собственный AOT для iOS и Mac Catalyst

В .NET для iOS 9 собственная компиляция накануне времени (AOT) для iOS и Mac Catalyst использует полную обрезку, чтобы уменьшить размер пакета приложения и производительность запуска. Это функция публикации, которую можно использовать при подготовке к отправке приложения.

Внимание

Приложение и это зависимости должны быть полностью обрезаны, чтобы использовать эту функцию.

См. также