네이티브 포함

샘플을 찾아봅니다. 샘플 찾아보기

일반적으로 .NET 다중 플랫폼 앱 UI(.NET MAUI) 앱에는 레이아웃(예: Grid및 보기 Button가 포함된 레이아웃)이 포함된 페이지가 포함됩니다. 페이지, 레이아웃 및 뷰는 모두 .에서 Element파생됩니다. 네이티브 포함을 사용하면 .NET Android, .NET iOS, .NET Mac Catalyst 및 WinUI 네이티브 앱에서 Element 파생되는 모든 .NET MAUI 컨트롤을 사용할 수 있습니다.

네이티브 앱에서 .NET MAUI 컨트롤을 사용하는 프로세스는 다음과 같습니다.

  1. 네이티브 임베디드 앱을 부트스트랩하는 확장 메서드를 만듭니다. 자세한 내용은 확장 메서드 만들기를 참조 하세요.
  2. .NET MAUI UI 및 모든 종속성이 포함된 .NET MAUI 단일 프로젝트를 만듭니다. 자세한 내용은 .NET MAUI 단일 프로젝트 만들기를 참조 하세요.
  3. 네이티브 앱을 만들고 .NET MAUI 지원을 사용하도록 설정합니다. 자세한 내용은 .NET MAUI 지원 사용을 참조 하세요.
  4. 확장 메서드를 호출하여 .NET MAUI를 UseMauiEmbedding 초기화합니다. 자세한 내용은 .NET MAUI 초기화를 참조 하세요.
  5. .NET MAUI UI를 만들고 확장 메서드를 사용하여 적절한 네이티브 형식으로 ToPlatformEmbedding 변환합니다. 자세한 내용은 .NET MAUI 컨트롤을 사용합니다.

참고 항목

네이티브 포함을 사용하는 경우 .NET MAUI의 데이터 바인딩 엔진은 여전히 작동합니다. 그러나 네이티브 탐색 API를 사용하여 페이지 탐색을 수행해야 합니다.

확장 메서드 만들기

.NET MAUI 컨트롤을 사용하는 네이티브 앱을 만들기 전에 먼저 .NET MAUI 클래스 라이브러리 프로젝트를 만들고 Platforms 폴더와 클래스를 Class1 삭제해야 합니다. 그런 다음, 다음 코드를 포함하는 클래스를 명명 EmbeddedExtensions 된 클래스에 추가합니다.

using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Maui.Platform;

#if ANDROID
using PlatformView = Android.Views.View;
using PlatformWindow = Android.App.Activity;
using PlatformApplication = Android.App.Application;
#elif IOS || MACCATALYST
using PlatformView = UIKit.UIView;
using PlatformWindow = UIKit.UIWindow;
using PlatformApplication = UIKit.IUIApplicationDelegate;
#elif WINDOWS
using PlatformView = Microsoft.UI.Xaml.FrameworkElement;
using PlatformWindow = Microsoft.UI.Xaml.Window;
using PlatformApplication = Microsoft.UI.Xaml.Application;
#endif

namespace Microsoft.Maui.Controls;

public static class EmbeddedExtensions
{
    public static MauiAppBuilder UseMauiEmbedding(this MauiAppBuilder builder, PlatformApplication? platformApplication = null)
    {
#if ANDROID
        platformApplication ??= (Android.App.Application)Android.App.Application.Context;
#elif IOS || MACCATALYST
        platformApplication ??= UIKit.UIApplication.SharedApplication.Delegate;
#elif WINDOWS
        platformApplication ??= Microsoft.UI.Xaml.Application.Current;
#endif

        builder.Services.AddSingleton(platformApplication);
        builder.Services.AddSingleton<EmbeddedPlatformApplication>();
        builder.Services.AddScoped<EmbeddedWindowProvider>();

        // Returning null is acceptable here as the platform window is optional - but we don't know until we resolve it
        builder.Services.AddScoped<PlatformWindow>(svc => svc.GetRequiredService<EmbeddedWindowProvider>().PlatformWindow!);
        builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IMauiInitializeService, EmbeddedInitializeService>());
        builder.ConfigureMauiHandlers(handlers =>
        {
            handlers.AddHandler(typeof(Window), typeof(EmbeddedWindowHandler));
        });

