Following the timeline you can see that this has taken me a week to finally get to a solution. I post it here now in hopes that someone else won't waste this much time. There seems to be a lot of posts about how to deal with this issue and the examples are limited. They either show only C# or Xaml. Then CollectionChanged and PropertyChanged aren't dealt with in a single example.
This is a simple example, that implements both collection changed and property changed. As well as binding in Xaml
Here is the Xaml.
<UserControl x:Class="simpleBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:src="clr-namespace:simpleBinding"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Canvas x:Name="LayoutRoot" Background="White" DataContext="{Binding}">
<Canvas.Resources>
<src:PersonList x:Key="myDataSource"></src:PersonList>
</Canvas.Resources>
<Button Name="_b" Content="Add" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="58" Canvas.Left="90" Canvas.Top="5" Click="OnAdd" />
<Button Canvas.Left="150" Canvas.Top="5" Content="Edit" Height="23" Name="button1" Width="58" Click="OnEdit" />
<TextBox Name="_tb" Canvas.Left="12" Canvas.Top="4" Height="24" Width="72"></TextBox>
<ListBox Name="_list" Canvas.Left="18" Canvas.Top="41" Height="98" Width="190" ItemsSource="{Binding Source={StaticResource myDataSource}}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}" Margin="0,0,2,0" />
<TextBlock Text="{Binding Path=LastName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Canvas>
- Add a xmlns that will reference your code behind. In this case my namespace is xmlns:src then you can use VS intellisense to go to the correct class.
- Add a resource to the layoutRoot item. In my case I'm using a canvas, but it could be Grid or Stackpanel etc.
- With the resource declared, you can now set the ItemSource binding in the ListBox.
- I've chosen to use a template to display the data which I think is really cool (best part of Xaml!) In this case there are two textBlocks but if my underlying data source had an image, I could have used this was well to graphically display the data. The binding for each textbox can be set because the exposed properties of the object are declared in the C# code. Which will be discussed next
C# Code behind
namespace simpleBinding
{
public partial class MainPage : UserControl
{
public PersonList m_pList = new PersonList();
public MainPage()
{
InitializeComponent();
_list.ItemsSource = m_pList;
m_pList.Add(new Person("John", "Doe"));
}
private void OnAdd(object sender, RoutedEventArgs e)
{
m_pList.Add(new Person("Jones", _tb.Text));
}
private void OnEdit(object sender, RoutedEventArgs e)
{
m_pList[1].FirstName = _tb.Text;
}
}
public class PersonList : ObservableCollection<Person> , INotifyPropertyChanged
{
public PersonList() : base() // need to call base on intialization otherwise the binded resource is not updated.
{
Add(new Person("Willa", "Cather"));
Add(new Person("Isak", "Dinesen"));
Add(new Person("Victor", "Hugo"));
Add(new Person("Jules", "Verne"));
}
}
public class Person : INotifyPropertyChanged
{
private string _fName;
private string _lName;
public event PropertyChangedEventHandler PropertyChanged;
public string FirstName
{
set
{
_fName = value;
NotifyPropertyChanged("FirstName");
}
get
{
return _fName;
}
}
public string LastName
{
set
{
_lName = value;
NotifyPropertyChanged("LastName");
}
get
{
return _lName;
}
}
public Person(string fName, string lName) : base()
{
FirstName = fName;
LastName = lName;
}
public override string ToString()
{
return String.Format("{0} {1}", FirstName, LastName);
}
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I've chosen to use the ObservableCollection because it implements INotifyCollectionChanged. The public variable is exposed which allows you to bind to the resource declared in the Xaml. (Better code, make the var private and have a property that exposes the variable through a get!)
- The ListBox _List needs to have its ItemsSource property set in Code Behind!!! without this whenever you change the list (add, delete etc) the UI is not updated. AND in fact you do not need the binding in the ListBox at all because we set the source in Code behind it is nice however in that in the designer with this bound control you can see that the binding is working because there are four names added when instantiating the PersonList.
- The ObservableCollection needs to have the INotifyCollectionChanged added. Without this, when a property is changed the UI is NOT changed.
- The properties that are to be exposed to the UI need to be implement in the object that is contained within the ObservableCollection (in my case the class Person exposed both FirstName and LastName) and then these properties can be bound in the Xaml (see the textBlocks's)
- INotifyPropertyChanged requires that you implement a PropertyChanged event i.e. public event PropertyChangedEventHandler PropertyChanged;
- To actually fire that event the "Person" object needs to implement code to do that, which in my case is the NotifyPropertyChanged Method. Each time a property is set, I call this method, which in turn looks to see is the PropertyChanged event is not null, and if not, then it raises that event.
- Here is the key to property changes, without adding the , INotifyPropertyChanged to the Observable collection PropertyChanged is null.
Hope this helps someone