2016년 10월 10일 월요일

Xamarin.Forms XAML Basics Part 4, Data Binding Basics

Xamarin.Forms에서의 XAML 사용이 궁금해서 아래 링크를 보며 대충 필요한 것만 정리함.
https://developer.xamarin.com/guides/xamarin-forms/xaml/

Part 4. Data Binding Basics


Data binding은 source, target 두 객체의 property들을 연결한다. 

이를 위해 두단계의 과정이 필요하다.
먼저 target의 BidningContext property가 source로 지정되어야 하고
SetBinding method을 target에서 호출 하여
source의 property와 binding 해야 한다. (뭔소리임?)

즉 Target property는 bindable property 여야 하는데 이를 위해 target은 BindableObject를 상속 받아야 한다.

XAML에서는 Binding markup extension이 SetBinding call과 Binding class를 대신한다는 것을 제외 하고 동일핟.
다만 BindingContext를 지정하는 방법이 있는게 아니라 code-behind file(XAML의 cs파일)에서 지정 하는 방법이나 StaticResource, x:Static markup extension을 사용하는 방법, BindingContext property-element tag를 사용하는 방법을 사용할 수 있다.


View-to-View Bindings
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_binding_basics/#View-to-View_Bindings

동일 page 내에서 view들을 binding하고자 할 경우 target object에 BindingContext를 x:Reference markup extension을 사용하여 지정할 수 있다.

아래는 Slider와 두개의 Label가 binding 되어 있는 XAML이다.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SliderBindingsPage"
             Title="Slider Bindings Page">

  <StackLayout>
    <Label Text="ROTATION"
           BindingContext="{x:Reference Name=slider}"
           Rotation="{Binding Path=Value}"
           FontAttributes="Bold"
           FontSize="Large"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />

    <Slider x:Name="slider"
            Maximum="360"
            VerticalOptions="CenterAndExpand" />

    <Label BindingContext="{x:Reference slider}"
          Text="{Binding Value,
                          StringFormat='The angle is {0:F0} degrees'}"
          FontAttributes="Bold"
          FontSize="Large"
          HorizontalOptions="Center"
          VerticalOptions="CenterAndExpand" />
  </StackLayout>
</ContentPage>

BindingContext에서 x:Refernce가 slider로만 되어 있는데 이는 대상 object의 x:Name이다.

BindingContext="{x:Reference Name=slider}"
BindingContext="{x:Reference slider}"

Binding markup extension은 BindingBase, Binding과 같은 여러 property를 가지고 있다.
Binding 의 ContentProperty로 지정된 proeprty는 Path이므로 Binding markup extension에서 처음 item일 경우 "Path=" 을 명시적으로 지정하지 않아도 된다.

Rotation="{Binding Path=Value}"
Text="{Binding Value,
               StringFormat='The angle is {0:F0} degrees'}"

함께 StringFormat이 사용되어 있는데 이는 Xamarin.Forms에서 implicit type conversions을 수행하지 않기 때문에 non-string인 Value 값을 string으로 변환하기 위해 사용되었다.

함께 알고 있어야 할 것은 StringFormat은 static String.Format method를 사용하는데 내부적으로 {}을 사용하고 있어 XAML parser에서의 혼동과 같은 문제를 야기할 수 있으므로 string formatting시 single quotation marks('')을 사용하여 표시해야 한다.

Text="{Binding Value,
               StringFormat='The angle is {0:F0} degrees'}"

Backwards Bindings
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_binding_basics/#Backwards_Bindings

하나의 view는 여러 property들에 대해서 data binding이 가능하다. 하지만 각 view는 하나의 BindingContext을 가지므로 multiple data binding 시 동일 object에 대해서 여러 reference를 가져야 한다.

이 같은 제약을 해결하기 위해서 종종 OneWayToSource나 TwoWay mode를 사용하여 view-to-view binding을 사용하기도 한다.

