XamarinのXAML基礎勉強メモ1

# Xamarin勉強メモ
仕事でXamarinを使用するので、勉強メモとして残しておく。

## XAMLとは
Xamarin.Formsのページを作成するのに使われる。

## まずはラベルのテキスト設定
```
<?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="HelloWorld.MyPage"
             Title="Hello world">
   <ContentPage.Content>
     <Label Text="Hello world" />  // ここでラベルを作成し、Textプロパティに値を設定している
   </ContentPage.Content>
</ContentPage>
```
上記の例の<ContentPage.Content>箇所は省略可能。

## StackLayoutとボタンクリックイベント
StackLayoutは複数のコントロールを縦や横に並べるコントロールのこと。
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="HelloWorld.MyPage"
             Title="Hello world">
   <StackLayout>
     <Label Text="Hello world" />
     <Button Text="Click me" 
             Clicked="OnClicked" /> // クリックイベント
   </StackLayout>
</ContentPage>
```
次にイベントの設定方法
今回はボタンのクリックイベントをC#側に実装する例とする。

```
using System;
using Xamarin.Forms;
namespace HelloWorld
{
 　　public partial class MyPage : ContentPage
 　　{
 　　　　public MyPage()
 　　   {
          InitializeComponent();
       }
 
       private void OnClicked(object sender, EventArgs args)
       {
         // ここに処理を書く
       }
    }
}
```

## Grid
Gridコントロールは画面を縦横で分割して、そこにコントロールを配置できます。
行はRowDefinitionsプロパティで定義し、列はColumnDefinitionsプロパティで定義する。

```
//Gridのみを抜粋
<Grid> 
  // 行を３分割にしている
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>

  // 列を３分割にしている
  <Grid.ColumnDefinitions>
    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>

  // 1×1のグリッドに配置
  <Button Text="0,0" />

  // 2×2のグリッドに配置
  <Button Text="1,1"
          Grid.Row="1"
          Grid.Column="1" />
  // 3×3のグリッドに配置
  <Button Text="2,2"
          Grid.Row="2"
          Grid.Column="2" />
</Grid>
```

## StaticResource 共通定義
StaticResourceの設定方法

```
<ContentPage.Resources>
   <ResourceDictionary>
     // x:Keyで設定した名前でアクセスできる
     <x:String x:Key="text">Hello world</x:String>
   </ResourceDictionary>
</ContentPage.Resources>

  // x:Keyで設定した名前でデータを取得
  <Label Text="{StaticResource text}"
         HorizontalOptions="Center"
         VerticalOptions="Center" />
```

## C#側クラスのstaticメンバを呼び出す方法

```
namespace HelloWorld
{
  public static class StaticItem
  {
    public static string Message { get; } = "Hello static world";
  }
}
```
```
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             // staticクラスのnamespaceを設定
             xmlns:local="clr-namespace:HelloWorld;assembly=HelloWorld"
             x:Class="HelloWorld.MyPage"
             Title="Hello world">

  // x:Stackで取得する
  <Label Text="{x:Static local:StaticItem.Message}"
         HorizontalOptions="Center"
         VerticalOptions="Center" />
```

## データバインディング
データバインディングはソースとターゲットの間の同期をとるために使用される。

### コントロール同士のデータバインディング

```
 <StackLayout VerticalOptions="Center">
   <Slider x:Name="slider"
           Maximum="100"
           Minimum="0"
           VerticalOptions="StartAndExpand" />
   // SliderコントロールのValueプロパティとLabelのTextプロパティを同期している
   <Label Text="{Binding Value, Source={x:Reference slider}}"
          HorizontalOptions="Center" />
 </StackLayout>
