티스토리 뷰

Xamarin.Forms Book/요약

8장 요약. 코드와 XAML의 조화

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

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

매개변수 전달

일반적으로 XAML을 통해 인스턴스화 되는 Class들은 반드시 매개변수 없는 생성자를 가져야 합니다. XAML을 통해 생성된 객체들은 Property를 통해 초기화 하게 됩니다. 여기에서는 XAML에서 객체를 초기화하는 방법 2가지를 더 소개합니다.

이 방법은 범용 기술임에도 불구하고 대부분 MVVM View Model과 함께 사용됩니다.

매개변수 있는 생성자

ParameteredConstructorDemo 샘플은 생성자 매개변수를 할당하기 위해 x:Arguments 태그를 사용하는 방법을 보여주고 있습니다. 이 매개변수들은 반드시 매개변수의 타입을 알려주는 태그에 의해 한정되어야 합니다.

<BoxView>
    <BoxView.Color>
        <Color>
            <!-- x:Arguments를 통한 생성자 매개변수 전달 -->
            <x:Arguments>
                <x:Double>0</x:Double>
                <x:Double>0</x:Double>
                <x:Double>1</x:Double>
                <x:Double>0.5</x:Double>
            </x:Arguments>
        </Color>
    </BoxView.Color>
</BoxView>

생성자 매개변수의 타입이 .NET 기본 데이터 유형일 때는 아래 태그를 사용할 수 있습니다.

  • x:Object
  • x:Boolean
  • x:Byte
  • x:Int16
  • x:Int32
  • x:Int64
  • x:Single
  • x:Double
  • x:Decimal
  • x:Char
  • x:String
  • x:TimeSpan
  • x:Array
  • x:DateTime

XAML에서 메서드 호출

FactoryMethodDemo 샘플은 오브젝트를 생성하는 팩토리 메서드를 호출하기 위해 x:FactoryMethod Element를 사용하는 방법을 보여주고 있습니다.

<BoxView>
    <BoxView.Color>
        <!-- x:FactoryMethod를 통해 메서드 호출 -->
        <Color x:FactoryMethod="FromRgb">
            <x:Arguments>
                <x:Double>0</x:Double>
                <x:Double>1.0</x:Double>
                <x:Double>0</x:Double>
            </x:Arguments>
        </Color>
    </BoxView.Color>
</BoxView>

XAML에서 호출하는 팩토리 메서드는 반드시 public static 메서드여야 하고 적합한 Type 값을 반환해야 합니다. (예를들어 위의 코드에서 x:FactoryMethod의 부모 Element는 BoxView.Color이며 Color Type을 받습니다. 때문에 Color를 반환하는 public static 메서드인 Color.FromRgb를 호출하고 있습니다.) 팩토리 메서드의 매개변수는 x:Arguments 태그를 통해 정의할 수 있습니다.

X:Name Attribute

XAML에서 생성되는 Element들은 x:Name Attribute를 통해 이름을 가질 수 있습니다. 이 이름은 C# 작명규칙과 동일한 규칙을 따릅니다.

<StackLayout>
    <Label x:Name="timeLabel" />
    <Label x:Name="dateLabel" />
</StackLayout>

x:Name Attribute를 통해 정의된 이름은 Code-behind에서 XAML Element에 접근하기 위해 사용됩니다. Code-behind의 생성자에서 InitializeComponent를 호출한 이후로 C# 코드에서 이름을 통해 XAML에 접근할 수 있습니다.

public XamlClockPage()
{
    InitializeComponent();

    Device.StartTimer(TimeSpan.FromSeconds(1), () =>
    {
        //C#에서 XAML에 정의된 Label에 접근할 수 있음
        DateTime dt = DateTime.Now;
        timeLabel.Text = dt.ToString("T");
        dateLabel.Text = dt.ToString("D");
        return true;
    });
}

XAML 파서는 XAML Element의 이름을 C#의 private field로 변환하기 때문에 C#에서 접근할 수 있게 되는 것입니다.

//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace XamlClock {
    using System;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;

    public partial class XamlClockPage : global::Xamarin.Forms.ContentPage {
        [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG",
                                                         "0.0.0.0")]
        private global::Xamarin.Forms.Label timeLabel;
    
        [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG",
                                                        "0.0.0.0")]
        private global::Xamarin.Forms.Label dateLabel;

        [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG",
                                                         "0.0.0.0")]
        private void InitializeComponent() {
            this.LoadFromXaml(typeof(XamlClockPage));
            timeLabel = this.FindByName<global::Xamarin.Forms.Label>("timeLabel");
            dateLabel = this.FindByName<global::Xamarin.Forms.Label>("dateLabel");
        }
    }
}

XamlClock 샘플은 Code-behind 파일에서 XAML에 정의된 2개의 Label에 접근하고 시간을 업데이트하기 위해 x:Name을 사용하는 모습을 보여주고 있습니다.

XAML 페이지에서 동일한 이름이 반복되어 사용될 수 없습니다. 이러한 제약은 아래와 같이 OnPlatform을 통해 코드를 작성할 때 문제가 될 수 있습니다.

