.NET MAUI - Xaml Compilation (for HighPerformance)

2022. 8. 22. 00:00MAUI

반응형

지금 까지 Xaml 을 작업하면서 약간의 불편을 느꼈을 것이다. 

못느꼈을수도 있다. (😂) 

어쨌는 Xaml 을 사용하여 작업하는 것은 많은 이점이 있고 편리함을 주지만

MVVM 형태로 작업할 때 ViewModel 을 통해 연결 하게 되면 몇가지 문제점을 발견하게 된다. 

 

  • 첫번째 ViewModel 에 존재하지 않는 property 나 command 를 컴파일 타임에 발견하지 못한다. 
  • 두번째 복잡한 UI 를 개발 하게 되면 Xaml 에 속도가 만족할 만큼 나오지 않는다고 생각할 수 있다.

 

위와 같은 두가 지 문제를 해결해 보자

 

일단 지금 첫번째 문제인 컴파일 타임에 Xaml 의 Binding 관련 오류를 발견해보자

생각보다 아주 간단하게 처리 할 수 있다. 

x:DataType="viewmodel" 

위의 한줄로 처리 가능 하다.

 

이전 강좌에서 사용했던 IncrementCounterPage.xaml 과 IncrementCounterViewModel.cs 를 확인해 보자

IncrementCounterPage.xaml 

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiApp1.MVVM.AutoScan.IncrementCounterPage"
             xmlns:local="clr-namespace:MauiApp1.MVVM.AutoScan"
             x:DataType="local:IncrementCounterViewModel"
             Title="IncrementCounterPage">
    <VerticalStackLayout>
        <Label Text="{Binding Counter, Mode=OneWay}"/>
        <Button
            Text="Click me!"
            Command="{Binding IncrementCounterCommand}"/>

        <Label Text="{Binding Message, Mode=OneWay}"/>
        <Label Text="{Binding Message2, Mode=OneWay}"/>
        <Button
            Text="Change message"
            Command="{Binding ChangeMessageCommand}"/>
    </VerticalStackLayout>
</ContentPage>

x:DataType="local:IncrementCounterViewModel"

위를 이용하여 처리 가능 하다

 

IncrementCounterViewModel.cs 

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MauiApp1.InjectableServices;

namespace MauiApp1.MVVM.AutoScan;
public partial class IncrementCounterViewModel : ObservableObject, ITransientService
{
    public IncrementCounterViewModel(IHelloWorldClass helloWorld)
    {
        _helloWorld = helloWorld;
    }

    [ObservableProperty]
    private int _counter;

    [ObservableProperty]
    private string _message;

    private readonly IHelloWorldClass _helloWorld;

    [RelayCommand]
    private void IncrementCounter() => Counter++;

    [RelayCommand]
    private void ChangeMessage()
    {
        Message = _helloWorld.Execute();
    }
}

IncrementCounterPage.xaml.cs

using MauiApp1.InjectableServices;

namespace MauiApp1.MVVM.AutoScan;

public partial class IncrementCounterPage : ContentPage, ITransientService
{
	public IncrementCounterPage(IncrementCounterViewModel incrementCounterViewModel)
	{
		InitializeComponent();
		BindingContext = incrementCounterViewModel;
	}
}

 

이렇게 하고 컴파일 하게 되면 Message2 를  찾을 수 없다는 오류가 나오게 된다. 

x:DataType 을 xaml 에서 제거하면 오류는 나오지 않는다. 

하지만 실제 runtime 시에  label 에는 아무 표시도 되지 않는다. 이러한 문제를 해결하기 위해서 x:DataType 을 활용하자

 

그리고 두번째 문제인 performance 을 처리 해보자

 

이문제는 IncrementCounterPage.xaml.cs 의  class 정의 상단에 다음과 같은 문구를 넣어 처리 가능 하다.

[XamlCompilation(XamlCompilationOptions.Compile)]

using MauiApp1.InjectableServices;

namespace MauiApp1.MVVM.AutoScan;
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class IncrementCounterPage : ContentPage, ITransientService
{
	public IncrementCounterPage(IncrementCounterViewModel incrementCounterViewModel)
	{
		InitializeComponent();
		BindingContext = incrementCounterViewModel;
	}
}

만약 전체 Xaml 에 대 적용하고 싶다면 assembly 수준에서 정의하면 된다. 

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

App.xaml.cs 파일로 이동하여 다음을 namespace 상단에 정의하자

이와 같이 정의 하면 같은 assembly 안에 있는 모든 Xaml page 는 Compile 과정을 거쳐 처리된다. 

 

그런데 Roslyn 같은 runtime code generator 가 가능한 기술을 사용하여 ViewModel 을 생성하는 경우도 생각해 볼수 있다. 

이런 경우에는 viewmodel 을 동적으로 생성하기 때문에 compile time 에 error 를 검출해서는 안된다. 

그럴경우를 위해 특정 page 만 skip 하도록 option 을 줄수 있다. 

XamlCompilationOptions.Skip 이 바로 그 작업을 수행한다. 

이러한 내용을 통해 assembly 또는 page 수준에서 XamlCompilationOption 을 설정하여 performace 를 높일 수 있다. 

 

 

 

 

관련영상

https://youtu.be/ou3-vK5yjxk

 

 

반응형