        return builder;
    }

    public static IMauiContext CreateEmbeddedWindowContext(this MauiApp mauiApp, PlatformWindow platformWindow, Window? window = null)
    {
        var windowScope = mauiApp.Services.CreateScope();

#if ANDROID
        var windowContext = new MauiContext(windowScope.ServiceProvider, platformWindow);
#else
        var windowContext = new MauiContext(windowScope.ServiceProvider);
#endif

        window ??= new Window();

        var wndProvider = windowContext.Services.GetRequiredService<EmbeddedWindowProvider>();
        wndProvider.SetWindow(platformWindow, window);
        window.ToHandler(windowContext);

        return windowContext;
    }

    public static PlatformView ToPlatformEmbedded(this IElement element, IMauiContext context)
    {
        var wndProvider = context.Services.GetService<EmbeddedWindowProvider>();
        if (wndProvider is not null && wndProvider.Window is Window wnd && element is VisualElement visual)
            wnd.AddLogicalChild(visual);

        return element.ToPlatform(context);
    }

    private class EmbeddedInitializeService : IMauiInitializeService
    {
        public void Initialize(IServiceProvider services) =>
            services.GetRequiredService<EmbeddedPlatformApplication>();
    }
}

이러한 확장 메서드는 네임스페이 Microsoft.Maui.Controls 스에 있으며 각 플랫폼에서 네이티브 임베디드 앱을 부트스트랩하는 데 사용됩니다. 확장 메서드 참조 EmbeddedPlatformApplicationEmbeddedWindowHandlerEmbeddedWindowProvider .NET MAUI 라이브러리 프로젝트에 추가해야 하는 형식입니다.

다음 코드는 클래스와 EmbeddedPlatformApplication 동일한 .NET MAUI 라이브러리 프로젝트에 추가되어야 하는 클래스를 EmbeddedExtensions 보여 있습니다.

#if ANDROID
using PlatformApplication = Android.App.Application;
#elif IOS || MACCATALYST
using PlatformApplication = UIKit.IUIApplicationDelegate;
#elif WINDOWS
using PlatformApplication = Microsoft.UI.Xaml.Application;
#endif

namespace Microsoft.Maui.Controls;

internal class EmbeddedPlatformApplication : IPlatformApplication
{
    private readonly MauiContext rootContext;
    private readonly IMauiContext applicationContext;

    public IServiceProvider Services { get; }
    public IApplication Application { get; }

    public EmbeddedPlatformApplication(IServiceProvider services)
    {
        IPlatformApplication.Current = this;

#if ANDROID
        var platformApp = services.GetRequiredService<PlatformApplication>();
        rootContext = new MauiContext(services, platformApp);
#else
        rootContext = new MauiContext(services);
#endif

        applicationContext = MakeApplicationScope(rootContext);
        Services = applicationContext.Services;
        Application = Services.GetRequiredService<IApplication>();
    }

    private static IMauiContext MakeApplicationScope(IMauiContext rootContext)
    {
        var scopedContext = new MauiContext(rootContext.Services);
        InitializeScopedServices(scopedContext);
        return scopedContext;
    }

    private static void InitializeScopedServices(IMauiContext scopedContext)
    {
        var scopedServices = scopedContext.Services.GetServices<IMauiInitializeScopedService>();

        foreach (var service in scopedServices)
            service.Initialize(scopedContext.Services);
    }
}

다음 코드는 클래스와 EmbeddedWindowHandler 동일한 .NET MAUI 라이브러리 프로젝트에 추가되어야 하는 클래스를 EmbeddedExtensions 보여 있습니다.

