酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

ToggleSwitchを使ってOn/Offを制御する

Windows Phone Advent Calendar "ひとり" 2011」第7日目です。

引き続き、Windows Phone Toolkitのお話です。標準の設定アプリ等で多く使われているToggleSwitchの使い方についてご紹介します。

ToggleSwitchは、スイッチ部分を右スライドと左スライドでOn/Offの切り替えをするため、画面を左右にスライドさせてページを切り替えるPanoramaやPivotと相性が悪いです。

(おそらくリジェクトはされないレベルだとは思いますが)アプリケーション認定要件に親コンテナ(例:Pivot)でジェスチャー(例:左右フリック)を使用するときは、同じジェスチャーを使用するコントロール(ToggleSwitch)を使うのは推奨されないとあります。

PanoramaやPivot上でOn/Offの切り替えしたい場合は、チェックボックスを使うようにするのが定石のようです。*1 *2

ToggleSwitchを表示する

Silverlight for Windows Phone Toolkitのアセンブリへの参照追加しておきます。表示させるのは非常に簡単で任意の場所に以下のXAMLを追加します。

<toolkit:ToggleSwitch Content="コンテント" Header="ヘッダー" />

実行してみました。簡単ですがヘッダー、コンテント、スイッチ部分が表示されているのが分かります。

全体のXAMLです。

<phone:PhoneApplicationPage 
    x:Class="ToggleTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

     <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="SOFTBUILD" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="ToggleSwitch Test" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <toolkit:ToggleSwitch Content="コンテント" Header="ヘッダー" />
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>

Onにされた事を通知するCheckedイベント

RoutedEventArgs型のOriginalSourceプロパティをToggleSwitch型にキャストして、チェックの状態がどうなっているか調べています。チェックが付けられた状態をIsCheckedプロパティから取得しています。

private void ToggleSwitch_Checked(object sender, RoutedEventArgs e) {
    var toggleSwitch = e.OriginalSource as ToggleSwitch;
    if (toggleSwitch == null) {
        return;
    }

    var isChecked = toggleSwitch.IsChecked;
}

Offにされた事を通知するUncheckedイベント

senderをToggleSwitch型にキャストして、チェックの状態がどうなっているか状態を取得しています。

private void ToggleSwitch_Unchecked(object sender, RoutedEventArgs e) {
    var toggleSwitch = sender as ToggleSwitch;
    if (toggleSwitch == null) {
        return;
    }

    var isChecked = toggleSwitch.IsChecked;
}

データバインディングでOn/Offの状態を管理する

OnとOffの切り替えのイベントが発行されるので、MainPage.xaml.cs内にbool型のクラスプロパティを制御することも出来ますが、数が増えてくると管理が出来なくなってしまうのでデータバインディングしてまとめて管理してしまいましょう。

管理用のViewModelクラスを作成します。

    public class SwitchViewModel : INotifyPropertyChanged {

        private bool CheckedVal;
        public bool Checked {
            get { return CheckedVal; }
            set {
                if (value != CheckedVal) {
                    CheckedVal = value;
                    NotifyPropertyChanged("Checked");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName) {
            if (PropertyChanged != null) {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }   

View側でIsCheckedプロパティを変更とViewModel側でのCheckedプロパティの変更を相互に反映するためにバインディングのモードをTwoWayにしておきます。

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <toolkit:ToggleSwitch Content="コンテント" Header="ヘッダー" IsChecked="{Binding Checked, Mode=TwoWay}" />
        </Grid>

LayoutRoot.DataContextに先ほど作成したSwitchViewModel型のインスタンスを設定して終わりです。

using System.ComponentModel;
using System.Windows;
using Microsoft.Phone.Controls;

namespace ToggleTest {
    public partial class MainPage : PhoneApplicationPage {

        SwitchViewModel switchViewModel = new SwitchViewModel();
        public MainPage() {
            InitializeComponent();
            LayoutRoot.DataContext = switchViewModel;
        }
    }
}

*1:[http://stackoverflow.com/questions/3888947/wp7-toggle-switch-in-a-pivot-control:title]

*2:[http://stackoverflow.com/questions/6345463/is-a-toggle-switch-inside-a-pivot-item-against-windows-phone-7-ui-guidance:title]