Microsoft MVP성태의 닷넷 이야기
Phone: 10. Xamarin.Forms - 1. Forms 시작하기 [링크 복사], [링크+제목 복사],
조회: 24090
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
[Part1.zip]    

(시리즈 글이 3개 있습니다.)
개발 환경 구성: 183. Xamarin 2.0 - 윈도우 환경의 Visual Studio에서 C#으로 iOS/Android 응용 프로그램 개발
; https://www.sysnet.pe.kr/2/0/1415

Phone: 10. Xamarin.Forms - 1. Forms 시작하기
; https://www.sysnet.pe.kr/2/0/1699

Phone: 11. Xamarin.Forms - 2. XAML을 이용한 페이지 개발
; https://www.sysnet.pe.kr/2/0/1702




Xamarin.Forms - 1. Forms 시작하기

이번에 Xamarin이 버전 3을 발표하면서 물건을 내놨습니다. ^^ 바로 Xamarin.Forms 인데요. 기존의 Xamarin 앱이 C# 문법만을 사용했을 뿐 각각의 플랫폼(iOS, Android)에서 앱을 만들려면 고유의 User Interface 관련 API를 익혀야 했습니다.

하지만, Xamarin.Forms가 나오면서 이런 상황이 바뀝니다. 물론, 앱은 각각의 플랫폼 별로 프로젝트가 있어야 하지만 UI 요소만큼은 Xamarin.Forms를 이용해 공통화했습니다. 게다가 플랫폼도 기존의 iOS, Android와 함께 Windows Phone (8.0 이상)까지 지원을 넓혔습니다. 한마디로, 1타 3피의 성과를 낼 수 있는데 예전 같으면 이런 경우 "Web App"을 이용해야 했지만 Xamarin을 통해 빠른 속도의 앱을 보장하면서도 3가지 주요 모바일 플랫폼을 지원할 수 있게 된 것입니다.

Xamarin.Forms 소개를 어떻게 할까 고민하던 중에 외국 블로거가 이미 쓰고 있어서, 이를 적당히 참고해서 소개하겠습니다. ^^

Learning Xamarin - Part 1: Forms
; http://blog.falafel.com/Blogs/jesse-liberty/2014/06/11/learning-xamarin---part-1-forms




어차피 Xamarin을 구매할 정도의 국내 업체라면 Visual Studio도 충분히 구비하고 있을 것이라 생각됩니다. 따라서 이 글에서는 Xamarin Studio를 이용하기 보다는 Visual Studio를 이용해 실습을 진행하겠습니다. (사실, 어느 걸로 해도 상관없습니다. 단지, Visual Studio의 에디터가 더 훌륭하기 때문에 선택한 것일뿐.)

Xamarin 3을 설치한 다음 Visual Studio 2013에서 새 프로젝트를 만들면 다음과 같이 "Mobile Apps" 영역에서 Xamarin.Forms 관련 프로젝트를 선택할 수 있습니다. 이 중에서 앱을 만들 것이기 때문에 "Blank App (Xamarin.Forms Portable)"로 프로젝트를 시작하겠습니다.

xamarin_forms_part1_1.png

프로젝트를 생성하면 다음과 같이 총 4개의 프로젝트가 만들어집니다.

xamarin_forms_part1_2.png

아하~~~ 이제 알겠습니다. ^^ 각각의 개별 Mobile App 플랫폼 별로 프로젝트가 생성되고, 공통 Form 관련 프로젝트(위에서는 Part1 프로젝트)가 있습니다. 즉, UI 관련 프로젝트를 3개의 모바일 폴랫폼에서 공유할 수 있도록 하는 것입니다. (일단, 제 컴퓨터에서는 iOS를 빌드할 수 있는 맥이 없으므로 Part1.iOS 프로젝트는 제거하겠습니다.)

예제를 위해 다음과 같은 데이터 클래스를 (Portable) 프로젝트에 만듭니다.

public class Monkey
{
    public string Name {get;set;}
    public string Location { get; set; }
    public string Details { get; set; }
}

이것을 Xamarin.Form과 연결해 줄 방법으로 "Learning Xamarin - Part 1: Forms" 글에서는 MVVM을 사용하고 있습니다. 3개의 모바일 플랫폼 유형에서 UI Form만 공유할 것이기 때문에 MVVM 방식은 꽤나 멋진 접근법이 될 수 있습니다.

