组合框绑定的物品来自哪里?

时间:2012-09-04 11:36:54

标签: wpf winforms combobox datasource reference-type

这可能是一个愚蠢(或琐碎)的问题,但似乎我只是不知道答案。情况就是这样 -

  1. 我指定了UserList作为组合框的ItemsSource。所以我所做的基本上是将引用类型分配给另一个。
  2. 我清除了UserList。所以现在我也得到了Count 0的ItemsSource
  3. 我仍然收到组合框中的物品。我还可以将组合框的SelectedItem投射到User对象。
  4. 这是完整的代码 -

    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 个答案:

答案 0 :(得分:2)

当您设置任何ItemsSource的{​​{1}}时,它会将列表中的ref复制到其ItemsControl属性中。然后,它订阅Items事件,并创建一个OnCollectionChanged对象。所以,在屏幕上你可以看到collectionView。

我在源代码CollectionView中找到的

包含两个列表:

ItemCollection

你怎么能得到SelectedItem?

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

internal void SetItemsSource(IEnumerable value) { //checks are missed this._itemsSource = value; this.SetCollectionView(CollectionViewSource.GetDefaultCollectionView((object) this._itemsSource, this.ModelParent)); } 有一个“视图”集合,每个ItemsControl sholud存储对项目(View实例)的引用,因为它必须在屏幕上绘制数据。因此,当您调用SelectedItem时,它将返回已保存的ref。

有关参考资料的更新

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

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

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

获得User后,它会从 123 地址返回用户实例。

SelectedItem

答案 1 :(得分:1)

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

在WPF中,当您设置ItemsSource的{​​{1}}属性时,控件将包装ItemsControl中的项目列表,这是一种优化供集合使用的集合类型。 UI框架。此CollectionView已分配给控件的CollectionView属性,并且是显示绘图代码实际工作的内容。如您所见,此集合完全独立于您最初分配给Items的对象,因此没有从一个到另一个的变化的传播。这就是清除原始列表时项目仍在控件中的原因:控件忽略原始列表,并且有自己的列表包含对象。

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

非常重要的是要注意,这就像在WinForms中发生的一样没有,这就是为什么我一直在敦促你澄清。

编辑:为了澄清,没有“深层复制”。正在发生的代码原则上类似于以下内容:

ObservableCollection

此代码运行后,列表中每个项目只有一个副本。但是每个项目都在两个列表中。如果您更改,清除或以其他方式操纵private List<object> myCopy; public void SetItemsSource(List<object> yourCopy) { myCopy = new List<object>(); foreach (var o in yourCopy) { myCopy.Add(o); } } yourCopy对此一无所知。你不能“破坏”清单myCopy列表中的任何对象 - 你所做的就是释放你自己对它们的引用。

答案 2 :(得分:0)

假设您使用的是WPF:

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

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

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

答案 3 :(得分:0)

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

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

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

ObservableCollection旨在克服这个问题。它实现了INotifyCollectionChanged,它在事件发生变化时触发事件,因此Combo知道它必须更新其显示。