4개의 Slider를 사용하여 Label의 Scale, Rotate, RotateX, RotateY를 조절하고자 할 경우 Label의 BindingContext가 하나이므로 Label에서 각 Slider로 binding하는 것은 어렵다. 이러한 점을 회피하고자 binding을 반대로 Slider들의 BindingContext를 Label로 지정하고 Slider의 Value property를 binding하는 방법을 사용한다.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SliderTransformsPage"
             Title="Slider Transforms Page">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <StackLayout Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">

      <!-- Scaled and rotated Label -->
      <Label x:Name="label"
             Text="TEXT"
             HorizontalOptions="Center"
             VerticalOptions="CenterAndExpand" />

    </StackLayout>

    <!-- Slider and identifying Label for Scale -->
    <Slider x:Name="scaleSlider"
            BindingContext="{x:Reference label}"
            Grid.Row="1" Grid.Column="1"
            Maximum="10"
            Value="{Binding Scale, Mode=TwoWay}" />

    <Label BindingContext="{x:Reference scaleSlider}"
           Text="{Binding Value, StringFormat='Scale = {0:F1}'}"
           Grid.Row="1" Grid.Column="0"
           VerticalTextAlignment="Center" />

    <!-- Slider and identifying Label for Rotation -->
    <Slider x:Name="rotationSlider"
            BindingContext="{x:Reference label}"
            Grid.Row="2" Grid.Column="1"
            Maximum="360"
            Value="{Binding Rotation, Mode=OneWayToSource}" />

    <Label BindingContext="{x:Reference rotationSlider}"
           Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"
           Grid.Row="2" Grid.Column="0"
           VerticalTextAlignment="Center" />

    <!-- Slider and identifying Label for RotationX -->
    <Slider x:Name="rotationXSlider"
            BindingContext="{x:Reference label}"
            Grid.Row="3" Grid.Column="1"
            Maximum="360"
            Value="{Binding RotationX, Mode=OneWayToSource}" />

    <Label BindingContext="{x:Reference rotationXSlider}"
           Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"
           Grid.Row="3" Grid.Column="0"
           VerticalTextAlignment="Center" />

    <!-- Slider and identifying Label for RotationY -->
    <Slider x:Name="rotationYSlider"
            BindingContext="{x:Reference label}"
            Grid.Row="4" Grid.Column="1"
            Maximum="360"
            Value="{Binding RotationY, Mode=OneWayToSource}" />

    <Label BindingContext="{x:Reference rotationYSlider}"
           Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
           Grid.Row="4" Grid.Column="0"
           VerticalTextAlignment="Center" />
  </Grid>
</ContentPage>

초기 예제는 Label들에서 slider의 Value property로 binding 하였으나
Label  --Value--   slider    --Value--   Label

아래와 같이
label의 Scale, Rotation, RotationX, RotationY를 각 slider에서 binding하고
각 slider들의 Value property를 각 Label들에서 binding하는 형태이다.

label <--  Scale  -->   scaleSlider       --Value--   Label
      <--Rotation--     rotationSlider    --Value--   Label
      <--RotationX--   rotationXSlider   --Value--   Label
      <--RotationY--   rotationYSlider   --Value--   Label

Scale property는 Twoway로 binding되는데 이는 Scale property의 초기값이 1인 관계로 이를 scaleSlider에도 반영하기 위해서 이다. OneWayToSource로 binding하게 되면 Scale property는 Slider의 기본 값인 0으로 설정되어 Label이 보이지 않게 된다.

또한 명세에서 보면 column 0에 Label을 column 1에 Slider를 배치하였지만 작성 순서는 Slider를 먼저 그다음에 Label을 배치하였음 이유는 Slider의 값을 Label에서 OneWay binding 하고 있어 Slider가 우선 정의 되어야 Label에서 값을 참조할 수 있음.


Bindings and Collections
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_binding_basics/#Bindings_and_Collections


Templated ListView를 사용할 때 XAML과 data binding feature가 상당히 유용하다.
ListView는 IEnumerable를 구현하고 있는 ItemSource property를 가지고 있어 이를 item을 표시할 때 사용한다. ListView collection은 Cell을 상속한 template를 사용함으로 써 사용자가 원하는 형태로 보여줄 수 있다. Template는 ListView의 개별 item들을 위해 clone되고 각 clone들을 설정하기 위해 data를 binding 한다.

주로 Custom Cell을 만들기 위해 ViewCell class를 사용하여 coding하는 방법이 꽤나 지저분하지만 XAML에서는 상당히 간단하다.

아래 sample project에서는 NamedColor class를 include한다. NamedColor는 Name과 FriendlyName, color를 가지고 있고 내부적으로 static read-only color 값의 목록을 포함하고 있다.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
             x:Class="XamlSamples.ListViewDemoPage"
             Title="ListView Demo Page">

  <ListView ItemsSource="{x:Static local:NamedColor.All}" />

