wpftreeviewnullreferenceexceptionpropertychanged

NullReferenceException when using Treeview and PropertyChanged


I'm getting a NullReferenceException when updating a bound property in my view model. This only happens when I use the TreeView control in the view. If I replaced it with a list, the exception goes away.

This is where the debugger breaks in my code:

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

About maybe 30 calls later, through many PresentationFramework and WindowsBase assemblies, the exception actually occurs here:

PresentationFramework.dll!System.Windows.StyleHelper.EvaluateOldNewStates()

This is the TreeView:

<TreeView ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups}"
          ItemTemplate="{StaticResource categoryTemplate}" DockPanel.Dock="Bottom" 
          SelectedItemChanged="TreeView_SelectedItemChanged"/>

And if I use this ListBox instead, the problem goes away:

<ListBox DockPanel.Dock="Bottom" ItemsSource="{Binding ApplicationServers}" DisplayMemberPath="Name"
         SelectedItem="{Binding SelectedApplicationServer}" Height="auto"/>

I'm not sure that this will help, but here is the property that gets updated:

public ObservableCollection<ApplicationServer> ApplicationServers
{
    get { return this._applicationServers; }
    private set
    {
        this._applicationServers = value;
        this.NotifyPropertyChanged(() => this.ApplicationServers);
    }
}

And here is the call to update that property:

this.ApplicationServers = new ObservableCollection<ApplicationServer>(ApplicationServerLogic.GetAll().ToList());

Has anyone experienced anything like this?
I don't know what's causing the issue, and I'm tempted to just use the ListBox.

Indeed, I pretty much have to use the ListBox at the moment.
How can I even troubleshoot this? Is it a bug in the PresentationFramework assembly, maybe?

Also, this is the code-behind of my view, showing the handling of the item changed event.

private void TreeView_SelectedItemChanged(object sender, System.Windows.RoutedPropertyChangedEventArgs<object> e)
{           
    ((ApplicationServerViewModel)DataContext).SelectedApplicationServer = e.NewValue as ApplicationServer;
}

Edit

Someone asked for more code, so here it is:

<CollectionViewSource x:Key="cvs" Source="{Binding ApplicationServers}">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="DeploymentEnvironment"/>
    </CollectionViewSource.GroupDescriptions>
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="Name" Direction="Ascending"/>
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

<!-- Our leaf nodes (server names) -->
<DataTemplate x:Key="serverTemplate">
    <TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>

<!-- Note: The Items path refers to the items in the 
     CollectionViewSource group (our servers).
     The Name path refers to the group name. -->
<HierarchicalDataTemplate x:Key="categoryTemplate" ItemsSource="{Binding Path=Items}"
                          ItemTemplate="{StaticResource serverTemplate}">
    <TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
</HierarchicalDataTemplate>

Solution

  • While it doesn't answer the why in your case; one approach would be to add the items to the collection instance versus changing out the instance via the setter. Remove the exposed setter and simply add the items as needed which will raise the CollectionChanged event on the underlying collection which will provide you with what you are ultimately after.

            public ObservableCollection<ApplicationServer> ApplicationServers
            {
                get { return this._applicationServers; }
            }
    
            ApplicationServers.Add(...);