```
x:Referenceで名前付きのコントロールのインスタンスを取得できるので、それを使用してLabelのTextプロパティとSliderのValueプロパティを同期しています。

データバインディングにはModeプロパティがあり、データバインディングの同期方向をカスタマイズすることができる。

* Default: バインドしているターゲットのプロパティに指定されたデフォルトの値が使用される。
* OneWay: ソースからターゲットへの一方通行で同期される。
* OneWayToSource: ターゲットからソースへの一方通行で同期される。
* TwoWay: ソースとターゲットの双方向で同期される。

## BindingContextについて
BindingContextはBindableObjectクラスに定義されているプロパティになる。
BindingContextに設定するオブジェクトはINotifyPropertyChangedインターフェースを実装する。
INotifyPropertyChangedのPropertyChangedイベントを監視して、データ変更の検知を行い、ターゲットの更新を行います。
まずは、INotifyPropertyChangedの実装定義をしておいて、それを継承していく方法を使います。

```
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace HelloWorld
{
  public class BindableBase : INotifyPropertyChanged
  {
    protected BindableBase()
    {
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
      this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
      if (object.Equals(field, value)) { return false; }

      field = value;
      this.OnPropertyChanged(propertyName);
      return true;
    }
  }
}
```

```
namespace HelloWorld
{
  public class MyPageViewModel : BindableBase
  {
    private double sliderValue;
    public double SliderValue
    {
      get { return this.sliderValue; }
      set { this.SetProperty(ref this.sliderValue, value); this.OnPropertyChanged(nameof(LabelValue)); }
    }
 
    public string LabelValue => string.Format("This is slider value '{0:000}'", this.SliderValue);
 }
}
```

```
<?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:HelloWorld"
             x:Class="HelloWorld.MyPage">
  <ContentPage.BindingContext>
    <local:MyPageViewModel />
  </ContentPage.BindingContext>
  <StackLayout VerticalOptions="Center">
    <Slider Maximum="100"
            Minimum="0"
            HorizontalOptions="Fill"
            Value="{Binding SliderValue}" />
    <Label Text="{Binding LabelValue}"
           HorizontalOptions="Fill" />
  </StackLayout>
</ContentPage>
```

## コレクションのデータバインディング
ListViewというコントロールを使用してコレクションをバインディングすることが可能です。
コレクションのバインディングにはItemsSourceプロパティにコレクションをデータバインドすることで行います。
その際にコレクションの要素の増減に対応するにはINotifyCollectionChangedインターフェースを実装して、CollectionChangedイベントを発行する必要がある。
このような条件があるため、可変コレクションの場合はObservableCollection<T>というINotifyCollectionChangedインターフェースを実装する。
可変でない場合はIEnumerable<T>インターフェースでもList<T>でも問題ない。

```
namespace HelloWorld
{
  public class Person
  {
    public string Name { get; set; }
  }
}
```

```
using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace HelloWorld
{
  public class MyPageViewModel : BindableBase
  {
    public ObservableCollection<Person> People { get; } = new ObservableCollection<Person>();
    public MyPageViewModel()
    {
      var r = new Random();
      Device.StartTimer(
        TimeSpan.FromSeconds(5),
        () =>
        {
          this.People.Add(new Person { Name = $"tanaka {r.Next()}" });
          return true;
        });
    }
  }
}
```

```
<?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:HelloWorld"
             x:Class="HelloWorld.MyPage">
  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness">
      <On Platform="iOS">0,20,0,0</On>
    </OnPlatform>
  </ContentPage.Padding>
  <ContentPage.BindingContext>
    <local:MyPageViewModel />
  </ContentPage.BindingContext>
  // ItemsSourceにPeopleクラスをバインディングして
  <ListView ItemsSource="{Binding People}">
    // ItemTemplateのDateTemplateを設定しPeopleクラスのNameプロパティをバインディングする
    <ListView.ItemTemplate>
      <DataTemplate>
        <TextCell Text="{Binding Name}" />
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>
</ContentPage>
```

## 参考文献
[Xamarin.Forms入門](https://www.slideshare.net/okazuki0130/xamarinforms-70553057)