<!-- XAML에서 x:Name은 중복될 수 없기 때문에 아래 코드는 오류를 발생시킨다 -->
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="PlatformSpecificLabels.PlatformSpecificLabelsPage">
    <OnPlatform x:TypeArguments="View">
        <OnPlatform.iOS>
            <Label x:Name="deviceLabel" Text="This is an iOS device" />
        </OnPlatform.iOS>
        <OnPlatform.Android>
            <Label x:Name="deviceLabel" Text="This is an Android device" />
        </OnPlatform.Android>
        <OnPlatform.WinPhone>
            <Label x:Name="deviceLabel" Text="This is an Windows device" />
        </OnPlatform.WinPhone>
    </OnPlatform>
</ContentPage>

PlatformSpecificLabele 샘플은 이러한 이름 중복 문제를 해결하는 방법을 보여줍니다.

<!-- x:Name 중복문제를 해결하는 다양한 방법이 존재한다 -->
<Label x:Name="deviceLabel">
    <OnPlatform x:TypeArguments="x:String"
        iOS="This is an iOS device"
        Android="This is an Android device"
        WinPhone="This is a Windows device" />
</Label>

XAML 기반 Custom View

XAML에서 코드의 반복을 피할 수 있는 방법이 몇가지 있습니다. 그 방법 중 하나는 ContentView를 통해 XAML View를 생성하는 것입니다. ColorViewList 샘플은 이러한 방법을 소개하고 있습니다. ColorViewContentView를 상속하고 특정한 색상과 색상명을 출력하는 Class입니다.

<!-- 반복되는 XAML코드는 ContentView로 분리한 후 재사용할 수 있다 -->
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="ColorViewList.ColorView">
    <Frame OutlineColor="Accent">
        <StackLayout Orientation="Horizontal">
            <BoxView x:Name="boxView" WidthRequest="70" HeightRequest="70" />
                <StackLayout>
                    <Label x:Name="colorNameLabel" />
                    <Label x:Name="colorValueLabel" />
                </StackLayout>
            </BoxView>
        </StackLayout>
    </Frame>
</ContentView>

ColorViewListPageContentPage를 상속한 페이지로서 17개의 ColorView 인스턴스를 생성하고 출력하고 있습니다.

ColorViewListPageXAML에서 ColorView View를 사용하려면 XML namespace 선언이 필요합니다. 일반적으로 동일한 어셈블리에 위치할 경우 namespace 명을 local로 정하는 것이 관례입니다. (물론 local아닌 이름을 사용할 수 있습니다.) XML namespace의 값에는 clr-namespace:와 해당 namespace 경로를 기술합니다.

<!-- 
    생성한 ContentView를 사용하려면 xmln namespace를 선언해야 한다.
    XAML에서 XML namespace는 C#의 namespace과 동일한 역할을 한다.
    1. namespace 선언은 xmlns:local="..." 형태로 한다.
    2. namespace에 속한 Class를 XAML에서 사용하려면 "<namespace명:Class명 ..."으로 시작해야한다.
-->
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:ColorViewList"
        x:Class="ColorViewList.ColorViewListPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    <ScrollView>
        <StackLayout Padding="6, 0">
            <local:ColorView ColorName="Aqua" />
            <local:ColorView ColorName="Black" />
            <local:ColorView ColorName="Blue" />
            <local:ColorView ColorName="Fuchsia" />
            <local:ColorView ColorName="Gray" />
            <local:ColorView ColorName="Green" />
            <local:ColorView ColorName="Lime" />
            <local:ColorView ColorName="Maroon" />
            <local:ColorView ColorName="Navy" />
            <local:ColorView ColorName="Olive" />
            <local:ColorView ColorName="Purple" />
            <local:ColorView ColorName="Pink" />
            <local:ColorView ColorName="Red" />
            <local:ColorView ColorName="Silver" />
            <local:ColorView ColorName="Teal" />
            <local:ColorView ColorName="White" />
            <local:ColorView ColorName="Yellow" />
        </StackLayout>
    </ScrollView>
</ContentPage>

이벤트와 핸들러

XAML에서 이벤트를 할당할 수 있지만 이벤트의 구현은 Code-Behind에서 이루어져야 합니다. XamlKeypad 샘플은 키패드 UI를 XAML에서 구현하고 Clicked 핸들러를 Code-behind에서 구현하는 방법을 소개합니다.

탭 제스처

View는 사용자 터치 입력을 받아 들이고 이벤트를 발생시킬 수 있습니다. 따라서 View를 상속한 모든 Class(Label 등)은 사용자의 터치에 상호작용할 수 있습니다. ViewGestureRecognizers Collection Property를 정의하고 있으며, 이 컬렉션에 GestureRecognizer를 상속한 Class 인스턴스를 추가할 수 있습니다.

GestureRecognizer 파생 Class 중에서 가장 자주 사용되는 TapGestureRecognizer는 사용자의 탭 입력을 받아서 Tapped 이벤트를 발생시킵니다. MonkeyTap 프로그램은 이미테이션 게임을 만들기 위해 4개의 BoxViewTapGestureRecognizer를 연결하는 방법을 보여하고 있습니다.
MonkeyTap(Imitation Game)

MonkeyTap 프로그램은 사운드 재생이 필수적이기 때문에 이 부분은 다음 챕터에서 다루게 됩니다. (다음 챕터 보기)

댓글
댓글쓰기 폼