title: ListBox ObservableCollection Properties Not Updating
body: <p>Having some issues getting a ListBox containing an ObservableCollection to update when properties of the items within change. The ObjectType that the items are, is using INotifyPropertyChanged and PropertyChanged(). The interesting thing (to me) is the Listbox won't work how I want/need it to, but I can get it working another way. My MVVM is based on this article/code: <a href="http://msdn.microsoft.com/en-us/magazine/dd419663.aspx" rel="nofollow">http://msdn.microsoft.com/en-us/magazine/dd419663.aspx</a> .</p>
  
  <p>I am trying to emulate his Workspaces for containing ServerConfigs from a BuildRepository. However, rather than having "AllServerConfigs" be a tab in that workspace I wanted it to be a Listbox on the left hand side where a user can open configs they have closed the tab on. The Listbox is updating for add/remove but the properties are not updating (if you save a a new name for example).</p>
  
  <p>Here is the <code>MainWindow.XAML</code>; the datacontext is set to "RepositoryViewModel".</p>
  
  <pre><code>&lt;Window x:Class="Server_Build_Config_Tool.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:vm="clr-namespace:Server_Build_Config_Tool.ViewModel"
      Height="800" Width="600"
      MinWidth="600"
      MinHeight="800"
      Title="{Binding Path=DisplayName}"
      WindowStartupLocation="CenterScreen" WindowState="Maximized"&gt;
  &lt;Window.Resources&gt;
      &lt;ResourceDictionary Source="pack://application:,,,/Resources/MainWindowResources.xaml" /&gt;
  &lt;/Window.Resources&gt;
  &lt;DockPanel&gt;
  
      &lt;Grid Margin="4"&gt;
          &lt;Grid.ColumnDefinitions&gt;
              &lt;ColumnDefinition Width="Auto" /&gt;
              &lt;ColumnDefinition Width="4" /&gt;
              &lt;ColumnDefinition Width="*" /&gt;
          &lt;/Grid.ColumnDefinitions&gt;
          &lt;Border Grid.Column="0" Width="170" Style="{StaticResource MainBorderStyle}"&gt;
              &lt;Grid&gt;
                  &lt;Grid.ColumnDefinitions&gt;
                      &lt;ColumnDefinition Width="Auto"/&gt;
                  &lt;/Grid.ColumnDefinitions&gt;
                  &lt;Grid.RowDefinitions&gt;
                      &lt;RowDefinition Height="Auto" /&gt;
                      &lt;RowDefinition Height="Auto" /&gt;
                      &lt;RowDefinition Height="Auto" /&gt;
                      &lt;RowDefinition Height="20" /&gt;
                      &lt;RowDefinition Height="Auto" /&gt;
                  &lt;/Grid.RowDefinitions&gt;
                  &lt;Label Grid.Row="0" Grid.Column="0" 
                Content="{Binding Path=DisplayName}" 
                HorizontalAlignment="Right"
              /&gt;
                  &lt;ListBox x:Name="lstConfigs" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Path=AllServerConfigs}" DisplayMemberPath="DisplayName"/&gt;
                  &lt;Button Grid.Row="2" Grid.Column="0" Content="Open" CommandTarget="{Binding ElementName=lstConfigs}" Command="{Binding Path=ShowSelectedServerConfigCommand}"
                          CommandParameter="{Binding ElementName=lstConfigs,Path=SelectedItem}"/&gt;
                  &lt;!-- A test to see if putting the view in would work --&gt;
                  &lt;ContentControl Grid.Row="4" Content="{Binding Path=All}" HorizontalAlignment="Left" VerticalAlignment="Top"/&gt;
  
              &lt;/Grid&gt;
          &lt;/Border&gt;
          &lt;Border Grid.Column="2" Style="{StaticResource MainBorderStyle}"&gt;
  
              &lt;ContentPresenter Content="{Binding Path=Workspaces}" ContentTemplate="{StaticResource WorkspacesTemplate}" /&gt;
          &lt;/Border&gt;
  
      &lt;/Grid&gt;
  &lt;/DockPanel&gt;
  </code></pre>
  
  <p></p>
  
  <p>Here is the <code>RepositoryViewModel</code>. If the <code>AllConfigsViewModel</code> is a workspace within the <code>workspaces</code> collection the listbox updates fine on that workspace tab control (as per Josh's example). However, the viewmodel properties don't update when placed directly into the MainWindow, nor does the ObservableCollection properties update directly in the MainWindow. If I force a <code>ListBox.items.refresh()</code> on the ListBox directly containing the OberservableCollection, when selection changes the properties update.</p>
  
  <pre><code>public class RepositoryViewModel : WorkspaceViewModel
  {
      readonly BuildRepository _buildRepository;
      ObservableCollection&lt;WorkspaceViewModel&gt; _workspaces;
  
      public RepositoryViewModel(string buildName)
      {
          base.DisplayName = buildName + " Properties";
          _buildRepository = new BuildRepository(buildName);
          _buildRepository.ServerConfigAdded += this.OnServerConfigAddedToRepository;
          this.CreateAllServerConfigs();
      }
  
      void CreateAllServerConfigs()
      {
          List&lt;ServerConfigViewModel&gt; all =
              (from config in _buildRepository.GetServerConfigs()
               select new ServerConfigViewModel(config, _buildRepository)).ToList();
  
          foreach (ServerConfigViewModel scvm in all)
              scvm.PropertyChanged += this.OnServerConfigViewModelPropertyChanged;
  
          this.AllServerConfigs = new ObservableCollection&lt;ServerConfigViewModel&gt;(all);
          this.AllServerConfigs.CollectionChanged += this.OnCollectionChanged;
  
      }
  
      public ObservableCollection&lt;ServerConfigViewModel&gt; AllServerConfigs
      {
          get;
          private set;
      }
  
      public ObservableCollection&lt;WorkspaceViewModel&gt; Workspaces
      {
          get
          {
              if (_workspaces == null)
              {
                  _workspaces = new ObservableCollection&lt;WorkspaceViewModel&gt;();
                  _workspaces.CollectionChanged += this.OnWorkspacesChanged;
              }
              return _workspaces;
          }
      }
  
      #region Private Helpers
      void AddNewServerConfig()
      {
          ServerConfig newServerConfig = new ServerConfig();
          ServerConfigViewModel workspace = new ServerConfigViewModel(newServerConfig, _buildRepository);
          this.Workspaces.Add(workspace);
          this.SetActiveWorkspace(workspace);
      }
  
      void ShowAllConfigs()
      {
          AllConfigsViewModel workspace =
             this.Workspaces.FirstOrDefault(vm =&gt; vm is AllConfigsViewModel)
             as AllConfigsViewModel;
  
          if (workspace == null)
          {
              workspace = new AllConfigsViewModel(_buildRepository);
              this.Workspaces.Add(workspace);
          }
          _all = workspace;
          base.OnPropertyChanged("All");
          this.SetActiveWorkspace(workspace);
      }
  
      AllConfigsViewModel _all;
  
      public AllConfigsViewModel All
      {
          get
          {
              return _all;
          }
      }
  
      void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
          if (e.NewItems != null &amp;&amp; e.NewItems.Count != 0)
              foreach (ServerConfigViewModel configVm in e.NewItems)
                  configVm.PropertyChanged += this.OnServerConfigViewModelPropertyChanged;
  
          if (e.OldItems != null &amp;&amp; e.OldItems.Count != 0)
              foreach (ServerConfigViewModel configVm in e.OldItems)
                  configVm.PropertyChanged -= this.OnServerConfigViewModelPropertyChanged;
  
      }
      void OnServerConfigViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
      {
          string DisplayName = "DisplayName";
          (sender as ServerConfigViewModel).VerifyPropertyName(DisplayName);
          if (e.PropertyName == DisplayName)
          {
              this.OnPropertyChanged("AllServerConfigs");
          }
      }
  
      void OnServerConfigAddedToRepository(object sender, ServerConfigAddedEventArgs e)
      {
          var viewModel = new ServerConfigViewModel(e.NewServerConfig, _buildRepository);
          this.AllServerConfigs.Add(viewModel);
          //this.OnPropertyChanged("AllServerConfigs");
      }
      #endregion
  }
  </code></pre>
  
  <p>Here is <code>ServerConfigViewModel</code></p>
  
  <pre><code>    public class ServerConfigViewModel : WorkspaceViewModel, IDataErrorInfo
      {
          readonly ServerConfig _serverConfig;
          readonly BuildRepository _buildRepository;
  
          #region Server Config Fields
          public string Name
          {
              get
              {
                  return _serverConfig.Name;
              }
              set
              {
                  if (value == _serverConfig.Name)
                  {
                      return;
                  }
  
                  _serverConfig.Name = value;
                  base.OnPropertyChanged("Name");
              }
          }
  
  
          #region Presentation Properties
          public override string DisplayName
          {
              get
              {
                  if (_serverConfig.Name == null)
                  {
                      return "New Server";
                  }
                  return _serverConfig.Name;
              }
          }
  
  
          #region Constructors
          public ServerConfigViewModel(ServerConfig serverConfig, BuildRepository buildRepository)
          {
              if (serverConfig == null)
              {
                  throw new ArgumentNullException("serverConfig");
              }
  
              if (buildRepository == null)
              {
                  throw new ArgumentNullException("buildRepository");
              }
  
              _serverConfig = serverConfig;
              _buildRepository = buildRepository;
          }
          #endregion
  
          #region Public Methods
          public void Save()
          {
              if (!_serverConfig.IsValid)
              {
                  throw new InvalidOperationException("Server Config is not valid, cannot be saved");
              }
              if (this.IsNewServerConfig)
              {
                  _buildRepository.AddServerConfig(_serverConfig);
              }
              base.OnPropertyChanged("DisplayName");
          }
          #endregion
      }
  </code></pre>
  
  <p>Here is <code>AllConfigsViewModel</code></p>
  
  <pre><code>    public class AllConfigsViewModel : WorkspaceViewModel
      {
          #region Properties
          readonly BuildRepository _buildRepository;
  
          #endregion
  
          #region Constructor
          public AllConfigsViewModel(BuildRepository buildRepository)
          {
              base.DisplayName = "All Configs";
              _buildRepository = buildRepository;
              _buildRepository.ServerConfigAdded += this.OnServerConfigAddedToRepository;
              this.CreateAllServerConfigs();
          }
  
          void CreateAllServerConfigs()
          {
              List&lt;ServerConfigViewModel&gt; all =
                  (from config in _buildRepository.GetServerConfigs()
                   select new ServerConfigViewModel(config, _buildRepository)).ToList();
  
              foreach (ServerConfigViewModel scvm in all)
                  scvm.PropertyChanged += this.OnServerConfigViewModelPropertyChanged;
  
              this.AllServerConfigs = new ObservableCollection&lt;ServerConfigViewModel&gt;(all);
              this.AllServerConfigs.CollectionChanged += this.OnCollectionChanged;
  
          }
  
  
          #endregion
  
          #region Public Interface
          public ObservableCollection&lt;ServerConfigViewModel&gt; AllServerConfigs
          {
              get;
              private set;
          }
          #endregion
  
          #region Event Handling
          void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
          {
              if (e.NewItems != null &amp;&amp; e.NewItems.Count != 0)
                  foreach (ServerConfigViewModel configVm in e.NewItems)
                      configVm.PropertyChanged += this.OnServerConfigViewModelPropertyChanged;
  
              if (e.OldItems != null &amp;&amp; e.OldItems.Count != 0)
                  foreach (ServerConfigViewModel configVm in e.OldItems)
                      configVm.PropertyChanged -= this.OnServerConfigViewModelPropertyChanged;
  
          }
          void OnServerConfigViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
          {
              string DisplayName = "DisplayName";
              (sender as ServerConfigViewModel).VerifyPropertyName(DisplayName);
              if (e.PropertyName == DisplayName)
              {
                  this.OnPropertyChanged("AllServerConfigs");
              }
          }
  
          void OnServerConfigAddedToRepository(object sender, ServerConfigAddedEventArgs e)
          {
              var viewModel = new ServerConfigViewModel(e.NewServerConfig, _buildRepository);
              this.AllServerConfigs.Add(viewModel);
              //this.OnPropertyChanged("AllServerConfigs");
          }
          #endregion
      }
  </code></pre>
  
  <p>The <code>MainWindowResources.xaml</code>, <code>WorkspaceViewModel</code> and <code>ViewModelBase</code> are all the same as in Josh Smith's MVVM article linked above (couldn't fit all the text in the body of the post).</p>
  
  <p>Update:
  Here is the <code>MainWindow.xaml.xs</code>. </p>
  
  <pre><code>namespace Server_Build_Config_Tool
  {
  
      public partial class MainWindow : System.Windows.Window
      {
          public MainWindow()
          {
              InitializeComponent();
          }
  
      }
  }
  
  namespace Server_Build_Config_Tool
  {
      /// &lt;summary&gt;
      /// Interaction logic for App.xaml
      /// &lt;/summary&gt;
      public partial class App : Application
      {
          protected override void OnStartup(StartupEventArgs e)
          {
              base.OnStartup(e);
  
              MainWindow window = new MainWindow();
  
              // Create the ViewModel to which 
              // the main window binds.
  
              var viewModel = new RepositoryViewModel("Test");
  
              // When the ViewModel asks to be closed, 
              // close the window.
              EventHandler handler = null;
              handler = delegate
              {
                  viewModel.RequestClose -= handler;
                  window.Close();
              };
              viewModel.RequestClose += handler;
  
              // Allow all controls in the window to 
              // bind to the ViewModel by setting the 
              // DataContext, which propagates down 
              // the element tree.
              window.DataContext = viewModel;
  
              window.Show();
          }
      }
  }
  </code></pre>