1

可能这是一个愚蠢的(或不仅仅是微不足道的)问题,但似乎我只是不知道答案。情况是这样的——

  1. 我分配了一个UserList作为ItemsSource组合框的。所以我所做的本质上是将引用类型分配给另一个。
  2. 我清除了UserList. 所以现在我也得到CountItemsSource0 。
  3. 我仍然得到组合框中的项目。而且我还可以将SelectedItem组合框投射到一个User对象上。

这是完整的代码 -

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public partial class MainWindow : Window
{
    private List<User> _userList;

    public MainWindow()
    {
        InitializeComponent();
        _userList = new List<User>()
                                  {
                                      new User() {Id = 1, Name = "X"},
                                      new User() {Id = 2, Name = "Y"},
                                      new User() {Id = 3, Name = "Z"}
                                  };
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        this.comboBox1.ItemsSource = _userList;
        this.comboBox1.DisplayMemberPath = "Name";
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        _userList.Clear();

        /* ItemsSource is cleared as well*/
        IEnumerable userList = this.comboBox1.ItemsSource;

        /*I can still get my User*/
        User user = this.comboBox1.SelectedItem as User;
    }
}  

那么,这些物品是从哪里来的呢?当我进行这种绑定时,实际上会发生什么?控件是否有某种缓存?意识到没有这样的基本想法是一种皇家痛苦。谁能解释一下幕后细节?

编辑:我在 WPF 中编写了代码,但我对 WinForms 有同样的问题Combobox

编辑:组合框不是从它的内存中显示它的项目Datasource吗?当该数据源包含 0 个项目时,它如何显示这些项目?

4

4 回答 4

2

当您设置 anItemsSource时,ItemsControl它会将列表中的引用复制到其Items属性中。然后它订阅OnCollectionChanged事件,并创建一个CollectionView对象。所以,在屏幕上你可以看到collectionView。

正如我在源代码中发现的那样ItemCollection包含两个列表:

internal void SetItemsSource(IEnumerable value)
    {
      //checks are missed
      this._itemsSource = value;
      this.SetCollectionView(CollectionViewSource.GetDefaultCollectionView((object) this._itemsSource, this.ModelParent));
    }

你怎么能得到 SelectedItem?

这是我快速查看源代码的假设:

ItemsControl有一个“视图”的集合,每个View应该存储一个对项目(User实例)的引用,因为它必须在屏幕上绘制数据。因此,当您调用 SelectedItem 时,它会返回一个保存的参考。

更新参考资料

假设有一个User实例。它 在内存中有地址123 。有一个清单。它存储引用。其中之一是123

当您设置一个ItemsSource ItemsControl保存对列表的引用,并创建一个视图集合。每个视图都存储对项目的引用。一个视图存储地址123

然后您清除了用户列表。现在列表不包含对Users. 但是在内存中有一个地址 123 并且有一个Userby this address 的实例。垃圾收集器不会销毁它,因为 View 有对它的引用。

当你得到它时,它会从123地址SelectedItem返回用户实例。

var user = new User();

var list = new List<User>();
list.Add(user);

list.Clear();
Console.WriteLine(list.Count()); //prints 0 - list is empty

Console.WriteLine(user == null); //prints false. - user instance is sill exists;
于 2012-09-04T11:46:28.197 回答
1

回答您对@GazTheDestroyer 的评论(“......为什么它没有被清除,以及它如何保存这些物品?”)

在 WPF 中,当您设置ItemsSourcean 的属性时ItemsControl,控件会将项目列表包装在 a 中CollectionView,这是一种为 UI 框架使用而优化的集合类型。这CollectionView被分配给Items控件的属性,并且是显示绘图代码实际工作的内容。如您所见,此集合与您最初分配给的对象完全分开,ItemsSource因此不会传播从一个对象到另一个对象的更改。这就是当您清除原始列表时项目仍在控件中的原因:控件忽略原始列表,并拥有包含您的对象的自己的列表。

正是由于这个原因,一个ItemsSource值需要引发事件 - 特别是INotifyCollectionChanged.NotifyCollectionChanged- 以便控件知道刷新Items列表。 ObservableCollection实现此接口并引发正确的事件,因此功能按预期工作。

非常重要的是要注意这WinForms 中发生的情况完全不同,这就是为什么我一直在敦促您进行澄清。

编辑:澄清一下,没有“深拷贝”。发生的代码原则上类似于以下内容:

private List<object> myCopy;

public void SetItemsSource(List<object> yourCopy)
{
     myCopy = new List<object>();
     foreach (var o in yourCopy)
     {
         myCopy.Add(o);
     }
}

运行此代码后,列表中的每个项目都只有一个副本。但是每个项目都在两个列表中。如果您更改、清除或以其他方式操纵yourCopymyCopy则对此一无所知。你不能“破坏”我清理列表中的任何对象yourCopy——你所做的只是释放你自己对它们的引用。

于 2012-09-04T12:53:08.643 回答
0

假设您使用的是 WPF:

List<User>不会触发 UI 将识别以刷新自身的任何事件。如果您ObservableCollection<User>改用,您的代码将起作用。

关键区别在于ObservableCollectionimplements INotifyCollectionChanged,它允许 UI 识别集合的内容已更改,从而刷新ComboBox.

(请注意,这在 WinForms中不起作用。在 WinForms 中,您可以设置DataSource控件的属性,但相同的ObservableCollection技巧在这里不起作用。)

于 2012-09-04T12:36:19.140 回答
0

当您将集合引用设置为 时ItemsControl,所有组合获取的都是一个引用,它知道它是可枚举的。

它将枚举引用并显示项目。无论是深拷贝还是浅拷贝都无关紧要,它所拥有的只是一个引用(有效的内存地址)。

如果您以某种方式更改您的收藏,除非您以某种方式告诉它,否则该组合无法知道。参考(地址)没有改变,一切看起来都与组合相同。您似乎在认为该对象以某种方式“活着”并且该组合可以观察内存的变化或其他什么?事实并非如此。它所拥有的只是一个可以枚举的引用。内容可以改变,但如果没有一些触发,组合不知道这一点,因此将无所事事。

ObservableCollection旨在克服这一点。它实现INotifyCollectionChanged了在更改时触发事件,因此 Combo 知道它必须更新其显示。

于 2012-09-04T13:35:25.470 回答