ItemsControl を利用して今度はリスト内にバインドされたデータの型によって、各行の表示を切り替えてみたいと思います
今回のソリューション構成はこちら
ViewModel を 2つと、TypeTemplateSelector という DataTemplateSelector を追加しました
TypeTemplateSelector は次のような感じです
/// <summary> /// データソースの型によってテンプレートを選択する DataTemplateSelector /// </summary> public class TypeTemplateSelector : DataTemplateSelector { /// <summary> /// デフォルトのテンプレート /// </summary> public DataTemplate DefaultTemplate { get; set; } /// <summary> /// 文字列アイテム用のテンプレート /// </summary> public DataTemplate TextItemTemplate { get; set; } /// <summary> /// 画像アイテム用のテンプレート /// </summary> public DataTemplate ImageItemTemplate { get; set; } /// <summary> /// テンプレートを選択する /// </summary> /// <param name="item">アイテムのデータソース</param> /// <param name="container">アイテムのコンテナ</param> /// <param name="index">アイテムのインデックス</param> /// <returns>選択されたテンプレート</returns> public override DataTemplate SelectTemplate(object item, BindableObject container, int? index = null) { var template = this.DefaultTemplate; // データの型ごとにテンプレートを変える switch (item.GetType().Name) { case @"TextItemViewModel": return this.TextItemTemplate ?? this.DefaultTemplate; case @"ImageItemViewModel": return this.ImageItemTemplate ?? this.DefaultTemplate; } return this.DefaultTemplate; } }
渡された item の型によって選択する DataTemplate を切り替えています
そして ViewModel
/// <summary> /// トップ画面の ViewModel /// </summary> public class TopPageViewModel : BindableBase { /// <summary> /// アイテム /// </summary> private ObservableCollection<object> items; /// <summary> /// アイテム /// </summary> public ObservableCollection<object> Items { get { return this.items; } set { this.SetProperty<ObservableCollection<object>>(ref this.items, value); } } /// <summary> /// コンストラクタ /// </summary> public TopPageViewModel() { this.items = new ObservableCollection<object>() { new TextItemViewModel() { Title = "ラグドール", }, new ImageItemViewModel() { Title = "ノルウェー・ジャン・フォレストキャット", Source = @"http://upload.wikimedia.org/wikipedia/commons/8/87/Norwegische_Waldkatze.jpg", }, new TextItemViewModel() { Title = "メインクーン", }, new ImageItemViewModel() { Title = "アメリカン・ショートヘア", Source = "http://upload.wikimedia.org/wikipedia/ja/5/50/Americanshorthair.JPG", }, "スコティッシュフォールド", new ImageItemViewModel() { Title = "マンチカン", Source = @"http://upload.wikimedia.org/wikipedia/commons/6/6e/Longhairedmunchkin.jpg", }, }; } }
Items の型を object のコレクションにして好き放題 ViewModel (実は string 直接もあったり)を突っ込んじゃってます
共通のデータがある場合は Inteface なり継承なりをジェネリックの型にしてば制約をつければいいです
最後に XAML
<?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:b="clr-namespace:XamarinControl.Behaviors;assembly=XamarinControl" xmlns:c="clr-namespace:XamarinControl.Controls;assembly=XamarinControl" xmlns:s="clr-namespace:XamarinControl.Selectors;assembly=XamarinControl" xmlns:vm="clr-namespace:XamarinControl.ViewModels;assembly=XamarinControl" x:Class="XamarinControl.Views.TopPage"> <ContentPage.BindingContext> <vm:TopPageViewModel/> </ContentPage.BindingContext> <c:ItemsControl ItemsSource ="{Binding Items}"> <c:ItemsControl.ItemTemplateSelector> <s:TypeTemplateSelector> <!-- TextItemViewModel の場合 --> <s:TypeTemplateSelector.TextItemTemplate> <DataTemplate> <Grid Padding="20,10"> <Label Text="{Binding Title}"/> </Grid> </DataTemplate> </s:TypeTemplateSelector.TextItemTemplate> <!-- ImageItemViewModel の場合 --> <s:TypeTemplateSelector.ImageItemTemplate> <DataTemplate> <Grid Padding="20,10" HeightRequest="80"> <Grid.ColumnDefinitions> <ColumnDefinition Width="80"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Image Grid.Column="0" Source="{Binding Source}" Aspect="AspectFit" HorizontalOptions="Start"/> <Label Grid.Column="1" Text="{Binding Title}"/> </Grid> </DataTemplate> </s:TypeTemplateSelector.ImageItemTemplate> <!-- その他の場合 --> <s:TypeTemplateSelector.DefaultTemplate> <DataTemplate> <Grid Padding="20,10"> <Label Text="{Binding}" TextColor="Red"/> </Grid> </DataTemplate> </s:TypeTemplateSelector.DefaultTemplate> </s:TypeTemplateSelector> </c:ItemsControl.ItemTemplateSelector> </c:ItemsControl> </ContentPage>
ItemTemplateSelector に各データごとに DataTemplate を定義しています
さてこれで実行すると・・・
1つのリスト内でバラバラな表示ができてますね・・・まさにポリモーフィズム!
標準の ListView だとこれができないかったり