表題のとおり、WPFのListBoxで、ItemsSourceとSelectedItemを
それぞれバインディング経由で使っていたときに、
バグといっていいかもしれない不具合があったので、一応メモ。
ちなみに手元の環境だと、.NET Framework 4.5では再現せず、
4.0だと再現するPCとしないPCがあったので(どちらもWindows 7, 64bit)、
どこかの更新で修正されたのかな。
3.5だとどちらも再現した。
なにをしたかというと、ItemsSourceとSelectedItemsにそれぞれ
オブジェクトをバインドしてる状態で、
ItemsSourceのコレクションに対し、選択中項目の要素を入れ替えてやったら
画面上で選択がずっと残ってしまい、SelectionMode="Single"なのに
2行選択されているような見え方をするようになってしまった。
以下、再現コード。
まず、XAMLのリストボックスはこんなの。
<ListBox ItemsSource="{Binding ItemList, Mode=OneWay}" SelectedItem="{Binding SelItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
この画面のDataContextとして使用するクラスが以下みたいなやつ。
public class MainWindowViewModel : INotifyPropertyChanged { /// <summary> /// ItemsSourceにバインドするやつ /// </summary> public ObservableCollection<ItemClass> ItemList { get; private set; } private ItemClass _selItem; /// <summary> /// SelectedItemにバインドするやつ /// </summary> public ItemClass SelItem { get { return _selItem; } set { _selItem = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("SelItem")); } } } /// <summary> /// INotifyPropertyChanged実装イベント /// </summary> public event PropertyChangedEventHandler PropertyChanged; }
ItemClassは、適当な文字列のプロパティとかを持ってて
ToString()をオーバーライドしてるような適当なやつ。
あとはItemListに適当な項目を適当に追加しておいて、
ListBoxの適当な行を選択した状態で、以下のようなことをやる。
int index = ItemList.IndexOf(SelItem); ItemList[index] = new ItemClass(); //選択項目のあった部分を入れ替え
すると、選択されていた行の選択状態が見た目だけ残ったままになって、
別の行を選択すると、2行選択されているようになってしまう。
その後SelectedItemにnullを入れたりしても選択が消えない…。
まあなんとなく書き方も悪いというか、事情を酌んでやれる気はする。
SelectedItemとしてバインドされていたオブジェクトが
いきなりItemsSourceのリストから消えてしまうので対応できない感じだろう。
対処としては、以下の一行を間に追加してやればOK。
int index = ItemList.IndexOf(SelItem); SelItem = null; //<-- 先にSelectedItemをnullにしておく ItemList[index] = new ItemClass(); SelItem = ItemList[index]; //<-- 選択行を維持したければ再設定