読者です 読者をやめる 読者になる 読者になる

酢ろぐ!

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

MultiselectListを使って複数の項目にチェックを入れられるリストを表示する

Windows Phone

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

……と無茶振りされてしまいましたので、今日は昨日に引き続いてWindows Phone Toolkitのお話です。

Windows Phone Toolkitに含まれるMultiselectListを使ってみましょう。MultiselectListはかなり有能なリストコントロールで、複数の項目にチェックを入れたり、iOSで詳細情報を参照する際に表示されるディスクロージャーのようなものを表示させる事が出来ます。

MultiselectListを表示する

新しいプロジェクトを作成します。テンプレートから「Windows Phone アプリケーション」を選択します。プロジェクトの名前は「MultiselectListTest」とします。

作成したプロジェクトでMultiselectListクラスを使用するために、Silverlight for Windows Phone Toolkitのアセンブリへの参照追加します。

ソリューションエクスプローラーからMultiselectListTestプロジェクトの参照設定を右クリックして、参照の追加を選択します。ダイアログが表示されますので、その中からMicrosoft.Phone.Controls.Toolkitを選択してOKボタンをクリックします。

MainPage.xamlに追記を行います。まずXAMLファイルのルート要素内でxmlns宣言を追加します。*1

<phone:PhoneApplicationPage
    x:Class="ContextServiceTest.MainPage"
    〜(中略)〜
    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

XAMLにMultiselectItem型のアイテムを直接定義しています。

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
	<toolkit:MultiselectList x:Name="list">
		<toolkit:MultiselectItem Content="ランタイム1" />
		<toolkit:MultiselectItem Content="ランタイム2" />
		<toolkit:MultiselectItem Content="ランタイム3" />
	</toolkit:MultiselectList>
</Grid>

XAMLにアイテムを適当に追加した後に実行してみます。この時点では見た目は普通のListBoxとあまり変わりません。

ここからがMultiselectListの特徴です。アイテムの左側をタップしてください。

左側からチェックボックスが表示されます。

チェックボックスが表示状態が変わった事を通知するIsSelectionEnabledChangedイベント

チェックボックスの表示状態が変更されるとIsSelectionEnabledChangedイベントが発行されます。DependencyPropertyChangedEventArgs型で通知されるのでbool型にキャストして変更状態を調べましょう。

// チェックボックスが表示状態が変わった事を通知するイベントハンドラ
private void list_IsSelectionEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) {

    if ((bool)e.NewValue) {
        // チェックボックスが表示された
    } else {
        // チェックボックスが非表示になった
    }
}

プログラム側からこのチェックボックスを表示させたい場合、IsSelectionEnabledプロパティを変更します。

private void ApplicationBarIconButton_Click(object sender, EventArgs e) {
    list.IsSelectionEnabled = true;
}
IsSelectionEnabledプロパティの注意点

Windows Phone Toolkit Nov11の不具合だと思うのですが、OnNavigatedToメソッドを抜ける前に、IsSelectionEnabledプロパティへの操作を行うと例外が発生します。

チェックされた事を通知するSelectionChangedイベント

次にチェックされているアイテムをどのようにして取得しましょう?選択された瞬間にアイテムを取得したい場合は、SelectionChangedイベントを使用します。

// チェックされた事を通知するイベントハンドラ
private void list_SelectionChanged(object sender, SelectionChangedEventArgs e) {
    var target = sender as MultiselectList;
    if (target == null) return;

    // 選択中のアイテムを取得する
    var items = target.SelectedItems;
    
    // イベントが発生した原因となったチェックされたアイテム
    var addItems = e.AddedItems;
    // イベントが発生した原因となったチェックを外されたアイテム
    var removeItems = e.RemovedItems;
}

MultiselectListの表示をカスタマイズする

MultiMenuItemを表示させるだけでは少し愛想が無いのでXAMLに修正を入れてみましょう。3つのStyleを定義します。

  • ItemContainerStyle アイテム毎のコンテナのStyle
  • ItemTemplate アイテム毎に表示する要素が定義されたStyle(通常のListBoxと同じ)
  • ItemInfoTemplate ディスクロージャー部分のStyle
<toolkit:MultiselectList x:Name="list" IsSelectionEnabledChanged="list_IsSelectionEnabledChanged" 
  SelectionChanged="list_SelectionChanged">

  <toolkit:MultiselectList.ItemContainerStyle>
    <Style TargetType="toolkit:MultiselectItem">
      <Setter Property="HintPanelHeight" Value="90"/>
    </Style>
  </toolkit:MultiselectList.ItemContainerStyle>

  <toolkit:MultiselectList.ItemTemplate>
    <DataTemplate>
      <StackPanel Margin="0,-14,0,24">
        <TextBlock Text="{Binding Content}" 
                   Margin="0,0,0,-4"
                   FontSize="{StaticResource PhoneFontSizeExtraLarge}" 
                   FontFamily="{StaticResource PhoneFontFamilySemiLight}"/>
        <TextBlock Text="{Binding Detail}"
                   Foreground="{StaticResource PhoneSubtleBrush}"
                   FontSize="{StaticResource PhoneFontSizeNormal}"/>
      </StackPanel>
    </DataTemplate>
  </toolkit:MultiselectList.ItemTemplate>
		
  <toolkit:MultiselectList.ItemInfoTemplate>
    <DataTemplate>
      <Border Margin="0,9,24,0" VerticalAlignment="Center">
        <Grid VerticalAlignment="Center">
  	  <Image Source="appbar.basecircle.rest.png" Stretch="None"/>
    	  <Image Source="appbar.next.rest.png" Stretch="None"/>
	</Grid>
      </Border>
    </DataTemplate>
  </toolkit:MultiselectList.ItemInfoTemplate>
</toolkit:MultiselectList>

これで見た目の部分の定義は完了です。appbar.basecircle.rest.pngとappbar.next.rest.pngはビルドアクションをコンテンツに設定してプロジェクト内に含んでいます。

ItemTemplateでContentとDetailをバインディングしていますので、プログラム側で使用するバインディング用のクラスを定義します。あまりひねりのないクラスですが……

public class ItemViewModel {
  public ItemViewModel() { }
  public ItemViewModel(string content) : base() {
      Content = content;
  }
  
  public string Content { set; get; }
  public string Detail { set; get; }
}

次にページを開いた時にリストを更新出来るようにOnNavigatedToメソッドにて、ItemsSourceプロパティに設定します。

protected override void OnNavigatedTo(NavigationEventArgs e) {
    var items = new List<ItemViewModel>();
    items.Add(new ItemViewModel() { Content = "ランタイム1", Detail = "ほげほげほげほげ" });
    items.Add(new ItemViewModel() { Content = "ランタイム2", Detail = "ぴよぴよぴよぴよ" });
    items.Add(new ItemViewModel() { Content = "ランタイム3", Detail = "ぷにぷにぷにぷに" });
    list.ItemsSource = items;
}

さて表示させてみましょう。

*1:細かい説明は[http://d.hatena.ne.jp/ch3cooh393/20111205/1323019350:title=こちら]のXAML項目をご覧ください。