Xamarin.Forms で複数の Switch を動かす Switch を作るには


こんにちは。エクセルソフトの田淵です。

> この記事は [Xamarin.Forms で複数の Switch を動かす Switch を作るには - Xamarin 日本語情報](http://ytabuchi.hatenablog.com/entry/2015/07/11/112735) の転載記事です。

まぁあれです。IT勉強会検索アプリを作成してまして。関東 Switch をトグルすると 東京、神奈川〜 みたいに関東全件をトグルする。とかそういうことがしたかったわけです。

<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">Xamarin.Formsのデータバインディングについてです。例えば一つの元スイッチをトグルすると複数の先スイッチが纏めてトグルされるのをやりたいです。元スイッチに複数のBindingを設定できないっぽいんですがどうやるのが良いんでしょうか？</p>&mdash; 田淵 義人＠エクセルソフト (@ytabuchi) <a href="https://twitter.com/ytabuchi/status/619069051067744257">2015, 7月 9</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

呟いたところ、有識者の皆さんが寄ってたかって色々考えて教えてくださいまして。本当にありがたいです涙 [@yakumomo](http://twitter.com/yakumomo) さんは [Qiita エントリー](http://qiita.com/yakumomo/items/502ad60ca9bcf615c128) まで書いてくださいました。ありがとうございます！

色々なやり方がありますが、各 Switch の値は ViewModel に保存しておきたいので、プロパティのところでイジるのが良いのではというところに落ち着きました。

# 画面写真
<img src="http://cdn-ak.f.st-hatena.com/images/fotolife/y/ytabuchi/20150711/20150711112530.png" width="150" /> <img src="http://cdn-ak.f.st-hatena.com/images/fotolife/y/ytabuchi/20150711/20150711112532.png" width="150" />


# View

## Xaml

```xml
<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 &amp; 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# 

```csharp
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

```cs
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# で書いてみるのが良いかもしれません。


# 簡単に Mvvm を使える Xamarin が気になった方は

是非 [ダウンロード(直接)](http://xamarin.com/download) / [ダウンロード(弊社経由)](https://www.xlsoft.com/jp/products/download/index.html?option=Xamarin&r=ythb) して触ってみてください。
[学習用リソース](http://ytabuchi.hatenablog.com/entry/2014/08/26/191759) や [JXUG リンクページ](http://jxug.org/links.html) に参考資料を纏めてますので併せてどうぞ。

<a href="http://blog.hatena.ne.jp/ytabuchi/ytabuchi.hatenablog.com/subscribe">Xamarin の情報が欲しい方はこのブログも購読いただいたりすると嬉しいです。</a>

以上です。