OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

WPF ListBox.SelectedIndexが勝手に変わると思ったら

ListBoxを配置して、適当に数値だけをメンバに持つオブジェクトをリストにして
ItemsSourceに格納します。
検証環境のビルドターゲットは.NET 4.0。

//リスト項目クラス  
public sealed class NumItem  
{  
    readonly int num;  

    public NumItem(int num)  
    {  
        this.num = num;  
    }  

    public override string ToString()  
    {  
        return num.ToString();  
    }  

    public override bool Equals(object obj)  
    {  
        var other = obj as NumItem;  
        if (other == null) return false;  

        //数値が同じなら等価とみなす  
        return this.num == other.num;  
    }  

    public override int GetHashCode()  
    {  
        return this.num.GetHashCode();  
    }  
}  
//1,2,3の3行をリスト化  
listbox.ItemsSource = new[] { 1, 2, 3 }.Select(n => new NumItem(n)).ToArray();  

この状態でSelectedItemプロパティに、存在しない項目(4)を設定する。
すると、選択できるわけがないので、
そのあとにSelectedItemプロパティを参照するとNullが返るし、
SelectedIndexは-1を返す。

listbox.SelectedItem = new NumItem(4);  
Console.WriteLine(listbox.SelectedItem == null);  // true  
Console.WriteLine(listbox.SelectedIndex);  // -1  

SelectedItemを設定する代わりに、SelectedIndex = 3 などとやっても同じ結果。
ここまでは想定どおりである。

ところが、そのあとに項目4を含むリストをItemSourceに再設定してやると、
なんとさっきの情報を覚えていて、
おせっかいにも勝手に選択してくれやがるじゃないですか。

listbox.SelectedItem = new NumItem(4);  
listbox.ItemsSource = new[] { 1, 2, 3, 4 }.Select(n => new NumItem(n)).ToArray();  
Console.WriteLine(listbox.SelectedItem);   // 4  
Console.WriteLine(listbox.SelectedIndex);  // 3  

SelectedIndex = 3とやった場合もやはり同じ結果になる。
おそらく直前に設定された値を内部的に覚えてるんだろうな。

もちろんリストに存在しない項目を選択するなんていう
気持ち悪いことをやらなきゃいい話なんだけど、
というか自分ではこんなコード書かないだろうけども、
他の人の作ったアプリが変な挙動していて
調査してたらここに原因があってだいぶはまりましたとさ。