データバインディングも可能だ。「可能」というより、ListViewコントロールに不定個のデータを表示するような場合には、WPFなどと同様に必須となる技法である。また、本稿では説明しないが、コマンドのバインディングも使えば、コードビハインド(=ファイル名の末尾が「.xaml.cs」のファイル)からロジックを追い出せる。つまり、UIとロジックを分離できるのである。データバインディングは、規模の大きいアプリ開発にも必須の技術なのだ。
Xamarin.Formsのデータバインディングは、WPFやWindowsストアアプリなどのものと基本的には同じだ。ただし、バインディングソース(=データの提供元)を簡単に書けるように改良されている。
バインディングソースにするクラスは、WPFであれXamarin.Formsであれ、INotifyPropertyChangedインタフェース(System.ComponentModel名前空間)を実装しなければならない。しかしXamarin.Formsでは、それを実装したBindableObjectクラス(Xamarin.Forms名前空間)が用意されている*1。BindableObjectクラスを継承したクラスであれば、簡単にバインディングソースを記述できるのだ。
*1 BindableObjectクラスはWPFなどのDependencyObjectクラス(Windows.UI.Xaml名前空間)に相当する。ただし、DependencyObjectクラスはINotifyPropertyChangedインタフェースを実装していない。また、次のコードに登場するBindablePropertyクラスは、WPFなどのDependencyPropertyクラス(Windows.UI.Xaml名前空間)に相当するものだ。
それでは、先ほどの時刻表示を、データバインディングで行うように改良してみよう。
データを保持するクラスを新設するのが本来ではあるが、ここではあえてAppクラス(これはコードビハインドだ)に時刻データを持たせてみよう。AppクラスもBindableObjectクラスを継承しているので、このクラスにデータを持たせてデータバインディングを行うことが可能だ。これには「App.xaml.cs」ファイルを開き、次のコードのように記述する。
public partial class App : Application
{
// NowTimeプロパティ
public static readonly BindableProperty NowTimeProperty
= BindableProperty.Create(
nameof(NowTime), // プロパティ名
typeof(DateTimeOffset), // プロパティの型
typeof(App), // プロパティが定義されているクラス
defaultValue: DateTimeOffset.Now, // プロパティの初期値(オプション)
defaultBindingMode: BindingMode.OneWay // モードの既定値(オプション)
);
public DateTimeOffset NowTime
{
get { return (DateTimeOffset)GetValue(NowTimeProperty); }
set { SetValue(NowTimeProperty, value); }
}
// Currentプロパティを利用しやすくするため、Appを返す「Current」を追加
public static App CurrentApp => Current as App;
……省略……
上で作ったNowTimeプロパティ(バインディングソース)を画面のLabelコントロールにバインドするには、「MainPage.xaml」ファイルを開いて次のコードのように変更する。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
……省略……
BindingContext="{Binding Source={x:Static Application.Current}}"
>
<StackLayout VerticalOptions="Center">
<!--<Label x:Name="Label1" Text="Hello, Xamarin!" TextColor="Aqua"
FontSize="Large" FontAttributes="Bold, Italic"
HorizontalOptions="Center" />-->
<Label Text="{Binding NowTime, StringFormat='{0:HH:mm:ss}'}"
TextColor="Aqua"
FontSize="Large" FontAttributes="Bold, Italic"
HorizontalOptions="Center" />
<Button Text="Click me!" Clicked="Button_Clicked"
FontSize="Medium" HorizontalOptions="Center" />
</StackLayout>
</ContentPage>
最後に、ボタンをクリックしたときのイベントハンドラーでデータを書き換えるようにして完成だ(次のコード)。実行してみると、先ほどと同じように、ボタンをクリックしたときの時刻が表示されるはずだ。
private void Button_Clicked(object sender, EventArgs e)
{
//Label1.Text = DateTimeOffset.Now.ToString("HH:mm:ss");
App.CurrentApp.NowTime = DateTimeOffset.Now;
}
せっかくなので、時刻表示をタイマで自動的に更新するようにして、時計アプリにしてみよう。ボタンは、タイマ動作のON/OFFに使うこととする。
Xamarin.Formsで利用できるタイマは、Deviceクラス(Xamarin.Forms名前空間)のStartTimerメソッドだけのようである*2。
StartTimerメソッドには、タイマ割り込みごとに行いたい処理をラムダ式で与える。面白いのは、このラムダ式がtrueを返せばタイマ継続、falseを返すとタイマ終了となることだ。そのため、StopTimerというようなメソッドは用意されていない。
*2 Xamarin.Forms名前空間のタイマでは、Xamarin.Forms以外からも共通に使いたいライブラリでは使えない。そんなとき、筆者はRxのタイマを使う。
タイマ処理をON/OFFするメソッドは、次のコードのように書ける。
// タイマ動作を継続させるフラグ
private bool m_ContinueFlag;
// タイマ動作をON/OFFするメソッド
public void StartStopTimer()
{
if (m_ContinueFlag)
{
// タイマ処理を終了させる
m_ContinueFlag = false;
}
else
{
// タイマ処理を開始する
m_ContinueFlag = true;
// タイマで時刻をセットする
Device.StartTimer(TimeSpan.FromSeconds(1.0), () => {
NowTime = DateTimeOffset.Now;
return m_ContinueFlag;
});
}
}
ボタンのクリック時に上のメソッドを呼び出すようにしよう(次のコード)。アプリを起動してボタンをクリックするとタイマが動き出し、1秒ごとに時刻表示が更新されるようになる(もう一度ボタンをクリックするとタイマは止まる)。
private void Button_Clicked(object sender, EventArgs e)
{
//App.CurrentApp.NowTime = DateTimeOffset.Now;
App.CurrentApp.StartStopTimer();
}
Copyright© Digital Advantage Corp. All Rights Reserved.