</ContentPage>

Item의 template을 지정하려면 ItemTemplate property에 ViewCell을 포함한 DataTemplate를 지정해야 한다.

<ListView ItemsSource="{x:Static local:NamedColor.All}">
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <ViewCell.View>
            <Label Text="{Binding FriendlyName}" />
          </ViewCell.View>
        </ViewCell>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>

좀 더 꾸미기 위해 page의 resource dictionary를 정의해서 사용할 수 있다.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
             x:Class="XamlSamples.ListViewDemoPage"
             Title="ListView Demo Page">

  <ContentPage.Resources>
    <ResourceDictionary>
      <OnPlatform x:Key="boxSize"
                  x:TypeArguments="x:Double"
                  iOS="50"
                  Android="50"
                  WinPhone="75" />

      <!-- This is only an issue on the iPhone; Android and
           WinPhone auto size the row height to the contents. -->
      <OnPlatform x:Key="rowHeight"
                  x:TypeArguments="x:Int32"
                  iOS="60"
                  Android="60"
                  WinPhone="85" />

      <local:DoubleToIntConverter x:Key="intConverter" />

    </ResourceDictionary>
  </ContentPage.Resources>

  <ListView ItemsSource="{x:Static local:NamedColorGroup.All}"
            RowHeight="{StaticResource rowHeight}">
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <ViewCell.View>
            <StackLayout Padding="5, 5, 0, 5"
                         Orientation="Horizontal"
                         Spacing="15">

              <BoxView WidthRequest="{StaticResource boxSize}"
                       HeightRequest="{StaticResource boxSize}"
                       Color="{Binding Color}" />

              <StackLayout Padding="5, 0, 0, 0"
                           VerticalOptions="Center">

                <Label Text="{Binding FriendlyName}"
                       FontAttributes="Bold"
                       FontSize="Medium" />

                <StackLayout Orientation="Horizontal"
                             Spacing="0">
                  <Label Text="{Binding Color.R,
                                   Converter={StaticResource intConverter},
                                   ConverterParameter=255,
                                   StringFormat='R={0:X2}'}" />
                  <Label Text="{Binding Color.G,
                                   Converter={StaticResource intConverter},
                                   ConverterParameter=255,
                                   StringFormat=', G={0:X2}'}" />
                  <Label Text="{Binding Color.B,
                                   Converter={StaticResource intConverter},
                                   ConverterParameter=255,
                                   StringFormat=', B={0:X2}'}" />
                </StackLayout>
              </StackLayout>
            </StackLayout>
          </ViewCell.View>
        </ViewCell>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>
</ContentPage>

Xamarin.Forms의 Color의 type은 0-1값을 가지는 double형이므로 int형으로 형변환이 필요하고 또한 Anroid RGB color 범위에 맞춰서 값 변환이 필요하다.

이는 외부 binding converter를 통해서 가능하다.

using System;
using System.Globalization;
using Xamarin.Forms;

namespace XamlSamples
{
    class DoubleToIntConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
                              object parameter, CultureInfo culture)
        {
            double multiplier;

            if (!Double.TryParse(parameter as string, out multiplier))
                multiplier = 1;

            return (int)Math.Round(multiplier * (double)value);
        }

        public object ConvertBack(object value, Type targetType,
                                  object parameter, CultureInfo culture)
        {
            double divider;

            if (!Double.TryParse(parameter as string, out divider))
                divider = 1;

            return ((double)(int)value) / divider;
        }
    }
}

ListView의 item이 동적으로 변경되는 것을 처리하기 위해서는 INotifyCollectionChanged interface를 구현하고 있는 ObservableCollection을 사용해서 CollectionChanged event handler를 통해서 처리 가능하다.
또한 property들이 변경되는 것은 INotifyPropertyChanged interface를 구현해서 PropertyChanged event handler를 통해서 처리 가능함.


2016년 10월 3일 월요일

Synology NAS의 Media server의 폴더가 업데이트 되지 않거나 유령 폴더가 보일 때

아래 내용은 예전 내용이고  
Synology DisStation > 제어판 > 색인 서비스 > 미디어 색인 > "색인 재설정" 버튼을 눌러
media 파일, 폴더를 동기화 시키면 문제가 해결됩니다. 

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

