这可能是一个愚蠢(或琐碎)的问题,但似乎我只是不知道答案。情况就是这样 -
UserList
作为组合框的ItemsSource
。所以我所做的基本上是将引用类型分配给另一个。 UserList
。所以现在我也得到了Count
0的ItemsSource
。 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个项目时,它如何显示项目?
答案 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知道它必须更新其显示。