LoginSignup
4
5

More than 5 years have passed since last update.

ListBoxのItemsSourceとDataTemplateから、実際に生成されたListBoxItemにアクセスする

Last updated at Posted at 2014-12-04

概要

ListBoxは指定されたアイテムのコレクションを表示することが目的のコントロールだが、そのアイテムの指定には二通りの方法がある。

  • 直接ListBoxItemを子要素として配置する方法
  • ItemsSourceプロパティにバインドしたコレクションを、DataTemplateで指定したコンテナに格納して配置させる方法

データバインディングを用いるなら後者の方法を使いたいが、実際に生成された子要素にアクセスするのはなかなか難しい。実際にコーディングする上でいくつか罠に引っかかってしまったので、これ以上不幸な犠牲者を出さないためにも、ここに覚え書きを残しておくことにする。

注:もしかすると下記の目標を達成するためにはもっとスマートな方法があるのかも知れないが、今後似たようなことをする機会もあるかも知れないので大目に見てほしい。

目標

ListBoxのアイテムを選択不可にする

具体的な手段

ListBox.SelectionChangedイベントで、各アイテムのSelector.IsSelected添付プロパティをすべてfalseに設定することで、ハンドラが処理を終えた時点ですべてのアイテムが非選択状態になる。

コード

ListBoxItemを子要素に格納する場合は、Itemsプロパティから取得したアイテムのコレクションのIsSelected添付プロパティ(実態はSelectorクラスが持つ)を書き換えれば良い。ListBoxの仕組みについて、以下の記事が非常に参考になった。

ListBoxの選択状態の変更

一方で、ItemsSourceを使う場合だと、ItemsでもSelectedItemsでもItemsSourceに設定したコレクションが持っているオブジェクトへの参照が帰ってくるだけで、実際にテンプレートが適用された子要素が返ってくるわけではない。これは正直理解しがたい仕様であるが、これに立ち向かう手段については、MSDNの以下の記事が詳しい。

方法 : DataTemplate によって生成された要素を検索する

なかなか狂気を感じる内容だが、要約すると以下の通りである。

  • 対象のListBoxのItemContainerGeneratorのContainerFromItemメソッドを使って、ほしいアイテムから子要素の実態(ListBoxItem)が取得できる
  • ListBoxItemの中身が見たい場合は、ListBoxItemのContentPresenterが持ってるDataTemplateのFindNameを使って引っ張る
  • ContentPresenterを取り出すのはVisualTreeHelperで頑張れ

実際のコードはこんな感じになる。XAML側は普通にItemsSourceにコレクションをバインドしているだけなので省略する。

example.xaml.cs
private void MyListBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    foreach (var item in MyListBox.Items)
    {
        var container = MyListBox.ItemContainerGenerator.ContainerFromItem(item);
        Selector.SetIsSelected(container, false);
    }
}
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5