using Microsoft.Maui.Handlers;

#if ANDROID
using PlatformWindow = Android.App.Activity;
#elif IOS || MACCATALYST
using PlatformWindow = UIKit.UIWindow;
#elif WINDOWS
using PlatformWindow = Microsoft.UI.Xaml.Window;
#endif

namespace Microsoft.Maui.Controls;

internal class EmbeddedWindowHandler : ElementHandler<IWindow, PlatformWindow>, IWindowHandler
{
    public static IPropertyMapper<IWindow, IWindowHandler> Mapper =
        new PropertyMapper<IWindow, IWindowHandler>(ElementHandler.ElementMapper)
        {
        };

    public static CommandMapper<IWindow, IWindowHandler> CommandMapper =
        new CommandMapper<IWindow, IWindowHandler>(ElementHandler.ElementCommandMapper)
        {
        };

    public EmbeddedWindowHandler() : base(Mapper)
    {
    }

    protected override PlatformWindow CreatePlatformElement() =>
        MauiContext!.Services.GetRequiredService<PlatformWindow>() ??
        throw new InvalidOperationException("EmbeddedWindowHandler could not locate a platform window.");
}

다음 코드는 클래스와 EmbeddedWindowProvider 동일한 .NET MAUI 라이브러리 프로젝트에 추가되어야 하는 클래스를 EmbeddedExtensions 보여 있습니다.

#if ANDROID
using PlatformWindow = Android.App.Activity;
#elif IOS || MACCATALYST
using PlatformWindow = UIKit.UIWindow;
#elif WINDOWS
using PlatformWindow = Microsoft.UI.Xaml.Window;
#endif

namespace Microsoft.Maui.Controls;

public class EmbeddedWindowProvider
{
    WeakReference<PlatformWindow?>? platformWindow;
    WeakReference<Window?>? window;

    public PlatformWindow? PlatformWindow => Get(platformWindow);
    public Window? Window => Get(window);

    public void SetWindow(PlatformWindow? platformWindow, Window? window)
    {
        this.platformWindow = new WeakReference<PlatformWindow?>(platformWindow);
        this.window = new WeakReference<Window?>(window);
    }

    private static T? Get<T>(WeakReference<T?>? weak) where T : class =>
        weak is not null && weak.TryGetTarget(out var target) ? target : null;
}

.NET MAUI 단일 프로젝트 만들기

.NET MAUI 컨트롤을 사용하는 네이티브 앱을 만들기 전에 이전에 만든 .NET MAUI 클래스 라이브러리 프로젝트와 동일한 솔루션에 .NET MAUI 앱 프로젝트를 추가해야 합니다. .NET MAUI 앱 프로젝트는 네이티브 임베디드 앱에서 다시 사용하려는 UI를 저장합니다. 솔루션에 새 .NET MAUI 앱 프로젝트를 추가한 후 다음 단계를 수행합니다.

  1. 프로젝트에서 속성 폴더를 삭제합니다.

  2. 프로젝트에서 Platforms 폴더를 삭제합니다.

  3. 프로젝트에서 Resources/AppIcon 폴더를 삭제합니다.

  4. 프로젝트에서 Resources/raw 폴더를 삭제합니다.

  5. 프로젝트에서 Resources/Splash 폴더를 삭제합니다.

  6. AppShell 프로젝트에서 클래스를 삭제합니다.

  7. App 속성을 설정하지 않도록 클래스를 수정합니다MainPage.

    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
        }
    }
    
  8. MainPage 프로젝트에서 클래스를 삭제합니다.

  9. 빌드 속성이 $(TargetFrameworks) 설정 net8.0되고 $(OutputType) 빌드 속성이 제거되도록 프로젝트 파일을 수정합니다.

    <PropertyGroup>
      <TargetFrameworks>net8.0</TargetFrameworks>
    
      <RootNamespace>MyMauiApp</RootNamespace>
      <UseMaui>true</UseMaui>
      <SingleProject>true</SingleProject>
      <ImplicitUsings>enable</ImplicitUsings>
      <Nullable>enable</Nullable>
    
      ...
    </PropertyGroup>
    
  10. 메서드 MauiProgramCreateMauiApp 반환되기 전에 호출되는 선택적 Action<MauiAppBuilder> 인수를 허용하게 클래스의 메서드를 수정합니다.

    public static MauiApp CreateMauiApp(Action<MauiAppBuilder>? additional = null)
    {
        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
    
        additional?.Invoke(builder);
        return builder.Build();
    }
    