Synology NAS를 사용해서 종종 media 파일들을 TV에서 시청한다.

UPnP, DLNA 기술을 사용하는 거라 Synology NAS에는 DLNA Digital Media Server인 Media Server가 있어 이를 Digital Media Player인 TV에서 검색(Browse)하고 재생을 할 수 있다.

하지만 Synology NAS의 indexing이 PC의 수정을 제대로 반영하지 못하는지 종종 업데이트가 안되거나 지우거나 이동한 유령 폴더들이 보일 때가 있어 종종 찝찝함.

이런 상황을 아래 방법을 통해 수정 가능함. (좀 무식한 방법이긴 하지만)

1. Synology NAS에 SSH를 사용하여 접속할 수 있도록 설정에서 SSH 허용
2. SSH 접속을 하여 아래 명령어 입력
    synoindex -R [type_music|type_video|type_photo] 
    (하지만 전체 컨텐트를 업데이트 하는 거라 생각보다 오래 걸림...)


특정 부분을 업데이트 하고자 할 경우 아래 usage 참고

hello@kkk:~$ synoindex
Usage:
synoindex [OPTIONS]

Index Options:
    -h, --help
        this help text
    -A dirpath
        add a dir
    -a filepath
        add a file
    -D dirpath
        delete a dir
    -d filepath
        delete a file
    -N new_dirpath old_dirpath
        rename a dir
    -n new_filepath old_filepath
        rename a file
    -R [all|media|photo|music|video|thumb|dirpath]
        all:     reindex all dirpath that registered in each package
        media:   reindex dirpath that registered in MediaIndex package
        photo:   reindex photo dirpath
        music:   reindex music dirpath
        video:   reindex video dirpath
        thumb:   check converted video of each video file
        dirpath: reindex this specific dirpath
    -R user:{user_name}
        reindex personal photo dirpath
    -R share:{share_name}
        reindex share dirpath
    -R [type_music|type_video|type_photo]
        reindex dirpath that registered with specific type in MediaIndex

Package Index Options:
    -P [MediaIndex|{package_name}] {index_option}
        index operation only apply on this package
    -p [MediaIndex|{package_name}] {index_option}
        index operation apply all packages except for this package

File Index Options:
    -f {index_option}
        index operation apply on file index
    -U photo
        update photo images

hello@kkk:~$ synoindex -R type_video

Xarmarin.Forms XAML Basics Part 2, Essential XAML Syntax

Xamarin.Forms에서의 XAML 사용이 궁금해서 아래 링크를 보며 대충 필요한 것만 정리함.
https://developer.xamarin.com/guides/xamarin-forms/xaml/

Part 2. Essential XAML Syntax


Property Elements

아래 둘은 동일한 설정임.

<Label Text="Hello, XAML!"
       VerticalOptions="Center"
       FontAttributes="Bold"
       FontSize="Large"
       TextColor="Aqua" />
<Label Text="Hello, XAML!"
       VerticalOptions="Center"
       FontAttributes="Bold"
       FontSize="Large">
  <Label.TextColor>
    Aqua
  </Label.TextColor>
</Label>

Label은 object element, Text, VerticalOptions, FontAttributes, FontSize들은 property attributes 임. 아래 XAML의 TextColor는 property element임.

하지만 아래 처럼 property element은 지나치게 긴 value를 간단하게 나타내기 어려울 때 사용할 수 있음.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.GridDemoPage"
             Title="Grid Demo Page"
             Padding="0, 20, 0, 0">

  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
      <RowDefinition Height="100" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="100" />
    </Grid.ColumnDefinitions>

    ...

  </Grid>
</ContentPage>

플랫폼 별로 ContentPage의 padding이 다른 경우가 있어 이를 위해 OnPlatform<T> generic class를 사용하는데 이를 XAML에서도 표현할 수 있다.

OnPlatform tag를 사용하는데 단 OnPlatform class는 generic class이므로 type을 지정해주어야 한다. 이를 위해 padding property의 type인 Thickness를 x:TypeArguments로 지정해 줘야한다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.GridDemoPage"
             Title="Grid Demo Page">

  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness">
      <OnPlatform.iOS>
        0, 20, 0, 0
      </OnPlatform.iOS>
      <OnPlatform.Android>
        0, 0, 0, 0
      </OnPlatform.Android>
      <OnPlatform.WinPhone>
        0, 0, 0, 0
      </OnPlatform.WinPhone>
    </OnPlatform>
  </ContentPage.Padding>

  ...

