티스토리 뷰

반응형
이 포스트의 내용은 Summary of Chapter 10. XAML markup extensions (https://docs.microsoft.com/en-us/xamarin/xamarin-forms/creating-mobile-apps-xamarin-forms/summaries/chapter10) 을 번역한 자료입니다. 한글로 표현하기 어색하거나 모호한 문장은 임의로 조정하거나 삭제하였으므로 필요한 경우 원문을 참고하시기 바랍니다. 또한 언제든지 원저자의 요청에 의해 게시물이 내려지거나 수정될 수 있음을 알려드립니다. 마지막으로 이 글은 개인적인 학습목적으로 번역한 자료이며, 따라서 오역이나 잘못된 해석이 포함되어 있을 수 있으며 이로인한 어떠한 일체의 보장을 하지 않습니다.

이 문서에서는 다음 주제를 다룹니다.

XAML 파서는 Converter를 사용하여 XAML Attribute의 문자열을 지정된 Type 인스턴스로 변경합니다. .NET 기본 데이터 유형(int, double 등)에 대해서는 TryParse와 같은 표준 Converter를 사용합니다. 그 외의 경우에는 Property나 Class에 수식된 TypeConverter 파생 Class와 TypeConverterAttribute를 통해 XAML 문자열을 지정된 Type 인스턴스로 변환합니다.

때로는 변환과정에 다른 처리를 추가할 수 있다면 편리할 때가 있습니다. 예를 들어 값을 Dictionary로부터 조회하거나, Static Property나 Field에서 값을 조회하거나, 변환 중에 특별한 처리가 필요할 수 있습니다.

이 것이 바로 XAML Markup Extension의 역할입니다. Markup Extension이라는 이름에도 불구하고 XML을 확장하는 것이 아닙니다. XAML은 항상 순수한 XML로 구성되며 XAML Markup Extension은 XAML 문자열을 변환하는 C# 컨버팅을 확장하는 것입니다.

코드 인프라

XAML Markup Extension은 IMarkupExtension interface를 구현한 Class를 말합니다. 이러한 Class들은 이름에 Extension이라는 접미사를 포함하지만 XAML에서는 Extension 접미사 없이 사용할 수 있습니다.

아래는 모든 XAML 구현에서 지원되는 XAML Markup Extension으로 Xamarin.Forms.Xaml 어셈블리에 정의되어 있습니다.

아래 4개의 XAML Markup Extension은 Xamarin.Fomrs를 포함한 수많은 XAML 구현에서 지원됩니다. Windows Presentation Foundation(WPF)로부터 시작되었으며 Silverlight, Windows Phone 7, 8, Windows 8, 10 등 모든 Microsoft XAML 구현에서 지원됩니다.

다음은 RelativeLayout과 함께 사용하기 위해 Xamarin.Forms에 추가된 XAML Markup Extension 입니다.

Static Member에 접근

x:Static은 Static Propery, Field, Enum, 상수(const) 값을 XAML에서 사용할 수 있게 해줍니다. Member Property에는 조회할 Static 값의 위치를 지정해주세요.

<Label Text="Just some text">
    <Label.TextColor>
        <x:StaticExtension Member="Color.Black" />
    </Label.TextColor>
    <Label.FontAttributes>
        <x:StaticExtension Member="FontAttributes.Italic" />
    </Label.FontAttributes>
</Label>

중괄호({, })를 사용하면 보다 간결하게 표현할 수 있습니다. Member Property 표기는 생략할 수 있습니다.

<Label Text="Just some text"
    BackgroundColor="{x:Static Member=Color.Accent}"
    TextColor="{x:Static Color.Black}" />

SharedStatics 샘플은 x:Static 사용예제를 제공합니다. Static 값은 AppConstants Class에 정의되어 있습니다. 이 예제는 앱의 공통상수를 정의한 후 앱 전체에서 사용하는 방법을 소개합니다.

XML namespace를 추가하면 .NET Framework에 정의된 Static Property, Field, Enum 값을 사용할 수 있습니다. 이에 관해서는 SystemStatics 샘플에서 다룹니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        x:Class="SystemStatics.SystemStaticsPage">

        <!--System.Math.PI을 사용 -->
        <Button Text="&#x03C0; BorderWidth"
                BorderWidth="{x:Static sys:Math.PI}" />

        <!--System.Environment.NewLine을 사용 -->
        <Label>
            <Label.FormattedText>
                <FormattedString>
                    <Span Text="3줄의 텍스트" />
                    <Span Text="{x:Static sys:Environment.NewLine}" />
                    <Span Text="Environment.NewLine를" />
                    <Span Text="{x:Static sys:Environment.NewLine}" />
                    <Span Text="사용하여 구분" />
                </FormattedString>
            </Label.FormattedText>
        </Label>
    </StackLayout>
</ContentPage>

중괄호({, })는 XAML에서 특별한 목적으로 사용되기 때문에 일반적인 방법으로 표현할 수 없습니다.

<!-- 중괄호({, })는 XAML에서 특별한 문자이므로 아래는 오류발생 -->
<Label Text="{중괄호 내부 텍스트}" />

중괄호 문자를 표현하려면 탈출문자열 {}을 사용해야 합니다.

<!-- 중괄호({, })를 사용하려면 탈출문자열 {}를 사용할 것-->
<Label Text="{}{중괄호 내부 텍스트}" />

Resource Dictionary

VisualElement Class는 ResourceDictionary 객체를 사용할 수 있는 Resource Property를 제공합니다. XAML에서는 x:Key와 함께 값을 Dictionary에 추가할 수 있습니다. ResourceDictionary에 저장된 값은 하위 Element에서 참조할 수 있습니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="ResourceSharing.ResourceSharingPage">
    <!-- VisualElement에서 제공하는 Resource에 값을 추가 -->
    <ContentPage.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions">Center</LayoutOptions>
            <LayoutOptions x:Key="vertOptions" Alignment="Center" Expands="True" />
            <x:Double x:Key="borderWidth">3</x:Double>
            <Color x:Key="textColor">Red</Color>
        </ContentPage.Resources>
    </ResourceDictionary>

    <!-- ContentPage 하위 Element들은 ResourceDictionary 값 사용가능 -->

</ContentPage>

일반적인 StaticResource 사용

대부분의 경우 StaticResource Markup Extension은 ResourceDictionary에 추가된 값을 참조하기 위해 사용됩니다. 이에 관해서는 ResourceSharing 샘플에서 예제를 제공합니다. 값을 참조하기 위해서 StaticResourceExtension Element나 StaticResource를 중괄호({, })와 함께 사용합니다.

<StackLayout>
    <!-- ResourceDictionary 정의 -->
    <StackLayout.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions">Center</LayoutOptions>
            <LayoutOptions x:Key="vertOptions" Alignment="Center" Expands="True" />
            <x:Double x:Key="borderWidth">3</x:Double>
            <Color x:Key="textColor">Red</Color>
            <OnPlatform x:Key="backgroundColor"
                x:TypeArguments="Color"
                Android="#404040" />
            <OnPlatform x:Key="borderColor"
                x:TypeArguments="Color"
                Android="White"
                WinPhone="Black" />
            <x:String x:Key="fontSize">Large</x:String>
        </ResourceDictionary>
    </StackLayout.Resources>

    <!-- StaticResourceExtension Element를 통한 접근 -->
    <Button Text=" Carpe diem ">
        <Button.HorizontalOptions>
            <StaticResourceExtension Key="horzOptions" />
        </Button.HorizontalOptions>
        <Button.VerticalOptions>
            <StaticResourceExtension Key="vertOptions" />
        </Button.VerticalOptions>
        <Button.BorderWidth>
            <StaticResourceExtension Key="borderWidth" />
        </Button.BorderWidth>
        <Button.TextColor>
            <StaticResourceExtension Key="textColor" />
        </Button.TextColor>
        <Button.BackgroundColor>
            <StaticResourceExtension Key="backgroundColor" />
        </Button.BackgroundColor>
        <Button.BorderColor>
            <StaticResourceExtension Key="borderColor" />
        </Button.BorderColor>
        <Button.FontSize>
            <StaticResourceExtension Key="fontSize" />
        </Button.FontSize>
    </Button>

    <!-- StaticResource를 중괄호와 사용하는 방법 -->
    <Button Text=" Sapere aude "
        HorizontalOptions="{StaticResource Key=horzOptions}"
        VerticalOptions="{StaticResource Key=vertOptions}"
        BorderWidth="{StaticResource Key=borderWidth}"
        TextColor="{StaticResource Key=textColor}"
        BackgroundColor="{StaticResource Key=backgroundColor}"
        BorderColor="{StaticResource Key=borderColor}"
        FontSize="{StaticResource Key=fontSize}" />

    <!-- StaticResource를 중괄호와 사용하는 방법2 (Key 생략가능) -->
    <Button Text=" Discere faciendo "
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="{StaticResource borderWidth}"
        TextColor="{StaticResource textColor}"
        BackgroundColor="{StaticResource backgroundColor}"
        BorderColor="{StaticResource borderColor}"
        FontSize="{StaticResource fontSize}" />
</StackLayout>

ResourceDictionary 사용
x:StaticStaticResource는 전혀 다른 Markup Extension이므로 주의해주세요.

Dictionary Tree

XAML 파서가 StaticResource를 만나면 XAML Tree를 거슬러 올라가며 ResourceDictionary찾아 Key에 해당하는 값을 찾습니다. 값을 찾지 못할 경우 최종적으로 App Class를 검색하고 값이 없으면 오류를 발생시킵니다. Visual Tree 상으로 하위의 ResourceDictionary와 부모의 ResourceDictionary에 동일한 Key 값이 존재하면 하위에 존재하는 값이 사용됩니다. 이러한 예제는 ResourceTrees샘플에 나타납니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="ResourceSharing.ResourceSharingPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Color x:Key="textColor">Red</Color>
            <Color x:Key="backColor">#404040</Color>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout>
        <StackLayout.Resources>
            <ResourceDictionary>
                <Color x:Key="textColor">Blue</Color>
            </ResourceDictionary>
        </StackLayout.Resources>

        <!-- 
            부모와 자식 ResourceDictionary에
            동일한 Key가 포함될 경우 자식이 부모를 Override함
            (버튼은 파란 텍스트에 회색 배경을 가짐)
         -->
        <Button Text="ResourceDictionary Tree"
            TextColor="{StaticResource textColor}" 
            BackgroundColor="{StaticResource backColor}" />
    </StackLayout>
</ContentPage>

DynamicResource

StaticResource Markup Extension은 InitializeComponemt가 호출될 때 Visual Tree 상의 ResourceDictionary를 검색하며 처음 Key가 일치하는 값을 사용합니다. StaticResource 대신 DynamicResource를 사용할 수도 있습니다. DynamicResourceStaticResource과 동일하게 값을 검색하고 사용합니다. 하지만 DynamicResourceResourceDictionary와 연결를 지속적으로 유지하고 ResourceDictionary의 값이 변경될 때 마다 반영합니다. 반대로 StaticResource는 처음 한번만 값을 검색하고 반영합니다.

DynamicVsStatic 샘플은 StaticResourceDynamicResource 간의 차이를 보여줍니다.

DynamicResource를 사용하는 Property는 반드시 Bindable Property에 의해 구현되어야 하며 이에 관해서는 Chapter 11, The bindable infrastructure에서 소개합니다.

덜 사용되는 Markup Extension

x:Null Markup Extension은 null값을 할당합니다.

x:Type Markup Extension은 .NET의 Type 값을 할당합니다.

x:Array Markup Extension은 배열을 정의합니다. 배열 값의 유형은 Type Property에 x:Type Markup Extension을 사용하여 지정합니다.

<SomeElement SomeProperty="{x:Null}" />

<AnotherElement TypeProperty="{x:Type Color}" />

<x:Array x:Key="array" Type="{x:Type x:String}">
    <x:String>One String</x:String>
    <x:String>Two String</x:String>
    <x:String>Red String</x:String>
    <x:String>Blue String</x:String>
</x:Array>

Custom Markup Extension

IMarkupExtension interface와 ProvideValue 메서드를 구현함으로써 XAML Markup Extension을 직접 제작할 수 있습니다.

public class HslColorExtension : IMarkupExtension
{
    public HslColorExtension() { A = 1; }

    public double H { set; get; }
    public double S { set; get; }
    public double L { set; get; }
    public double A { set; get; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return Color.FromHsla(H, S, L, A);
    }
}

HslColorExtension Class는 IMarkupExtension를 구현한 Custom Markup Extension입니다. HslColorExtension Class는 H, S, L, A Property 값을 설정하면 Color 값을 반환합니다. 이 Class는 이 책에 전반애 걸쳐 다루는 Xamarin.FormsBook.Toolkit이라는 Xamarin.Forms 라이브러리의 첫번째 항목입니다.

CustomExtensionDemo 샘플은 이 라이브러리를 레퍼런스에 추가하고 이 Custom Mrkup Extension을 사용하는 방법을 보여줍니다.


댓글