イベント ドリブン UI とデータ バインド UI を比較する
- 6 分
イベント ドリブン ユーザー インターフェイス (UI) は、コントロールが公開するイベントを中心に設計されています。 これらのイベントは、イベントがトリガーされたときに呼び出されるイベント ハンドラー コードに関連付けることができます。 たとえば、クリックすると実行時間の長い操作を実行するボタンがあるとします。
Clicked イベントに割り当てられたイベント ハンドラーでは、操作を開始した後、ボタンの IsEnabled プロパティを false に設定して、操作の実行中にボタンが再度クリックされないようにすることができます。
データ バインド UI は、データ バインドを使って、データの表示と操作を行います。 コントロールのプロパティはデータ オブジェクトのプロパティにバインドされ、それらのバインドによりプロパティの変更を検出できます。 前の例を使って、長い時間がかかる操作を実行するボタンについて考えます。 コードビハインドでボタンを無効にする代わりに、IsEnabled プロパティがデータ オブジェクトの IsBusy プロパティにバインドされます。 データ オブジェクトが "ビジー" になると、ボタンの有効な状態が自動的に一致するように変更されます。
イベントとコードビハインドの使用の長所と短所
コード ビハインドでコントロールのイベント ハンドラーを使うことは、UI 用のアプリ ロジックを設計する簡単で便利な方法です。 コードを使って、サービスを呼び出してデータを取得し、そのデータに対する操作を実行して、ページ上のコントロールを操作します。 コードを使って、UI とデータの同期を維持します。
気象サービス アプリの例を考えてみます。 次の XAML フラグメントには、ユーザーが選ぶと最新のデータを取得して湿度で UI を更新する簡単な UI ボタンが含まれています。
<VerticalStackLayout Margin="10">
<HorizontalStackLayout Spacing="20">
<Label Text="Postal Code:" VerticalOptions="Center" />
<Entry x:Name="PostalCode" WidthRequest="100" />
<Button x:Name="RefreshWeatherButton" Text="Refresh" WidthRequest="200" Clicked="RefreshWeatherButton_Clicked" />
</HorizontalStackLayout>
<Label x:Name="Humidity" Text="Humidity: ?" />
</VerticalStackLayout>
この例には、3 つの名前付きコントロールがあります。
-
Entryという名前の コントロール。 -
Buttonという名前の コントロール。 -
Labelという名前の コントロール。
RefreshWeatherButton には、Clicked イベントに対して宣言されたイベント ハンドラーがあります。 ボタンがクリックされると、イベント ハンドラーは、 PostalCode エントリ コントロールに入力されたデータを使用して最新の天気予報の天気予報サービスを照会し、 Humidity ラベルのテキストを現在の湿度に設定します。
private void RefreshWeatherButton_Clicked(object sender, EventArgs e)
{
WeatherService.Location = PostalCode.Text;
WeatherService.Refresh();
Humidity.Text = $"Humidity: {WeatherService.Humidity}";
}
この 1 つのイベント ハンドラーで、3 つのコントロールがコードビハインドを通じて相互に、そしてデータと、密結合されます。
この設計は小さな UI では問題なく動作しますが、UI が複雑になるとたちまち、密結合されたコードビハインドを維持するのが難しくなり得ます。 コントロールを削除または変更する場合は、それらの UI コントロールを使っているコードをクリーンアップする必要があり、これにはイベント ハンドラーが含まれる場合があります。 UI を再設計する場合は、リファクタリングするコードも多数あります。 そして、基になっているデータ構造が変更されたら、同期を保つために各 UI のコードを詳しく調べる必要があります。
データ バインドが役に立つ
データ バインディングは XAML またはコードで実装できますが、XAML でははるかに一般的で、分離コード ファイルのサイズを小さくするのに役立ちます。 イベント ハンドラーの手続き型コードを宣言型コードまたはマークアップに置き換えると、アプリが簡略化され、明確になります。 バインドにコードビハインドは必要ないため、データの表示方法に合わせて UI を簡単に作成、変更、または再設計できます。
前のセクションと同じ例を、データ バインドを使うように更新してみましょう。
<VerticalStackLayout Margin="10">
<HorizontalStackLayout Spacing="20">
<Label Text="Postal Code:" VerticalOptions="Center" />
<Entry Text="{Binding Location, Mode=OneWayToSource}" WidthRequest="100" />
<Button Text="Refresh" Command="{Binding RefreshWeather}" WidthRequest="200" />
</HorizontalStackLayout>
<Label Text="{Binding Humidity}" />
</VerticalStackLayout>
データ バインドされているプロパティがわかります。それらでは、プロパティの値に {Binding ...} という XAML 拡張構文が使われています。 詳細はまだ心配しないでください。これについては、このモジュールの後半で説明します。
XAML では同じ 3 つのコントロールが宣言されていますが、名前は必要ないため、名前は付けられません。
Entryコントロール: このコントロールのTextプロパティは、Locationという名前のプロパティにバインドされます。Buttonコントロール: ボタンのCommandプロパティは、RefreshWeatherという名前のプロパティにバインドされます。Commandは、ボタンが押されたときにコードを呼び出すボタンのプロパティです。 これは、データ バインドで使われるClickedイベントの代わりです。Labelコントロール: このTextプロパティは、Humidityという名前のプロパティにバインドされます。
この簡単な UI で、すべてのコードビハインドが不要になります。 データバインディングの目的は、コードビハインドを削除することではありません。たとえ可能であっても。 コードビハインドにはさらにそれ自体に意味があります。 どれだけの量のデータ バインドを実装するかはユーザー次第です。
これで、UI はデータ オブジェクトに疎結合されます。 密結合ではなく疎結合であるのはなぜでしょうか。 それはバインドの評価方法のためです。 各コントロールには BindingContext プロパティがあります。 コンテキストが設定されていない場合は、親コントロールのコンテキストが使われ、XAML のルートが評価されるまでそれが繰り返されます。 バインドが評価されるときは、コンテキストのオブジェクト インスタンスで必要なプロパティがチェックされます (コンテキストの Text プロパティに対するラベル コントロールの Humidity バインドなど)。 コンテキストに Humidity が存在しない場合は、何も行われません。
UI は疎結合されているため、コードの中断を気にせずに UI を再設計できます。 ただし、機能が損なわれることはあり得ます。 たとえば、ボタンを削除してもアプリはコンパイルされて実行されますが、天気を更新する方法はなくなります。 一方で、Entry と Button コントロールを、単一の SearchBar コントロールに置き換えることができます。 このコントロールを使うと、テキストを入力してコマンドを呼び出すことができます。
<SearchBar Text="{Binding Location, Mode=OneWayToSource}" SearchCommand="{Binding RefreshWeather}" />
ご覧のように、UI の設計でデータ バインドを使うと、多くの作業を行わずに UI を進化させ、変更するのに役立ちます。 UI とデータの同期が自動的に維持され、アプリ ロジックは UI から分離されます。