放浪軍師さんのコントロール配列の呪いを解きたいを見て、なんか頑張ってる姿に触発されたので、自分だったらこんな感じかなぁと言うのを試してみました。
Xamarin.Forms + Prism + ReactiveProperyのそれぞれ最新状態です。
構成
- MainPage.xaml : View
- MainPageViewModel : ViewModel
- Itemクラス : リスト項目のmodel
- ToggleButtonクラス : Buttonに選択状態のプロパティを拡張したもの
項目のモデルは名前と選択状態だけ保持しておいて、表示に関する制御はviewが行います。 リストはサンプルなのでViewModelで生成してしまっています。
MainPage.xaml
ポイントは、
- ボタンのCommandは、親となるContentPage(x:Name=“Base”)のBindingContext(=MainViewModel)にあるコマンドを指定する
- ボタンのテキストと背景の色は、StyleのTriggerを使って、ToggleButtonのIsSelectedプロパティによって変化させる
の2つです。 (サンプルで手抜きしてるので、ToggleButtonのコードがMainPageViewModelと同じファイルに書いてあります)
<?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:viewmodels="clr-namespace:BlankApp6.ViewModels"
x:Class="BlankApp6.Views.MainPage"
x:Name="Base"
Title="Main Page">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="ItemSelectedStyle" TargetType="viewmodels:ToggleButton">
<Setter Property="TextColor" Value="Black"/>
<Setter Property="BackgroundColor" Value="Pink"/>
<Style.Triggers>
<Trigger TargetType="viewmodels:ToggleButton" Property="IsSelected" Value="True">
<Setter Property="TextColor" Value="Red"/>
<Setter Property="BackgroundColor" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<viewmodels:ToggleButton Command="{Binding BindingContext.ItemTapped, Source={x:Reference Base}}"
CommandParameter="{Binding}"
Text="{Binding Name}"
IsSelected="{Binding IsSelected}"
Style="{StaticResource ItemSelectedStyle}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
MainViewModel
ポイントは特にないです。 ReactiveCommandはタップしたItemが引数としてきますので、それに対して操作をします。
public class MainPageViewModel
{
public ObservableCollection<Item> Items { get; }
public ReactiveCommand<Item> ItemTapped { get; }
public MainPageViewModel()
{
Items = new ObservableCollection<Item>
{
new Item { Name = "アイテム1" },
new Item { Name = "アイテム2" },
new Item { Name = "アイテム3" },
new Item { Name = "アイテム4" }
};
ItemTapped = new ReactiveCommand<Item>();
ItemTapped.Subscribe(x =>
{
x.IsSelected = !x.IsSelected;
});
}
}
Itemクラス
すごく単純化して、表示する名称と選択されたかどうかだけ管理しています。 多分裏側の処理では、これが選択されてるかどうかでルーレットの対象になるかどうか決める感じですかね。 表示に関する部分は、viewに任せてあるので、こいつはどうやって表示されるかは気にしません。
public class Item : BindableBase
{
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
private string name;
public bool IsSelected
{
get => isSelected;
set => SetProperty(ref isSelected, value);
}
private bool isSelected;
}
ToggleButtonクラス
ButtonにIsSelectedという選択状態を管理するbool型のプロパティを追加しただけのコントロールです。 この追加したプロパティとItemのIsSelectedをbindingして使うことになります。 で、viewに書いてあるstyleトリガがそのIsSelectedを見て色を変える、ということです。
public class ToggleButton : Button
{
public static readonly BindableProperty IsSelectedProperty =
BindableProperty.Create(
"IsSelected",
typeof(bool),
typeof(ToggleButton),
false);
public object IsSelected
{
get => GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
}
実行結果
iPhoneシミュレータでの動作です。