</ContentPage>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.GridDemoPage"
             Title="Grid Demo Page">

  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness"
                iOS="0, 20, 0, 0" />
  </ContentPage.Padding>

  ...

</ContentPage>

Attached Properties
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/essential_xaml_syntax/#Attached_Properties

Grid의 child들이 자신들의 grid 위치를 정하기 위해 grid attributes(Grid.Row, Grid.Column등)의 속성을 사용할 수 있는데 이를 attached properties라 한다.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.GridDemoPage"
             Title="Grid Demo Page">

  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness"
                iOS="0, 20, 0, 0" />
  </ContentPage.Padding>

  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
      <RowDefinition Height="100" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="100" />
    </Grid.ColumnDefinitions>

    <Label Text="Autosized cell"
           Grid.Row="0" Grid.Column="0"
           TextColor="White"
           BackgroundColor="Blue" />

    <BoxView Color="Silver"
             HeightRequest="0"
             Grid.Row="0" Grid.Column="1" />

    <BoxView Color="Teal"
             Grid.Row="1" Grid.Column="0" />
    <Label Text="Leftover space"
           Grid.Row="1" Grid.Column="1"
           TextColor="Purple"
           BackgroundColor="Aqua"
           HorizontalTextAlignment="Center"
           VerticalTextAlignment="Center" />

    <Label Text="Span two rows (or more if you want)"
           Grid.Row="0" Grid.Column="2" Grid.RowSpan="2"
           TextColor="Yellow"
           BackgroundColor="Blue"
           HorizontalTextAlignment="Center"
           VerticalTextAlignment="Center" />

    <Label Text="Span two columns"
           Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
           TextColor="Blue"
           BackgroundColor="Yellow"
           HorizontalTextAlignment="Center"
           VerticalTextAlignment="Center" />

    <Label Text="Fixed 100x100"
           Grid.Row="2" Grid.Column="2"
           TextColor="Aqua"
           BackgroundColor="Red"
           HorizontalTextAlignment="Center"
           VerticalTextAlignment="Center" />

  </Grid>
</ContentPage>

Content Properties
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/essential_xaml_syntax/#Content_Properties


ContentPage의 Content property나 Layout들의 Children property는 아래와 같이 XAML에 서 사용할 수 있지만 일반적으로 생략하고 사용하고 있다. 이는 유일하게 사용되는 property는 ContentProperty class attribute로 지정하고 있어서 명시적으로 해당 property를 사용하지 않아도 자동으로 지정된다.

