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 欢迎加入