Associate an Observable Collection with a ListBox in XAML

I spent many hours on this problem.

I have a class with data:

class userClass : INotifyPropertyChanged { public int _key; private string _fullName; private string _nick; public int key { get{return _key;} set { _key = value; NotifyPropertyChanged("key"); } } private string nick { get { return _nick; } set { _nick = value; NotifyPropertyChanged("nick"); } } private string fullName { get { return _fullName; } set { _fullName = value; NotifyPropertyChanged("fullName"); } } public userClass() { nick = "nickname"; fullName = "fullname"; } public userClass(String nick, String name, int key) { this.nick = nick; this.fullName = name; } //INotifzPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public override string ToString() { return string.Format("{0} {1}, {2}", key, nick, fullName); } } 

Next, I have a class with an observable set of userClass:

 class userListClass : ObservableCollection<userClass> { public userListClass(){} //public override void Add(userClass user) //{ // //user.PropertyChanged += new PropertyChangedEventHandler(user); // base.Add(user); //} ~userListClass() { //Serialize(); } public void Serialize(ObservableCollection<userClass> usersColl) { FileStream fs = new FileStream("DataFile.dat", FileMode.Create); BinaryFormatter formatter = new BinaryFormatter(); try { formatter.Serialize(fs, usersColl); } catch (SerializationException e) { Console.WriteLine("Failed to serialize. Reason: " + e.Message); throw; } finally { fs.Close(); } } public void Deserialize() { FileStream fs = new FileStream("DataFile.dat", FileMode.Open); try { BinaryFormatter formatter = new BinaryFormatter(); //users = (Hashtable) formatter.Deserialize(fs); //usersColl = (ObservableCollection<userClass>)formatter.Deserialize(fs); } catch (SerializationException e) { MessageBox.Show(" Error: " + e.Message); throw; } finally { fs.Close(); } } public override string ToString() { return "test"; //return base.ToString(); } } 

In fact, after a lot of editing testing, most of the code does not work, such as serialization. But there is no need to bind and bind data to what I am currently solving.

So, I have this collection and you want to link it to a listBox. I tried several ways, but it didn’t work for me.

The last one I tried gave me a write error:

The "users" of the resource cannot be allowed.

 <ListBox Grid.Column="0" Name="userViewLeft" ItemsSource="{Binding Source={StaticResource users} }" /> 
+7
source share
2 answers

Some points to note

  • Make properties public , not private .
  • Make variables private .
  • Follow naming conventions and don't add class per class.
  • ItemsSource you must be supplied in accordance with the data area. In my example, the user list is in the class scope, and I provided the ItemSource event when the window loads.

Here is the complete sample code. In this, I nested the Grid Control inside the ListBox, because later you can change the ListBox property for VirtualizingStackPanel. So this will give a huge performance boost if you have major updates on the list. You can also use a BindingList , which, in my opinion, is better than ObservableCollection performance.

User Class:

  public class User : INotifyPropertyChanged { private int _key; private string _fullName; private string _nick; public int Key { get { return _key; } set { _key = value; NotifyPropertyChanged("Key"); } } public string NickName { get { return _nick; } set { _nick = value; NotifyPropertyChanged("NickName"); } } public string Name { get { return _fullName; } set { _fullName = value; NotifyPropertyChanged("Name"); } } public User(String nick, String name, int key) { this.NickName = nick; this.Name = name; this.Key = key; } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public override string ToString() { return string.Format("{0} {1}, {2}", Key, NickName, Name); } } 

User List Class:

  public class Users : ObservableCollection<User> { public Users() { Add(new User("Jamy", "James Smith", Count)); Add(new User("Mairy", "Mary Hayes", Count)); Add(new User("Dairy", "Dary Wills", Count)); } } 

XAML:

  <Grid> <Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="416,12,0,0" x:Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" /> <ListBox x:Name="UserList" HorizontalContentAlignment="Stretch" Margin="12,41,12,12"> <ListBox.ItemTemplate> <DataTemplate> <Grid Margin="10"> <Grid.ColumnDefinitions> <ColumnDefinition Width="20" /> <ColumnDefinition Width="150" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Key}" Margin="3" Grid.Column="0" /> <TextBlock Text="{Binding NickName}" Margin="3" Grid.Column="1" /> <TextBlock Text="{Binding Name}" Margin="3" Grid.Column="2" /> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> 

XAML code behind:

 public partial class MainWindow : Window { public static Users userslist = new Users(); DispatcherTimer timer = new DispatcherTimer(); public MainWindow() { InitializeComponent(); this.Loaded += new RoutedEventHandler(MainWindow_Loaded); } void MainWindow_Loaded(object sender, RoutedEventArgs e) { timer.Interval = DateTime.Now.AddSeconds(10) - DateTime.Now; timer.Tick += new EventHandler(timer_Tick); UserList.ItemsSource = userslist; } void timer_Tick(object sender, EventArgs e) { userslist.Add(new User("Jamy - " + userslist.Count, "James Smith", userslist.Count)); userslist.Add(new User("Mairy - " + userslist.Count, "Mary Hayes", userslist.Count)); userslist.Add(new User("Dairy - " + userslist.Count, "Dary Wills", userslist.Count)); } private void button1_Click(object sender, RoutedEventArgs e) { if (button1.Content.ToString() == "Start") { button1.Content = "Stop"; timer.Start(); } else { button1.Content = "Start"; timer.Stop(); } } } 
+26
source

You need to do 2 things:

First, set the DataContext any element ( Window / UserControl / whatever) containing your ListBox for an object that looks like this:

 public class ViewModel { public ViewModel() { this.users = new userListClass(); } public userListClass users { get; private set; } } 

This is your view model, and this is what you want to bind to.

Second, change the binding to ItemsSource="{Binding Path=users}" . This means that β€œthe value of my ItemsSource matches the value of the users property on this.DataContext . Since the DataContext inherited from the parent and you set it to the ViewModel class above, your ListBox now display your list of users.

+2
source

All Articles