Tutorial :How to make listview update itself in wpf?


I realize this is kind of a long question, but I saw no other way here than to post my code, that I tried to keep as short and simple as possible for the sake of clarity. Of course tons of best practices are violated to do so, the example is long enough as it is..

I made a very simple wpf app that

  • shows a list of Persons on the left of the screen (format : name and age between () )
  • shows all the properties of the selected person on the right of the screen
  • on the right you can edit the properties and view the entire selection in a msgbox

In the following example, I have edited the age of Bar. However, in the List, the age is not updated. If I ask the underlying collection, it still appears to have been updated.. How can I make the list know ?

Following are, besides a screenshot, the code and the XAML

NOTE : if the image does not show, try to open it in a new tab or window.

alt text

namespace ASAPBinding  {      public class Person      {          public string Name { get; set; }          public int Age { get; set; }            public override string ToString()          {              return String.Format("{0} ({1})",Name,Age);               }      }    }  

namespace ASAPBinding  {      public class Dal      {          public ObservableCollection<Person> Persons { get; set; }            public Dal()          {              Persons = new ObservableCollection<Person>();              Persons.Add(new Person() {Name = "Bar", Age = 25});              Persons.Add(new Person() {Name = "Foo", Age = 50});          }            public void PrintOutCollection()          {              MessageBox.Show(                  Persons[0].ToString() + "\n" + Persons[1].ToString()                  );          }      }  }  

<Window x:Class="ASAPBinding.EditPersons"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:local="clr-namespace:ASAPBinding"       x:Name="window1"      Title="EditPersons" Height="300" Width="300">      <Window.Resources>          <local:Dal x:Key="dal"/>      </Window.Resources>      <Grid>          <Grid.ColumnDefinitions>              <ColumnDefinition Width="1*" />              <ColumnDefinition Width="1*" />          </Grid.ColumnDefinitions>            <ListBox Name="ListBox1"                    ItemsSource="{Binding Source={StaticResource dal}, Path=Persons, Mode=TwoWay}"                    Grid.Column="0"/>          <StackPanel               DataContext="{Binding ElementName=ListBox1, Path=SelectedItem, Mode=TwoWay}"              Grid.Column="1" Margin="0,0,0,108">                <TextBox Text="{Binding Path=Name}" />              <TextBox Text="{Binding Path=Age}"  />              <Button Click="Button_Click">Show Collection</Button>          </StackPanel>      </Grid>  </Window>  

public partial class EditPersons : Window      {          public EditPersons()          {              InitializeComponent();          }            private void Button_Click(object sender, RoutedEventArgs e)          {              Dal dal = (Dal) window1.FindResource("dal");              dal.PrintOutCollection();          }      }  


It is not enough to have an ObservableCollection, if you want to update binding on specific properties, your Person type must implement INotifyPropertyChanged.


I've just noticed, your left ListBox is not updated because you have no DataTemplate set for a Person object. What you have now is a ToString() implementation, which does not get updated once it reports to the UI.

You need something like this:

<DataTemplate DataType="{x:Type local:Person}">     <StackPanel Orientation="Horizontal">          <TextBlock Text="{Binding Name}"/>          <TextBlock Text="("/>          <TextBlock Text="{Binding Age}"/>          <TextBlock Text=")"/>      </StackPanel>  </DataTemplate>  


First, at the collection level, The ItemsSource of you ListView should be a ObservableCollection

ObservableCollection<Person> Persons = new ObservableCollection<Person>();  

This will notify the ListView that the order of items has been changed.

Second, at the item level. You Person should also implement INotifyPropertyChanged Interface as mentioned in the official document and here.

This will tell the ListView that the content of an item has been changed.

When you implement INotifyPropertyChanged,

public event PropertyChangedEventHandler PropertyChanged;  

is defined and has to be called explicitly in the setter of properties. One thing needs to notice is that you don't need to hard-code the property name, CallerMemberName attribute can be used to automatically get the caller's name.

public class DemoCustomer : INotifyPropertyChanged  {      // These fields hold the values for the public properties.       private Guid idValue = Guid.NewGuid();      private string customerNameValue = String.Empty;      private string phoneNumberValue = String.Empty;        public event PropertyChangedEventHandler PropertyChanged;        // This method is called by the Set accessor of each property.       // The CallerMemberName attribute that is applied to the optional propertyName       // parameter causes the property name of the caller to be substituted as an argument.       private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")      {          if (PropertyChanged != null)          {              PropertyChanged(this, new PropertyChangedEventArgs(propertyName));          }      }        // The constructor is private to enforce the factory pattern.       private DemoCustomer()      {          customerNameValue = "Customer";          phoneNumberValue = "(312)555-0100";      }        // This is the public factory method.       public static DemoCustomer CreateNewCustomer()      {          return new DemoCustomer();      }        // This property represents an ID, suitable       // for use as a primary key in a database.       public Guid ID      {          get          {              return this.idValue;          }      }        public string CustomerName      {          get          {              return this.customerNameValue;          }            set          {              if (value != this.customerNameValue)              {                  this.customerNameValue = value;                  NotifyPropertyChanged();              }          }      }        public string PhoneNumber      {          get          {              return this.phoneNumberValue;          }            set          {              if (value != this.phoneNumberValue)              {                  this.phoneNumberValue = value;                  NotifyPropertyChanged();              }          }      }  }  



public class Person : DependencyObject  {      public static readonly DependencyProperty NameProperty = DependencyProperty.Register(          "Name",          typeof(string),          typeof(Person)      );        public static readonly DependencyProperty AgeProperty = DependencyProperty.Register(          "Age",          typeof(int),          typeof(Person)      );        public string Name      {          get { return (string)GetValue(NameProperty); }          set { SetValue(NameProperty, value); }      }        public int Age      {          get { return (int)GetValue(AgeProperty ); }          set { SetValue(AgeProperty , value); }      }        public override string ToString()      {              return String.Format("{0} ({1})",Name,Age);           }  }  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Next Post »