WPF列表控件中对于相同引用的对象奇怪的行为

WPF中常用的List控件如ListBox, ListView 等,在内置的Item对象是同一个对象的引用时,选择这些Item时候,这些相同引用的Item会同时被选中。如下图:

image

 

不管是string还是复杂对象,只要是相同的引用,就会产生问题。为此我们就需要研究下List控件式如何实现的。List控件都继承于 Selector 对象,用 Reflector 将 Selector 类反编译后,我们会发现它实现了基本的Item增加删除选择的行为。Selector 类在用户选择完毕后,他需要将内部的selectedItems集合的值反映到Visual上,这就需要用到了一个内部方法:UpdateSelectedItems

Code Snippet
  1. private void UpdateSelectedItems()
  2. {
  3.     IList selectedItemsImpl = this.SelectedItemsImpl;
  4.     if (selectedItemsImpl != null)
  5.     {
  6.         InternalSelectedItemsStorage storage = new InternalSelectedItemsStorage(selectedItemsImpl.Count) {
  7.             UsesItemHashCodes = this._selectedItems.UsesItemHashCodes
  8.         };
  9.         for (int i = 0; i < selectedItemsImpl.Count; i++)
  10.         {
  11.             object t = selectedItemsImpl[i];
  12.             if (this._selectedItems.Contains(t) && !storage.Contains(t))
  13.             {
  14.                 storage.Add(t);
  15.             }
  16.             else
  17.             {
  18.                 selectedItemsImpl.RemoveAt(i);
  19.                 i--;
  20.             }
  21.         }
  22.         foreach (object obj3 in (IEnumerable) this._selectedItems)
  23.         {
  24.             if (!storage.Contains(obj3))
  25.             {
  26.                 selectedItemsImpl.Add(obj3);
  27.             }
  28.         }
  29.     }
  30. }

好,我们重点看下这个方法(其他内部如何实现选择的方法很简单,不多叙述,大家可以自己在Reflector中看)。我们知道,Selector 将所有选择到的Item存放在内部的selectedItems中和一个叫做SelectedItemsImpl IList中,这里我们看到代码从SelectedItemsImpl中逐个取出对象(其实只是对象的引用)并在selectedItems 和一个InternalSelectedItemsStorage结构中寻找是否已 经存在这个对象,对于相同引用的对象selectedItems中当然是存在的,而他并不在这个新构成的InternalSelectedItemsStorage中,所以会在storage中增加一个新的对象key。然后就是最后一个if块,在selectedItemsImpl增加一个选中对象。这是注意,旧的对象并没有被从selectedItemsImpl移除,这就造成一个对象重复被选择,但又没法移除。

总结:在使用ListBox时候,注意所添加的引用是否相同,要避免此类问题。通常是使用clone体来对内容相同的对象来重复添加。

 

WPF QQ交流群: 113404016  欢迎加入

posted @ 2010-06-02 21:26  Jarrey  阅读(640)  评论(0编辑  收藏  举报