따라서 MonkeysViewModel.cs 파일을 추가하고 다음의 코드를 추가합니다.

using Part1;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Monkeys.ViewModels
{
    public class MonkeysViewModel
    {
        public ObservableCollection<Monkey> Monkeys { get; set; }

        public MonkeysViewModel()
        {
            Monkeys = new ObservableCollection<Monkey>();
            Monkeys.Add(new Monkey
            {
                Name = "Baboon",
                Location = "Africa & Asia",
                Details = "Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae."
            });

            Monkeys.Add(new Monkey
            {
                Name = "Capuchin Monkey",
                Location = "Central & South America",
                Details = "The capuchin monkeys are New World monkeys of the subfamily Cebinae. Prior to 2011, the subfamily contained only a single genus, Cebus."
            });

            Monkeys.Add(new Monkey
            {
                Name = "Blue Monkey",
                Location = "Central and East Africa",
                Details = "The blue monkey or diademed monkey is a species of Old World monkey native to Central and East Africa, ranging from the upper Congo River basin east to the East African Rift and south to northern Angola and Zambia"
            });


            Monkeys.Add(new Monkey
            {
                Name = "Squirrel Monkey",
                Location = "Central & South America",
                Details = "The squirrel monkeys are the New World monkeys of the genus Saimiri. They are the only genus in the subfamily Saimirinae. The name of the genus Saimiri is of Tupi origin, and was also used as an English name by early researchers."
            });

            Monkeys.Add(new Monkey
            {
                Name = "Golden Lion Tamarin",
                Location = "Brazil",
                Details = "The golden lion tamarin also known as the golden marmoset, is a small New World monkey of the family Callitrichidae."
            });

            Monkeys.Add(new Monkey
            {
                Name = "Howler Monkey",
                Location = "South America",
                Details = "Howler monkeys are among the largest of the New World monkeys. Fifteen species are currently recognized. Previously classified in the family Cebidae, they are now placed in the family Atelidae."
            });

            Monkeys.Add(new Monkey
            {
                Name = "Japanese Macaque",
                Location = "Japan",
                Details = "The Japanese macaque, is a terrestrial Old World monkey species native to Japan. They are also sometimes known as the snow monkey because they live in areas where snow covers the ground for months each"
            });

            Monkeys.Add(new Monkey
            {
                Name = "Mandrill",
                Location = "Southern Cameroon, Gabon, Equatorial Guinea, and Congo",
                Details = "The mandrill is a primate of the Old World monkey family, closely related to the baboons and even more closely to the drill. It is found in southern Cameroon, Gabon, Equatorial Guinea, and Congo."
            });

            Monkeys.Add(new Monkey
            {
                Name = "Proboscis Monkey",
                Location = "Borneo",
                Details = "The proboscis monkey or long-nosed monkey, known as the bekantan in Malay, is a reddish-brown arboreal Old World monkey that is endemic to the south-east Asian island of Borneo."
            });
        }
    }
}

데이터가 마련되었으니, 이제 3개의 프로젝트(여기서는 iOS제외 2개)에서 공통적으로 사용할 페이지를 만들어 보겠습니다. 역시 (Portable) 프로젝트에 MonkeyPage.cs 파일을 추가하고 코드를 만들어 줍니다.

using Monkeys.ViewModels;
using Xamarin.Forms;

namespace Part1
{
    public class MonkeyPage : ContentPage
    {
        public MonkeyPage()
        {
            Title = "Monkeys";

            var list = new ListView();

            var viewModel = new MonkeysViewModel();
            list.ItemsSource = viewModel.Monkeys;

            var cell = new DataTemplate(typeof(TextCell));

            cell.SetBinding(TextCell.TextProperty, "Name");
            cell.SetBinding(TextCell.DetailProperty, "Location");

            list.ItemTemplate = cell;

            Content = list;
        }
    }
}

위의 코드를 이해하려면 WPF를 알고 있어야 합니다. 왜냐하면 Xamarin.Forms는 이름과는 달리 윈도우 폼이 아닌 WPF 방식을 채택하고 있기 때문입니다. (여기서 WPF에 대한 설명을 하는 것은 어렵기 때문에 넘어가겠습니다. ^^)

