title: How do you sync a ListView and List model using MVVM?
body: <p>I am attempting to use the MVVM pattern to display a List of objects that can be updated in the UI.  I would also like some basic operations like Add/Remove.  Right now I have a simple <code>Customer</code> model that is just a first and last name.  I have the <code>DataContext</code> assigned to the <code>ViewModel</code>.  In the <code>ViewModel</code> class I have the “model” (which is just a <code>List</code> of Customers) that will be injected at a later point.   To keep the underlying <code>List</code> model up to date, I wrap the <code>List</code> with an <code>ObservableCollection</code> each time it’s accessed.  In doing this, it doesn’t seem to keep the <code>SelectedValue</code> active because when you remove the <code>SelectedValue</code>, it sets it to <code>null</code> and clears out the selection in the <code>ListView</code>.  This means that I would need some manual tracking (which I am hoping to avoid).
  I have tried to keep an <code>ObservableCollection</code> as a member variable to the <code>ViewModel</code> class, but this only copies the underlying list of data and doesn’t keep it synced if you add/remove objects from it.</p>
  
  <p>I also want to avoid using <code>ObservableCollection</code> as the model, since this seems more geared toward <code>ViewModel</code> data binding support (<a href="http://updatecontrols.net/doc/tips/common_mistakes_observablecollection" rel="nofollow">reference; see Using it in the model</a>).
  Has anyone done this before and found a good way to keep the <code>List</code> model in sync but also data bind to the <code>ListView</code> using <code>ObservableCollection</code>?</p>
  
  <p><strong>MainWindow.xaml</strong></p>
  
  <pre><code>&lt;Window x:Class="TestCollectionAndSelectedItem.MainWindow"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:local="clr-namespace:TestCollectionAndSelectedItem"
          Title="MainWindow" Height="350" Width="525"&gt;
      &lt;Window.Resources&gt;
          &lt;local:IndexConverter x:Key="IndexConverter" /&gt;
      &lt;/Window.Resources&gt;
      &lt;Grid&gt;
          &lt;Grid.RowDefinitions&gt;
              &lt;RowDefinition Height="35*" /&gt;
              &lt;RowDefinition Height="276*" /&gt;
          &lt;/Grid.RowDefinitions&gt;
          &lt;Button Content="Add" Command="{Binding Path=AddCustomer}"  Width="95" Margin="22,11,0,231" Grid.Row="1" HorizontalAlignment="Left" /&gt;
          &lt;Label Content="First" Height="26" Width="80" HorizontalAlignment="Left" Margin="263,95,0,155" Grid.Row="1" /&gt;
          &lt;TextBox Text="{Binding SelectedCustomer.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="31" Width="133" IsEnabled="True" Margin="345,93,25,152" Name="textBox1" Grid.Row="1" /&gt;
          &lt;Label Content="Last" Height="26" Width="80" HorizontalAlignment="Left" Margin="263,139,0,111" Grid.Row="1" /&gt;
          &lt;TextBox Text="{Binding SelectedCustomer.LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="133" Height="31" IsEnabled="True" Margin="345,137,25,108" Name="textBox2" Grid.Row="1" /&gt;
          &lt;ListView SelectionMode="Single" ItemsSource="{Binding Customers}" SelectedValue="{Binding SelectedCustomer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Name="listviewNames" Margin="22,56,258,77" Grid.Row="1"&gt;
              &lt;ListView.View&gt;
                  &lt;GridView&gt;
                      &lt;GridView.Columns&gt;
                          &lt;GridViewColumn Header="Number"
                              DisplayMemberBinding="{Binding RelativeSource={RelativeSource FindAncestor, 
                              AncestorType={x:Type ListViewItem}}, 
                              Converter={StaticResource IndexConverter}}" /&gt;
                          &lt;GridViewColumn Header="Last" DisplayMemberBinding="{Binding Path=LastName}" Width="80"/&gt;
                          &lt;GridViewColumn Header="First" DisplayMemberBinding="{Binding Path=FirstName}" Width="80"/&gt;
                      &lt;/GridView.Columns&gt;
                  &lt;/GridView&gt;
              &lt;/ListView.View&gt;
          &lt;/ListView&gt;
          &lt;Button Command="{Binding Path=RemoveCustomer}" CommandParameter="{Binding ElementName=listviewNames, Path=SelectedIndex}" Content="Remove" HorizontalAlignment="Left" Margin="150,11,0,231" Width="95" Grid.Row="1" /&gt;
      &lt;/Grid&gt;
  &lt;/Window&gt;
  </code></pre>
  
  <p><strong>MainWindow.xaml.cs</strong></p>
  
  <pre><code>public partial class MainWindow : Window
  {
     // wont go here, just example
     List&lt;Customer&gt; customers = new List&lt;Customer&gt;() { 
        new Customer() { LastName = "Anderson", FirstName = "John" },
        new Customer() { LastName = "NoName", FirstName = "" } };
  
     public MainWindow()
     {
        InitializeComponent();
        DataContext = new ViewModel(customers);
     }
  }
  </code></pre>
  
  <p><strong>ViewModel.cs</strong></p>
  
  <pre><code>public class ViewModel : INotifyPropertyChanged
  {
     public event PropertyChangedEventHandler PropertyChanged;
  
     private List&lt;Customer&gt; _customersModel; 
     private Customer _selectedCustomer;
  
     public DelegateCommand&lt;object&gt; AddCustomer { get; private set; }
     public DelegateCommand&lt;int&gt; RemoveCustomer { get; private set; }
  
     public ViewModel(List&lt;Customer&gt; listOfCustomers)
     {
        _customersModel = listOfCustomers;
        AddCustomer = new DelegateCommand&lt;object&gt;((a) =&gt; Add(), (a) =&gt; CanAdd());
        RemoveCustomer = 
           new DelegateCommand&lt;int&gt;((a) =&gt; Remove(a), (a) =&gt; CanRemove());
     }
  
     public ObservableCollection&lt;Customer&gt; Customers
     {
        get
        {
           return new ObservableCollection&lt;Customer&gt;(_customersModel);
        }
        set
        {
           _customersModel = new List&lt;Customer&gt;(value);
           OnPropertyChanged("Customers");
        }
     }
  
     public Customer SelectedCustomer
     {
        get { return _selectedCustomer; }
        set
        {
           if (_selectedCustomer != value)
           {
              _selectedCustomer = value;
              UpdateCommands();
              OnPropertyChanged("SelectedCustomer");
           }
        }
     }
  
     public void Add()
     {
        _customersModel.Add(new Customer() { FirstName = "", LastName = "" });
        UpdateCommands();
        OnPropertyChanged("Customers");
     }
  
     public Boolean CanAdd()
     {
        return _customersModel.Count &lt; 8;
     }
  
     public void Remove(int selectedIndex)
     {
        _customersModel.Remove(_selectedCustomer);
        UpdateCommands();
        OnPropertyChanged("Customers");
     }
  
     public Boolean CanRemove()
     {
        return (_selectedCustomer != null) &amp;&amp; 
               (_customersModel != null) &amp;&amp;
               (_customersModel.Count &gt; 0);
     }
  
     public void UpdateCommands()
     {
        RemoveCustomer.RaiseCanExecuteChanged();
        AddCustomer.RaiseCanExecuteChanged();
     }
  
     protected void OnPropertyChanged(string name)
     {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
           handler(this, new PropertyChangedEventArgs(name));
        }
     } 
  }
  </code></pre>
  
  <p><strong>IndexConverter.cs</strong></p>
  
  <pre><code>public class IndexConverter : IValueConverter
  {
     public object Convert(object value, Type TargetType, object parameter, CultureInfo culture)
     {
        ListViewItem item = (ListViewItem)value;
        ListView listView = 
           ItemsControl.ItemsControlFromItemContainer(item) as ListView;
        int index = 
           listView.ItemContainerGenerator.IndexFromContainer(item) + 1;
        return index.ToString();
     }
  
     public object ConvertBack(object value, 
        Type targetType, object parameter, CultureInfo culture)
     {
        throw new NotImplementedException();
     }
  }
  </code></pre>
  
  <p><strong>Customer.cs</strong></p>
  
  <pre><code>public class Customer
  {
     public String FirstName { get; set; }
     public String LastName { get; set; }
  }
  </code></pre>