Xamarin.Forms のバインド可能なレイアウト
バインド可能なレイアウトを使用すると、Layout<T>
クラスから派生した任意のレイアウト クラスで、項目のコレクションにバインドしてコンテンツを生成すること、また、オプションで DataTemplate
を使って各項目の外観を設定することができます。 バインド可能なレイアウトは BindableLayout
によって提供されます。このクラスは、次の添付プロパティを公開します。
ItemsSource
: レイアウトによって表示されるIEnumerable
項目のコレクションを指定します。ItemTemplate
: レイアウトで表示される項目のコレクション内の、各項目に適用するDataTemplate
を指定します。ItemTemplateSelector
: 実行時に項目のDataTemplate
を選択するために使用するDataTemplateSelector
を指定します。
Note
たとえば、ItemTemplate
と ItemTemplateSelector
の両方のプロパティを設定した場合、ItemTemplate
プロパティが優先されます。
さらに、BindableLayout
クラスは次のバインド可能なプロパティを公開します。
EmptyView
:ItemsSource
プロパティがnull
の場合、またはItemsSource
プロパティで指定されたコレクションがnull
または空の場合、表示されるstring
またはビューを指定します。 既定値はnull
です。EmptyViewTemplate
:ItemsSource
プロパティがnull
の場合、またはItemsSource
プロパティで指定されたコレクションがnull
または空の場合、表示されるDataTemplate
を指定します。 既定値はnull
です。
Note
たとえば、EmptyView
と EmptyViewTemplate
の両方のプロパティを設定した場合、EmptyViewTemplate
プロパティが優先されます。
これらのプロパティはすべて、AbsoluteLayout
、FlexLayout
、Grid
、RelativeLayout
、StackLayout
クラスにアッタッチできます。これらはすべて Layout<T>
クラスから派生します。
Layout<T>
クラスは Children
コレクションを公開しており、それにレイアウトの子要素が追加されます。 BindableLayout.ItemsSource
プロパティが項目のコレクションに設定され、Layout<T>
-derived クラスにアタッチされると、コレクション内の各項目がレイアウトによって表示されるように Layout<T>.Children
コレクションに追加されます。 その後、基になるコレクションが変更されると、Layout<T>
から派生したクラスによって子のビューが更新されます。 Xamarin.Forms レイアウト サイクルの詳細については、「カスタム レイアウトの作成」を参照してください。
バインド可能なレイアウトは、表示する項目のコレクションが小さく、スクロールと選択が必要ない場合にのみ使用してください。 ScrollView
ではスクロールはバインド可能なレイアウトをラップすることによって提供できますが、バインド可能なレイアウトには UI 仮想化がないため、これはお勧めしません。 スクロールが必要な場合は、UI 仮想化を含むスクロール可能なビュー (CollectionView
または ListView
など) を使用する必要があります。 この推奨事項を守らないと、パフォーマンスの問題が発生する可能性があります。
重要
バインド可能なレイアウトを、Layout<T>
クラスから派生するレイアウト クラスにアタッチすることは技術的には可能ですが、特に、AbsoluteLayout
、Grid
、RelativeLayout
クラスに対しては、そうすることが必ずしも実用的であるとは限りません。 たとえば、バインド可能なレイアウトを使用して Grid
のデータのコレクションを表示するシナリオを考えてみましょう。この場合、コレクション内の各項目は複数のプロパティを含むオブジェクトです。 Grid
の各行にはコレクションのオブジェクトが表示され、Grid
の各列にはオブジェクトのプロパティの 1 つが表示されます。 バインド可能なレイアウトの DataTemplate
には 1 つのオブジェクトしか含めることができないため、そのオブジェクトは、特定の Grid
列にオブジェクトのプロパティの 1 つをそれぞれ表示する複数のビューを含むレイアウト クラスである必要があります。 このシナリオはバインド可能なレイアウトで実現できますが、バインドされたコレクション内の各アイテムの子 Grid
を含む親 Grid
が作成されることになります。これは、Grid
レイアウトの非常に非効率的で問題のある使用方法です。
バインド可能なレイアウトへのデータの設定
バインド可能なレイアウトには、ItemsSource
プロパティを IEnumerable
を実装するコレクションに設定し、Layout<T>
派生クラスにアタッチすることでデータが設定されます。
<Grid BindableLayout.ItemsSource="{Binding Items}" />
同等の C# コードを次に示します。
IEnumerable<string> items = ...;
var grid = new Grid();
BindableLayout.SetItemsSource(grid, items);
BindableLayout.ItemsSource
添付プロパティがレイアウトに設定されているが、BindableLayout.ItemTemplate
添付プロパティが設定されていない場合、IEnumerable
コレクション内のすべての項目は、BindableLayout
クラスによって作成された Label
によって表示されます。
項目の外観を定義
バインド可能なレイアウト内の各項目の外観は、添付プロパティ DataTemplate
を BindableLayout.ItemTemplate
に設定することで定義できます。
<StackLayout BindableLayout.ItemsSource="{Binding User.TopFollowers}"
Orientation="Horizontal"
...>
<BindableLayout.ItemTemplate>
<DataTemplate>
<controls:CircleImage Source="{Binding}"
Aspect="AspectFill"
WidthRequest="44"
HeightRequest="44"
... />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
同等の C# コードを次に示します。
DataTemplate circleImageTemplate = ...;
var stackLayout = new StackLayout();
BindableLayout.SetItemsSource(stackLayout, viewModel.User.TopFollowers);
BindableLayout.SetItemTemplate(stackLayout, circleImageTemplate);
この例では、TopFollowers
コレクション内のすべての項目は、DataTemplate
で定義されている CircleImage
ビューによって表示されます。
データ テンプレートの詳細については、「Xamarin.Forms のデータ テンプレート」を参照してください。
実行時にアイテムの外観を選択
バインド可能なレイアウト内の各項目の外観は、BindableLayout.ItemTemplateSelector
添付プロパティを DataTemplateSelector
に設定することで、項目の値に基づいて実行時に選択できます。
<FlexLayout BindableLayout.ItemsSource="{Binding User.FavoriteTech}"
BindableLayout.ItemTemplateSelector="{StaticResource TechItemTemplateSelector}"
... />
同等の C# コードを次に示します。
DataTemplateSelector dataTemplateSelector = new TechItemTemplateSelector { ... };
var flexLayout = new FlexLayout();
BindableLayout.SetItemsSource(flexLayout, viewModel.User.FavoriteTech);
BindableLayout.SetItemTemplateSelector(flexLayout, dataTemplateSelector);
サンプル アプリケーションで使用されている DataTemplateSelector
を次の例に示します。
public class TechItemTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultTemplate { get; set; }
public DataTemplate XamarinFormsTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
return (string)item == "Xamarin.Forms" ? XamarinFormsTemplate : DefaultTemplate;
}
}
TechItemTemplateSelector
クラスは、異なるデータ テンプレートに設定される DefaultTemplate
および XamarinFormsTemplate
DataTemplate
プロパティを定義します。 OnSelectTemplate
メソッドは、項目が "Xamarin.Forms" と等しい場合に XamarinFormsTemplate
を返します。これは、横にハートが付いた濃い赤色の項目を表示します。 項目が "Xamarin.Forms" と等しくない場合、OnSelectTemplate
メソッドは DefaultTemplate
を返します。これは、Label
の既定の色を使用して項目を表示します。
データ テンプレート セレクターの詳細については、「Xamarin.Forms DataTemplateSelector の作成」を参照してください。
データが使用できないときに文字列を表示
EmptyView
プロパティは文字列に設定できます。これは、ItemsSource
プロパティが null
の場合、または ItemsSource
プロパティで指定されたコレクションが null
または空の場合、Label
によって表示されます。 次の XAML は、このシナリオの例を示しています。
<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}"
BindableLayout.EmptyView="No achievements">
...
</StackLayout>
その結果、データ バインドされたコレクションが null
の場合、EmptyView
プロパティ値として設定された文字列が表示されます。
データが使用できない場合にビューを表示する
EmptyView
プロパティはビューに設定できます。このビューは、ItemsSource
プロパティが null
の場合、または ItemsSource
プロパティで指定されたコレクションが null
または空の場合に表示されます。 単一のビューでも、複数の子ビューを含むビューでも表示できます。 次の XAML の例は、複数の子ビューを含むビューに設定された EmptyView
プロパティを示しています。
<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
<BindableLayout.EmptyView>
<StackLayout>
<Label Text="None."
FontAttributes="Italic"
FontSize="{StaticResource smallTextSize}" />
<Label Text="Try harder and return later?"
FontAttributes="Italic"
FontSize="{StaticResource smallTextSize}" />
</StackLayout>
</BindableLayout.EmptyView>
...
</StackLayout>
その結果、データ バインドされたコレクションが null
の場合、StackLayout
とその子ビューが表示されます。
同様に、EmptyViewTemplate
を DataTemplate
に設定できます。これは、ItemsSource
プロパティが null
の場合、または ItemsSource
プロパティで指定されたコレクションが null
または空の場合に表示されます。 DataTemplate
には、単一のビュー、または複数の子ビューを含むビューを含めることができます。 さらに、EmptyViewTemplate
の BindingContext
は、BindableLayout
の BindingContext
から継承されます。 次の XAML の例は、単一のビューを含む DataTemplate
に設定された EmptyViewTemplate
プロパティを示しています。
<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
<BindableLayout.EmptyViewTemplate>
<DataTemplate>
<Label Text="{Binding Source={x:Reference usernameLabel}, Path=Text, StringFormat='{0} has no achievements.'}" />
</DataTemplate>
</BindableLayout.EmptyViewTemplate>
...
</StackLayout>
その結果、データ バインドされたコレクションが null
の場合、DataTemplate
内の Label
が表示されます。
Note
EmptyViewTemplate
プロパティは DataTemplateSelector
を介して設定できません。
実行時に EmptyView を選択する
データを使用できない場合に EmptyView
として表示されるビューは、ResourceDictionary
内の ContentView
オブジェクトとして定義できます。 EmptyView
プロパティは、実行時にビジネス ロジックに基づいて特定の ContentView
に設定できます。 次の XAML は、このシナリオの例を示しています。
<ContentPage ...>
<ContentPage.Resources>
...
<ContentView x:Key="BasicEmptyView">
<StackLayout>
<Label Text="No achievements."
FontSize="14" />
</StackLayout>
</ContentView>
<ContentView x:Key="AdvancedEmptyView">
<StackLayout>
<Label Text="None."
FontAttributes="Italic"
FontSize="14" />
<Label Text="Try harder and return later?"
FontAttributes="Italic"
FontSize="14" />
</StackLayout>
</ContentView>
</ContentPage.Resources>
<StackLayout>
...
<Switch Toggled="OnEmptyViewSwitchToggled" />
<StackLayout x:Name="stackLayout"
BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
...
</StackLayout>
</StackLayout>
</ContentPage>
XAML では、ページレベル ResourceDictionary
に 2 つの ContentView
オブジェクトが定義されており、Switch
オブジェクトは、どの ContentView
オブジェクトを EmptyView
プロパティ値として設定するかを制御します。 Switch
が切り替わると、OnEmptyViewSwitchToggled
イベント ハンドラーは ToggleEmptyView
メソッドを実行します。
void ToggleEmptyView(bool isToggled)
{
object view = isToggled ? Resources["BasicEmptyView"] : Resources["AdvancedEmptyView"];
BindableLayout.SetEmptyView(stackLayout, view);
}
ToggleEmptyView
メソッドは、Switch.IsToggled
プロパティの値に基づいて、stackLayout
オブジェクトの EmptyView
プロパティを、ResourceDictionary
に格納されている 2 つの ContentView
オブジェクトのいずれかに設定します。 次に、データ バインド コレクションが null
の場合、EmptyView
プロパティとして設定された ContentView
オブジェクトが表示されます。