자, 이제 이렇게 만들어진 앱 페이지를 각각의 플랫폼 프로젝트에서 사용해야겠지요. ^^

그 방법은 (Portable) 프로젝트의 App.cs에 정의된 GetMainPage가 해결해 줍니다. 즉, 3개의 앱 프로젝트는 (Portable) 프로젝트의 App.GetMainPage가 반환하는 페이지를 화면에 보여주는 것입니다. 따라서, 다음과 같이 App.cs 코드를 (기존 내용은 지우고) 구성하면 됩니다.

using Xamarin.Forms;

namespace Part1
{
    public class App
    {
        public static Page GetMainPage()
        {
            var monkeys = new MonkeyPage();
            return new NavigationPage(monkeys);

        }
    }
}

끝입니다. 여기까지 코드를 구성했으면 이제 "Part1.WinPhone (Windows Phone 8.0)" 프로젝트를 빌드하고 (에뮬레이터로) 실행하면 이렇게 보입니다.

xamarin_forms_part1_3.png

이어서 "Part1.Android" 프로젝트를 빌드하고 (에뮬레이터로) 실행하면 이렇게 보입니다.

xamarin_forms_part1_4.png

오... 그렇군요. 안타깝지만 아직 UI를 똑같이 보여주지는 않습니다. ^^ 따지고 보면, 윈폰에서 Detail 속성의 텍스트가 파란색으로 나오는 것이 오히려 이상합니다. 위의 코드에 보면 우리는 파란색이 나오라고 지정한 것이 없기 때문입니다. 아마도 Xamarin 측에서 TextCell.DetailProperty를 자동으로 파란색으로 바인딩하는 것 같은데, 그 처리를 안드로이드에서는 아직 구현을 못한 것 같습니다.




첫 페이지는 그렇게 했고, 그럼 다른 페이지로의 네비게이션은 어떻게 할 수 있을까요? 이를 위해 ListView 컨트롤의 항목을 눌렀을 때 상세 페이지로 넘어가는 것을 "Learning Xamarin - Part 1: Forms"글에 설명된 데로 구현해 보겠습니다.

우선, MonkeyPage 만들때처럼 ContentPage를 상속받는 DetailPage.cs를 (Portable) 프로젝트에 추가합니다.

using Xamarin.Forms;

namespace Part1
{
    public class DetailPage : ContentPage
    {
        public DetailPage(Monkey monkey)
        {
            this.Title = monkey.Name;

            var details = new Label
            {
                Text = monkey.Details
            };

            Content = new ScrollView
            {
                Padding = 20,
                Content = details
            };
        }
    }
}

그리곤 MonkeyPage.cs에 포함된 ListView 항목이 선택되는 것에 대한 이벤트 처리기를 만들고 DetailPage에 네비게이션하는 코드를 넣어줍니다.


using Monkeys.ViewModels;
using Xamarin.Forms;

namespace Part1
{
    public class MonkeyPage : ContentPage
    {
        public MonkeyPage()
        {
            // ... [생략] ...

            list.ItemTapped += (sender, args) =&gt;
            {
              var monkey = args.Item as Monkey;
              if (monkey == null)
                return;

              Navigation.PushAsync(new DetailsPage(monkey));
              list.SelectedItem = null;
            };

            Content = list;
        }
    }
}

보시는 바와 같이 Navigation 객체의 PushAsync를 이용해 다른 페이지로 네비게이션하고 있습니다. 역시 이번에도 실행해 보면 윈도우폰/안드로이드폰 모두 잘 동작합니다.

이 정도면, Xamarin.Forms에 대해서 대강 감이 오시겠죠? ^^