이 시점에서 종속성 및 리소스를 포함하여 필요한 .NET MAUI UI를 프로젝트에 추가하고 프로젝트가 올바르게 빌드되는지 확인해야 합니다.

.NET MAUI 지원 사용

.NET Android, .NET iOS, .NET Mac Catalyst 또는 WinUI 앱에서 Element 파생된 .NET MAUI 컨트롤을 사용하려면 네이티브 앱 프로젝트를 이전에 만든 .NET MAUI 클래스 라이브러리 프로젝트와 동일한 솔루션에 추가해야 합니다. 그런 다음 프로젝트 파일의 첫 번째 <PropertyGroup> 노드에서 속성을 true 설정하고 빌드하여 네이티브 앱의 프로젝트 파일에서 .NET MAUI 지원을 사용하도록 설정 $(UseMaui)$(MauiEnablePlatformUsings) 해야 합니다.

<PropertyGroup>
    ...
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>

    <UseMaui>true</UseMaui>
    <MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>  
</PropertyGroup>

.NET Mac Catalyst 앱의 경우 빌드 속성을 최소 14.0으로 설정 $(SupportedOSPlatformVersion) 해야 합니다.

<PropertyGroup>
    ...
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>

    <SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>
    <UseMaui>true</UseMaui>
    <MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>  
</PropertyGroup>

WinUI 앱의 경우 빌드 속성을 false다음으로 설정 $(EnableDefaultXamlItems) 해야 합니다.

<PropertyGroup>
    ...
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>

    <UseMaui>true</UseMaui>
    <MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>    
    <EnableDefaultXamlItems>false</EnableDefaultXamlItems>
</PropertyGroup>

이렇게 하면 이미 정의되고 있는 메서드에 대한 InitializeComponent 빌드 오류가 수신되지 않습니다.

그런 다음, 프로젝트 파일 및 Microsoft.Maui.Controls.Compatiblity NuGet 패키지에 Microsoft.Maui.Controls 대한 빌드 항목을 추가 $(PackageReference) 합니다.

<ItemGroup>
    <PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
    <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
</ItemGroup>

.NET MAUI 초기화

네이티브 앱 프로젝트가 .NET MAUI 컨트롤을 생성하려면 먼저 .NET MAUI를 초기화해야 합니다. 초기화 시기는 주로 앱 흐름에서 가장 편리한 시기에 따라 달라집니다. 시작 시 또는 .NET MAUI 컨트롤이 생성되기 직전에 수행할 수 있습니다. 여기서 설명하는 방법은 앱의 초기 UI를 만들 때 .NET MAUI를 초기화하는 것입니다.

일반적으로 네이티브 앱 프로젝트에서 .NET MAUI를 초기화하는 패턴은 다음과 같습니다.

Android에서 클래스의 재정의 OnCreateMainActivity 는 일반적으로 앱 시작 관련 작업을 수행하는 위치입니다. 다음 코드 예제에서는 클래스에서 초기화되는 .NET MAUI를 MainActivity 보여줍니다.

