May be it's a silly (or more than trivial) kinda question, but it seems i just don't know the answer. Here's the case -
UserList
as the ItemsSource
of a combobox. So what i did essentially is assigning a reference type to another. UserList
. So now i get the Count
of the ItemsSource
0 as well. SelectedItem
of the combobox to a User
object.Here's the complete code -
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;
}
}
So, where the items are coming from? What actually happens under-the-hood when i make such binding? Does the control have some kind of cache? It's a royal pain to realize not having such basic ideas. Can anybody explain the behind-the-scene detail?
EDIT : I wrote the code in WPF, but i have the same question for WinForms Combobox
.
EDIT : Doesn't a combobox display its items from it's in-memory Datasource
? When that datasource contains 0 items, how does it display the items?
When you set an ItemsSource
of any ItemsControl
it copies the ref to the list into its Items
property. Then it subscribes to the OnCollectionChanged
event, and creates a CollectionView
object. So, on the screen you can see that collectionView.
as I have found in source code ItemCollection
holds two lists:
internal void SetItemsSource(IEnumerable value)
{
//checks are missed
this._itemsSource = value;
this.SetCollectionView(CollectionViewSource.GetDefaultCollectionView((object) this._itemsSource, this.ModelParent));
}
How could you get SelectedItem?
This is my assumption from quick look into the source code:
ItemsControl
has a collection of "views" and each View
sholud store a ref to the item (User
instance), because it has to draw data on the screen. So, when you call SelectedItem it returns a saved ref.
Upd about references
Assume there is an User
instance. It has the adress 123 in memory. There is a list. It stores references. One of them is 123.
When you set an ItemsSource
ItemsControl
saves a reference to the list, and creates a Views collection. Each view stores a references to an item. One view stores an address 123.
Then you cleared a list of users. Now list doesn't contains any references to Users
. But in memory there is an adrress 123 and there is an instance of User
by this adress. Garbage Collector doesn't destroy it, because View has a reference to it.
When you get SelectedItem
it returns User instance from the 123 adress.
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;