こんにちは。エクセルソフトの田淵です。
まぁあれです。IT勉強会検索アプリを作成してまして。関東 Switch をトグルすると 東京、神奈川〜 みたいに関東全件をトグルする。とかそういうことがしたかったわけです。
Xamarin.Formsのデータバインディングについてです。例えば一つの元スイッチをトグルすると複数の先スイッチが纏めてトグルされるのをやりたいです。元スイッチに複数のBindingを設定できないっぽいんですがどうやるのが良いんでしょうか?
— 田淵 義人@エクセルソフト (@ytabuchi) 2015, 7月 9
呟いたところ、有識者の皆さんが寄ってたかって色々考えて教えてくださいまして。本当にありがたいです涙 @yakumomo さんは Qiita エントリー まで書いてくださいました。ありがとうございます!
色々なやり方がありますが、各 Switch の値は ViewModel に保存しておきたいので、プロパティのところでイジるのが良いのではというところに落ち着きました。
画面写真
View
Xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:vm="clr-namespace:XF_ManySwitches;assembly=XF_ManySwitches" x:Class="XF_ManySwitches.SwitchPageXaml" Title="SwitchPage Xaml"> <ContentPage.Padding> <OnPlatform x:TypeArguments="Thickness" iOS="0,20,0,0" Android="0" WinPhone="0" /> </ContentPage.Padding> <ContentPage.BindingContext> <vm:SwitchPageViewModel /> </ContentPage.BindingContext> <ContentPage.Content> <TableView Intent="Menu"> <TableRoot> <TableSection Title="Toggle all"> <SwitchCell Text="Toggle sw1 & sw2" On="{Binding SwAllValue, Mode=OneWayToSource}" /> </TableSection> <TableSection Title="Toggle each"> <SwitchCell Text="Toggle sw1" On="{Binding Sw1Value, Mode=TwoWay}" /> <SwitchCell Text="Toggle sw2" On="{Binding Sw2Value, Mode=TwoWay}" /> </TableSection> <TableSection Title="Values of ViewModel"> <TextCell Text="sw1 value" Detail="{Binding Sw1Value}" /> <TextCell Text="sw2 value" Detail="{Binding Sw2Value}" /> </TableSection> </TableRoot> </TableView> </ContentPage.Content> </ContentPage>
C#
SwitchPageViewModel vm = new SwitchPageViewModel(); BindingContext = vm; var sw0 = new SwitchCell { Text = "Toggle sw1 & sw2" }; sw0.SetBinding(SwitchCell.OnProperty, "SwAllValue", BindingMode.OneWayToSource); var sw1 = new SwitchCell { Text = "Toggle sw1" }; sw1.SetBinding(SwitchCell.OnProperty, "Sw1Value", BindingMode.TwoWay); var sw2 = new SwitchCell { Text = "Toggle sw2" }; sw2.SetBinding(SwitchCell.OnProperty, "Sw2Value", BindingMode.TwoWay); var tc1 = new TextCell { Text = "sw1 value" }; tc1.SetBinding(TextCell.DetailProperty, "Sw1Value"); var tc2 = new TextCell { Text = "sw2 value" }; tc2.SetBinding(TextCell.DetailProperty, "Sw2Value"); var tv = new TableView { Intent = TableIntent.Menu, Root = new TableRoot { new TableSection("Toggle all") { sw0, }, new TableSection("Toggle each") { sw1, sw2, }, new TableSection("Values of ViewModel") { tc1, tc2, } } }; Content = new StackLayout { Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0), Children = { tv, } };
ViewModel
bool _sw1Value; public bool Sw1Value { get { return _sw1Value; } set { if (_sw1Value != value) { _sw1Value = value; OnPropertyChanged("Sw1Value"); } } } bool _sw2Value; public bool Sw2Value { get { return _sw2Value; } set { if (_sw2Value != value) { _sw2Value = value; OnPropertyChanged("Sw2Value"); } } } bool _swAllValue; public bool SwAllValue { get { return _swAllValue; } set { if (_swAllValue != value) { _swAllValue = value; OnPropertyChanged("SwAllValue"); } if (_sw1Value != value) { _sw1Value = value; OnPropertyChanged("Sw1Value"); } if (_sw2Value != value) { _sw2Value = value; OnPropertyChanged("Sw2Value"); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
ポイント
- ViewModel の一括で変更する「変更元 Switch」側で変更したい Switch の value を一緒にチェックして変更するようにします。
- 変更元 Switch では BindingMode を OneWayToSource として ViewModel に伝えるだけにします。
これで「変更先 Switch」を自由に変更しつつ、変更元 Switch をトグルした時だけ一括で変更できます。
今回は Switch ですが、何かのヒントになれば嬉しいです。
なお、Xamarin.Forms の Xaml ですが、{Binding の後のプロパティに IntelliSense が効かず、何を Binding すれば良いかイマイチ分かりません。コードは凄い冗長になってしまいますが、C# で書くと IntelliSense で何を Binding するのか分かるので、先に C# で書いてみるのが良いかもしれません。
Xamarin 気になった方は
Mvvm の勉強にも最適な Xamarin.Forms、イイですね!
是非 ダウンロード(直接) / ダウンロード(弊社経由) して触ってみてください。 学習用リソース や JXUG リンクページ に参考資料を纏めてますので併せてどうぞ。
Xamarin の情報が欲しい方はこのブログも購読いただいたりすると嬉しいです。
以上です。