(첨부 파일은 위의 예제 코드를 구현한 Xamarin 프로젝트입니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2014-07-08 02시03분
멋지군요. Xamarin.Forms 관련 라이브러리들이 등장하는군요.

XForms/Xamarin-Forms-Labs
; https://github.com/XForms/Xamarin-Forms-Labs

--------------------------------------------------------

Microsoft Graveyard
; https://microsoftgraveyard.com/
정성태
2021-01-26 07시53분
[.NET Conf 2021 x Seoul] 닷넷으로 하는 크로스 플랫폼 앱 개발의 현재와 미래 (동영상 57분)
; https://www.youtube.com/watch?v=WdssztHlPDI
정성태

... 76  77  78  79  80  81  82  83  84  [85]  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11811정성태2/11/201920957오류 유형: 510. 서버 운영체제에 NVIDIA GeForce Experience 실행 시 wlanapi.dll 누락 문제
11810정성태2/11/201918539.NET Framework: 808. .NET Profiler - GAC 모듈에서 GAC 비-등록 모듈을 참조하는 경우의 문제
11809정성태2/11/201920759.NET Framework: 807. ClrMD를 이용해 메모리 덤프 파일로부터 특정 인스턴스를 참조하고 있는 소유자 확인
11808정성태2/8/201922073디버깅 기술: 123. windbg - 닷넷 응용 프로그램의 메모리 누수 분석
11807정성태1/29/201919978Windows: 156. 가상 디스크의 용량을 복구 파티션으로 인해 늘리지 못하는 경우 [4]
11806정성태1/29/201919607디버깅 기술: 122. windbg - 덤프 파일로부터 PID와 환경 변수 등의 정보를 구하는 방법
11805정성태1/28/201921788.NET Framework: 806. C# - int []와 object []의 차이로 이해하는 제네릭의 필요성 [4]파일 다운로드1
11804정성태1/24/201919641Windows: 155. diskpart - remove letter 이후 재부팅 시 다시 드라이브 문자가 할당되는 경우
11803정성태1/10/201918512디버깅 기술: 121. windbg - 닷넷 Finalizer 스레드가 멈춰있는 현상
11802정성태1/7/201920216.NET Framework: 805. 두 개의 윈도우를 각각 실행하는 방법(Windows Forms, WPF)파일 다운로드1
11801정성태1/1/201921486개발 환경 구성: 427. Netsh의 네트워크 모니터링 기능 [3]
11800정성태12/28/201820602오류 유형: 509. WCF 호출 오류 메시지 - System.ServiceModel.CommunicationException: Internal Server Error
11799정성태12/19/201822363.NET Framework: 804. WPF(또는 WinForm)에서 UWP UI 구성 요소 사용하는 방법 [3]파일 다운로드1
11798정성태12/19/201821210개발 환경 구성: 426. vcpkg - "Building vcpkg.exe failed. Please ensure you have installed Visual Studio with the Desktop C++ workload and the Windows SDK for Desktop C++"
11797정성태12/19/201817189개발 환경 구성: 425. vcpkg - CMake Error: Problem with archive_write_header(): Can't create '' 빌드 오류
11796정성태12/19/201817529개발 환경 구성: 424. vcpkg - "File does not have expected hash" 오류를 무시하는 방법
11795정성태12/19/201820751Windows: 154. PowerShell - Zone 별로 DNS 레코드 유형 정보 조회 [1]
11794정성태12/16/201816870오류 유형: 508. Get-AzureWebsite : Request to a downlevel service failed.
11793정성태12/16/201819387개발 환경 구성: 423. NuGet 패키지 제작 - Native와 Managed DLL을 분리하는 방법 [1]
11792정성태12/11/201819175Graphics: 34. .NET으로 구현하는 OpenGL (11) - Per-Pixel Lighting파일 다운로드1
11791정성태12/11/201819192VS.NET IDE: 130. C/C++ 프로젝트의 시작 프로그램으로 .NET Core EXE를 지정하는 경우 닷넷 디버깅이 안 되는 문제 [1]
11790정성태12/11/201817647오류 유형: 507. Could not save daemon configuration to C:\ProgramData\Docker\config\daemon.json: Access to the path 'C:\ProgramData\Docker\config' is denied.
11789정성태12/10/201831280Windows: 153. C# - USB 장치의 연결 및 해제 알림을 위한 WM_DEVICECHANGE 메시지 처리 [2]파일 다운로드2
11788정성태12/4/201817560오류 유형: 506. SqlClient - Value was either too large or too small for an Int32.Couldn't store <2151292191> in ... Column
11787정성태11/29/201821717Graphics: 33. .NET으로 구현하는 OpenGL (9), (10) - OBJ File Format, Loading 3D Models파일 다운로드1
11786정성태11/29/201818694오류 유형: 505. OpenGL.NET 예제 실행 시 "Managed Debugging Assistant 'CallbackOnCollectedDelegate'" 예외 발생
... 76  77  78  79  80  81  82  83  84  [85]  86  87  88  89  90  ...