WPF列表控件中對于相同引用的對象奇怪的行為
WPF中常用的List控件如ListBox, ListView 等,在內置的Item對象是同一個對象的引用時,選擇這些Item時候,這些相同引用的Item會同時被選中。如下圖:
不管是string還是復雜對象,只要是相同的引用,就會產生問題。為此我們就需要研究下List控件式如何實現的。List控件都繼承于 Selector 對象,用 Reflector 將 Selector 類反編譯后,我們會發現它實現了基本的Item增加刪除選擇的行為。Selector 類在用戶選擇完畢后,他需要將內部的selectedItems集合的值反映到Visual上,這就需要用到了一個內部方法:UpdateSelectedItems
- private void UpdateSelectedItems()
- {
- IList selectedItemsImpl = this.SelectedItemsImpl;
- if (selectedItemsImpl != null)
- {
- InternalSelectedItemsStorage storage = new InternalSelectedItemsStorage(selectedItemsImpl.Count) {
- UsesItemHashCodes = this._selectedItems.UsesItemHashCodes
- };
- for (int i = 0; i < selectedItemsImpl.Count; i++)
- {
- object t = selectedItemsImpl[i];
- if (this._selectedItems.Contains(t) && !storage.Contains(t))
- {
- storage.Add(t);
- }
- else
- {
- selectedItemsImpl.RemoveAt(i);
- i--;
- }
- }
- foreach (object obj3 in (IEnumerable) this._selectedItems)
- {
- if (!storage.Contains(obj3))
- {
- selectedItemsImpl.Add(obj3);
- }
- }
- }
- }
好,我們重點看下這個方法(其他內部如何實現選擇的方法很簡單,不多敘述,大家可以自己在Reflector中看)。我們知道,Selector 將所有選擇到的Item存放在內部的selectedItems中和一個叫做SelectedItemsImpl IList中,這里我們看到代碼從SelectedItemsImpl中逐個取出對象(其實只是對象的引用)并在selectedItems 和一個InternalSelectedItemsStorage結構中尋找是否已 經存在這個對象,對于相同引用的對象selectedItems中當然是存在的,而他并不在這個新構成的InternalSelectedItemsStorage中,所以會在storage中增加一個新的對象key。然后就是最后一個if塊,在selectedItemsImpl增加一個選中對象。這是注意,舊的對象并沒有被從selectedItemsImpl移除,這就造成一個對象重復被選擇,但又沒法移除。
總結:在使用ListBox時候,注意所添加的引用是否相同,要避免此類問題。通常是使用clone體來對內容相同的對象來重復添加。
WPF QQ交流群: 113404016 歡迎加入
浙公網安備 33010602011771號