namespace MyNativeEmbeddedApp.Droid
{
    [Activity(Label = "@string/app_name", MainLauncher = true, Theme = "@style/AppTheme")]
    public class MainActivity : Activity
    {
        public static readonly Lazy<MauiApp> MauiApp = new(() =>
        {
            var mauiApp = MauiProgram.CreateMauiApp(builder =>
            {
                builder.UseMauiEmbedding();
            });
            return mauiApp;
        });

        public static bool UseWindowContext = true;

        protected override void OnCreate(Bundle? savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Ensure .NET MAUI app is built before creating .NET MAUI views
            var mauiApp = MainActivity.MauiApp.Value;

            // Create .NET MAUI context
            var mauiContext = UseWindowContext
                ? mauiApp.CreateEmbeddedWindowContext(this) // Create window context
                : new MauiContext(mauiApp.Services, this);  // Create app context

            ...              
        }
    }
}

iOS 및 Mac Catalyst에서 클래스의 재정의 FinishedLaunchingAppDelegate 를 수정하여 기본 보기 컨트롤러를 만들어야 합니다.

namespace MyNativeEmbeddedApp.iOS
{
    [Register("AppDelegate")]
    public class AppDelegate : UIApplicationDelegate
    {
        public override UIWindow? Window { get; set; }

        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
        {
            Window = new UIWindow(UIScreen.MainScreen.Bounds);
            var vc = new MainViewController();
            Window.RootViewController = vc;
            Window.MakeKeyAndVisible();
            return true;
        }
    }
}

그런 다음 기본 뷰 컨트롤러의 ViewDidLoad 메서드에서 .NET MAUI를 초기화할 수 있습니다.

using Microsoft.Maui.Platform;

namespace MyNativeEmbeddedApp.iOS
{
    public class MainViewController : UIViewController
    {
        UIWindow GetWindow() =>
            View?.Window ??
            ParentViewController?.View?.Window ??
            MainViewController.MauiApp.Value.Services.GetRequiredService<IUIApplicationDelegate>().GetWindow() ??
            UIApplication.SharedApplication.Delegate.GetWindow();

        public static readonly Lazy<MauiApp> MauiApp = new(() =>
        {
            var mauiApp = MauiProgram.CreateMauiApp(builder =>
            {
                builder.UseMauiEmbedding();
            });
            return mauiApp;
        });

        public static bool UseWindowContext = true;

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Ensure app is built before creating .NET MAUI views
            var mauiApp = MainViewController.MauiApp.Value;

            // Create .NET MAUI context
            var mauiContext = UseWindowContext
                ? mauiApp.CreateEmbeddedWindowContext(GetWindow()) // Create window context
                : new MauiContext(mauiApp.Services);               // Create app context

            ...
        }
    }
}

Windows에서 MainWindow 클래스는 일반적으로 UI 관련 앱 시작 작업을 수행할 수 있는 위치입니다.

namespace MyNativeEmbeddedApp.WinUI
{
    public sealed partial class MainWindow : Microsoft.UI.Xaml.Window
    {
        public static readonly Lazy<MauiApp> MauiApp = new(() =>
        {
            var mauiApp = MauiProgram.CreateMauiApp(builder =>
            {
                builder.UseMauiEmbedding();
            });
            return mauiApp;
        });

        public static bool UseWindowContext = true;

        public MainWindow()
        {
            this.InitializeComponent();

            // Ensure .NET MAUI app is built before creating .NET MAUI views
            var mauiApp = MainWindow.MauiApp.Value;

            // Create .NET MAUI context
            var mauiContext = UseWindowContext
                ? mauiApp.CreateEmbeddedWindowContext(this) // Create window context
                : new MauiContext(mauiApp.Services);        // Create app context

            ...
        }
    }
}

