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>
上記の例の箇所は省略可能。
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>
// 行を3分割にしている
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
// 列を3分割にしている
<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というINotifyCollectionChangedインターフェースを実装する。
可変でない場合はIEnumerableインターフェースでもListでも問題ない。
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>