[Xamarin.Forms.ContentProperty("Content")]
public class ContentPage : Page
[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout ...

Xamarin.Forms에서 사용되는 ContentProperty attribute들은 다음과 같다.

ELEMENTCONTENT PROPERTY
ContentPageContent
ContentViewContent
FrameContent
LabelText
Layout<T>Children
ScrollViewContent
ViewCellView














2016년 9월 26일 월요일

Xamarin Android project 빌드 시 에러 관련(major version 52 is newer than 51, the highest major version supported by this compiler)

그냥 메모로 남김.

Xamarin.Forms Android project 빌드 시 아래와 같은 메세지가 발생하면

major version 52 is newer than 51, the highest major version supported by this compiler.


JDK를 1.8 버전을 설치하고 이를 Xamarin Android 설정으로 지정하면 됨.

Visual Studio 2015 > 도구 > 옵션 > Xamarin > Android Settings > Java Development Kit Location

http://stackoverflow.com/a/38546946








Xamarin for Visual Studio 가 업데이트 되지 않을 때

Visual Studio 2015 설치 시 함께 Xamarin을 함께설치할 수 있고
VS를 사용하다보면 작업 표시줄에서 Xamarin for Visual Studio의 업데이트를 알리는 tooltip이 보일 때가 있다.

일시적인 현상인지는 모르겠으나 해당 tooltip을 클릭해도
업데이트 할 수 없는 현상이 있는데 이럴 때 해결 책은

Visual Studio > 도구 메뉴 > 옵션 항목 > Xamarin > Other 의 Check Now를 클릭하면 업데이트 가능하다.

https://forums.xamarin.com/discussion/comment/176387/#Comment_176387

JimArbuthnot.6008JimArbuthnot.6008Jim Arbuthnot
 edited January 21
Actually I just found the solution from StackOverflow:
Tools --> Options --> Xamarin
Under "iOS Settings" and "Android Settings" there is a link for "Check Now" next to the Updates settings
Update: if you are using VS 2012, Tools--> Options --> Xamarin --> Other --> Check Now

2016년 9월 22일 목요일

Xamarin.Forms XAML Basics Part 1, Getting Started with XAML

Xamarin.Forms에서의 XAML 사용이 궁금해서 아래 링크를 보며 대충 필요한 것만 정리함.
https://developer.xamarin.com/guides/xamarin-forms/xaml/

XAML Basics

https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/


Xarmarin 장단점

장점
- 코드에 비해 명료하여 읽고 이해하기 쉬움
- XML 형식을 따르므로 UI 객체의 상속구조를 표현할 수 있음.

단점
- Layout만을 표현하므로 이벤트 핸들러는 코드에서 처리 되어야 한다.
- XAML은 ListView와 같이 반복적인 동작(loop)은 포함할 수 없다.
- XAML은 conditional processing을 포함할 수 없어 조건에 따라 동적으로 layout을 변경할 수 없다.
- XAML은 일반적으로 파라미터를 가지는 생성자를 가진 객체의 인스턴스화를 할 수 없다. (방법은 있다고 함.)
- XAML은 일반적으로 method를 호출할 수 없다. (방법은 있다고 함.)

XAML은 일반적으로 XML이지만 아래의 특성을 가진다.
- Property elements
- Attached properties
- Markup extensions

Part 1. Getting Started with XAML

https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/getting_started_with_xaml/


Anatomy of a XAML Class

- XAML 파일(xxx.xaml) 생성 시 xxx.xaml.cs가 함께 생성 됨.
  xxx.xaml.cs는 C# code 파일로서 XAML file과 함께 xxx class 정의를 위해 사용됨.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.HelloXamlPage">
</ContentPage>

XML의 namespace는 기본적으로 Xamarin.Forms와 x로 지정된 MS의 XAML specification(https://msdn.microsoft.com/en-us/library/ff629155.aspx)을 따르고 있음.

x:Class로 XAML 파일이 인스턴스화 될 class 이름이고 XamlSamples namespace의 HelloXamlPage class 임을 말하고 있다.

HelloXamlPage.xaml.cs 를 보면 다음과 같다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XamlSamples
{
    public partial class HelloXamlPage
    {
        public HelloXamlPage()
        {
            InitializeComponent();
        }
    }
}
눈여겨 봐야할 부분은 HelloXamlPage class가 partial로 정의 되어 있어 다른 곳에 부분적인 class 정의가 있음을 명시하고 있는 점과 ContentPage를 상속받지 않는다는 점이다.

일단 HelloXamlPage를 main page로 아래와 같이 설정하고 컴파일을 해보자.
namespace XamlSamples
{
    public class App : Xamarin.Forms.Application
    {
        public App ()
        {
            MainPage = new HelloXamlPage();
        }
    }
}
 Build-Deply-Run 주기에서 XAML 파일은 두번 parsing된다. building 시점에 한번 parsing되고 runtime 시점에 전체 XAML 파일이 Portable Code Library DLL에 binding되고 parsing 된다.

빌드 시점에 XAML 파일은 C# code file을 obj/Debug 폴더에 다음과 같은 HelloXamlPage.xaml.g.cs 이름으로 생성한다. (.g. = generate 의미)

namespace XamlSamples {
    using System;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;

    public partial class HelloXamlPage : ContentPage {

        private void InitializeComponent() {
            this.LoadFromXaml(typeof(HelloXamlPage));
        }
    }
}

HelloXamlPage class의 다른 정의 부분이고 HelloXamlPage가 ContentPage를 상속하고 HelloXamlPage.xaml.cs에서 호출하는 InitiailzeComponent()를 정의하고 함수내에서 Xaml을 로드함을 알 수 있다.

즉 runtime 시 각 platform project들은 초기화면을 구성하기 위해 App.GetMainPage를 호출하고 그 때 HelloXamlPage class가 instance화 된다. instance 시 생성자가 호출되며 InitialzeComponet가 호출되고 XAML파일을 로드하여 main page를 보여주게 된다.

XAML로드 시점인 LoadFromXaml() 호출 시 XAML exception들(Xamarin.Forms.Xaml.XamlParseException)이 발생이 될 수 있다.

XAML and Code Interactions
: https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/getting_started_with_xaml/#XAML_and_Code_Interactions

기본 코드 : Stack Layout내에 Slider, Label, Button이 존재함.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.XamlPlusCodePage"
             Title="XAML + Code Page">
  <StackLayout>
    <Slider VerticalOptions="CenterAndExpand" />

    <Label Text="A simple Label"
           Font="Large"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />

    <Button Text="Click Me!"
            HorizontalOptions="Center"
            VerticalOptions="CenterAndExpand" />
  </StackLayout>
</ContentPage>

Part 4. Data Binding Basics(https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_binding_basics/)와 같이 XAML과 data binding을 통해서 Slider와 Label을 처리할 수 있지만 지금은 toturial이므로 XAML에서 handler를 hardcoding하여 code에서 처리하는 예제임.

XamlPlusCodePage.xaml.cs에서 slider와 button의 event들을 처리할 method들을 정의. event handler들이고 XAML에서 호출하는 것은 동일 class이므로 public으로 정의할 필요는 없음.
namespace XamlSamples
{
    public partial class XamlPlusCodePage
    {
        public XamlPlusCodePage()
        {
            InitializeComponent();
        }

        void OnSliderValueChanged(object sender,
                                  ValueChangedEventArgs args)
        {

        }

        void OnButtonClicked(object sender, EventArgs args)
        {

        }
    }
}

위 Xaml에서 Slider, Button에 각 event에 대한 method를 지정
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.XamlPlusCodePage"
             Title="XAML + Code Page">
  <StackLayout>
    <Slider VerticalOptions="CenterAndExpand"
            ValueChanged="OnSliderValueChanged" />

    <Label Text="A simple Label"
           Font="Large"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />

    <Button Text="Click Me!"
            HorizontalOptions="Center"
            VerticalOptions="CenterAndExpand"
            Clicked="OnButtonClicked" />
  </StackLayout>
</ContentPage>

Slider의 변경에 따라서 Label을 변경하려고 할 때 Code에서 Label을 지정하여 변경할 수 있어야 한다. 이 때 XAML의 x:Name와 같은 이름의 객체를 이용하여 사용하게 되므로 XAML에서 x:Name을 지정해 줘야 한다.

<Label x:Name="valueLabel"
       Text="A simple Label"
       Font="Large"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand" />
또한 Slider의 OnSliderValueChanged handler를 통해 Slider의 값이 args를 통해 전달 되어 이를 세자리 Float 형식의 string으로 만들어 valueLabel의 Text로 지정한다.
void OnSliderValueChanged(object sender,
                          ValueChangedEventArgs args)
{
    valueLabel.Text = args.NewValue.ToString("F3");
}

Button이 클릭 될 경우 button의 text를 alert창으로 보여줄 것이고 코드는 다음과 같음.
async void OnButtonClicked(object sender, EventArgs args)
{
    Button button = (Button)sender;
    await DisplayAlert("Clicked!",
        "The button labeled '" + button.Text + "' has been clicked",
        "OK");
}

async 지시자는 DisplayAlert method가 비동기적으로 실행되어야 하는 부분인 await operator가 선언된 부분이 있음을 알리는 것이고 해당 부분은 비동기적으로 수행되므로 block 될 수 있음을 염두해야 한다.

설명한 것을 정리하면 XAML에서 발생된 event는 code-behind 파일의 event handler에서 처리 될 수 있다는 것과 XAML과 code-behind file간 interaction은 XAML에서 명시된 x:Name attribute를 통해서 해당 object에 접근할 수 있다는 것이 interaction의 주요 내용으로 보임.

한가지 팁은 XamlPlugCode.xaml.g.cs 에서 x:Name attribute의 object를 private field로 지정해서 사용할 수 있는 점이다.
public partial class XamlPlusCodePage : ContentPage {

    private Label valueLabel;

    private void InitializeComponent() {
        this.LoadFromXaml(typeof(XamlPlusCodePage));
        valueLabel = this.FindByName<Label>("valueLabel");
    }
}
이런 경우 valueLevel을 접근하기 위해 private field를 언제든 사용가능하다는 것과 접근할 때 마다 parsing이 필요 없다는 점이 장점이다.