이 예제에서는 MauiApp 지연 초기화를 사용하여 개체를 만듭니다. UseMauiEmbedding 확장 메서드는 개체에서 MauiAppBuilder 호출됩니다. 따라서 네이티브 앱 프로젝트에는 이 확장 메서드를 포함하는 만든 .NET MAUI 클래스 라이브러리 프로젝트에 대한 참조가 포함되어야 합니다. MauiContext 그런 다음 개체에서 컨텍스트의 MauiApp 범위를 결정하는 개체 bool 를 만듭니다. 개체는 MauiContext .NET MAUI 컨트롤을 네이티브 형식으로 변환할 때 사용됩니다.

.NET MAUI 컨트롤 사용

네이티브 앱에서 .NET MAUI가 초기화된 후 네이티브 앱의 레이아웃에 .NET MAUI UI를 추가할 수 있습니다. 이 작업은 UI의 인스턴스를 만들고 확장 메서드를 사용하여 적절한 네이티브 형식으로 ToPlatformEmbedded 변환하여 수행할 수 있습니다.

Android에서 확장 메서드는 ToPlatformEmbedded .NET MAUI 컨트롤을 Android View 개체로 변환합니다.

var mauiView = new MyMauiContent();
Android.Views.View nativeView = mauiView.ToPlatformEmbedded(mauiContext);

이 예제에서는 ContentView-derived 개체가 Android View 개체로 변환됩니다.

참고 항목

ToPlatformEmbedded 확장 메서드는 이전에 만든 .NET MAUI 클래스 라이브러리에 있습니다. 따라서 네이티브 앱 프로젝트에는 해당 프로젝트에 대한 참조가 포함되어야 합니다.

그러면 네이 View 티브 앱의 레이아웃에 개체를 추가할 수 있습니다.

rootLayout.AddView(nativeView, new LinearLayout.LayoutParams(MatchParent, WrapContent));

iOS 및 Mac Catalyst에서 ToPlatformEmbedded 확장 메서드는 .NET MAUI 컨트롤을 개체로 UIView 변환합니다.

var mauiView = new MyMauiContent();
UIView nativeView = mauiView.ToPlatformEmbedded(mauiContext);

이 예제에서는 ContentView-derived 개체가 개체로 UIView 변환됩니다.

참고 항목

ToPlatformEmbedded 확장 메서드는 이전에 만든 .NET MAUI 클래스 라이브러리에 있습니다. 따라서 네이티브 앱 프로젝트에는 해당 프로젝트에 대한 참조가 포함되어야 합니다.

UIView 그런 다음 개체를 뷰 컨트롤러의 보기에 추가할 수 있습니다.

stackView.AddArrangedSubView(nativeView);

Warning

현재 iOS 및 Mac Catalyst에서 .NET MAUI UI와 상호 작용할 수 없습니다. 자세한 내용은 GitHub 문제 #19340을 참조 하세요.

또한 ToUIViewController .NET MAUI의 확장 메서드를 사용하여 .NET MAUI 페이지를 다음으로 변환할 UIViewController수 있습니다.

MyMauiPage myMauiPage = new MyMauiPage();
UIViewController myPageController = myMauiPage.ToUIViewController(mauiContext);

이 예제에서는 ContentPage-derived 개체가 .로 변환 UIViewController됩니다.

Windows에서 확장 메서드는 ToPlatformEmbedded .NET MAUI 컨트롤을 개체로 FrameworkElement 변환합니다.

var mauiView = new MyMauiContent();
FrameworkElement nativeView = myMauiPage.ToPlatformEmbedded(mauiContext);

이 예제에서는 ContentView-derived 개체가 개체로 FrameworkElement 변환됩니다. 그런 다음 개체를 FrameworkElement WinUI 페이지의 콘텐츠로 설정할 수 있습니다.

그러면 네이 FrameworkElement 티브 앱의 레이아웃에 개체를 추가할 수 있습니다.

stackPanel.Children.Add(nativeView);

Important

오류가 발생하지 않도록 하려면 디버그 구성에서 네이티브 포함된 앱을 실행하기 전에 XAML 핫 다시 로드를 사용하지 않도록 설정해야 합니다.