Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting "Key cannot be null" after implementing INotifyPropertyChanged on C# object

I am developing a WPF application in C# 4.5.2 in Visual Studio 2017. Within the application I have a custom object that gets rolled into an ObservableCollection<T> for later processing. I want to be able to handle this CollectionChanged event if/when necessary.

I added the appropriate decoration to the class (: INotifyPropertyChanged), added the event and handler, and attributed each property in my object with the Interface PropertyChanged:

public class OrderLineItem : INotifyPropertyChanged
{
    private int _lineItemNumber;
    public int LineItemNumber
    {
        get => _lineItemNumber;
        set { _lineItemNumber = value; OnPropertyChanged(); }
    }

    private int _quantity;
    public int Quantity
    {
        get => _quantity;
        set { _quantity = value; OnPropertyChanged(); }

    }

    private string _partNumber;
    public string PartNumber
    {
        get => _partNumber;
        set { _partNumber = value; OnPropertyChanged(); }

    }

    private Hinge _hinged;
    public Hinge Hinging
    {
        get => _hinged;
        set { _hinged = value; OnPropertyChanged(); }
    }

    private Finish _finished;
    public Finish Finished
    {
        get => _finished;
        set { _finished = value; OnPropertyChanged(); }
    }

    private decimal _unitPrice;
    public decimal UnitPrice
    {
        get => _unitPrice;
        set { _unitPrice = value; OnPropertyChanged(); }
    }

    private decimal _modifyPrice;
    public decimal ModifyPrice
    {
        get => _modifyPrice;
        set { _modifyPrice = value; OnPropertyChanged(); }
    }

    private decimal _extendedPrice;
    public decimal ExtendedPrice
    {
        get => _extendedPrice;
        set { _extendedPrice = value; OnPropertyChanged(); }
    }

    private List<string> _modifications;
    public List<string> Modifications
    {
        get => _modifications;
        set { _modifications = value; OnPropertyChanged(); }
    }

    private CabinetType _type;
    public CabinetType Type
    {
        get => _type;
        set { _type = value; OnPropertyChanged(); }
    }

    private string _display;
    public string Display
    {
        get => _display;
        set { _display = value; OnPropertyChanged(); }
    }

    public enum Hinge { None = 0, L, R, BD }
    public enum Finish { None = 0, L, R, B }

    public OrderLineItem()
    {
        LineItemNumber = -1;
        Quantity = -1;
        PartNumber = string.Empty;
        Hinging = Hinge.None;
        Finished = Finish.None;
        UnitPrice = 0.00m;
        ModifyPrice = 0.00m;
        ExtendedPrice = 0.00m;
        Modifications = new List<string>();
        Type = CabinetType.None;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

In my MainWindow.xaml.cs file I have added the .CollectionChanged handler to my ObservableCollection<T> and the .PropertyChanged handler to my object:

private void AddedItemsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach (OrderLineItem newItem in e.NewItems)
        {
            _addedItems.Add(newItem);
            newItem.PropertyChanged += OnItemPropertyChanged;
        }
    }

    if (e.OldItems != null)
    {
        foreach (OrderLineItem oldItem in e.OldItems)
        {
            _addedItems.Add(oldItem);
            oldItem.PropertyChanged -= OnItemPropertyChanged;
        }
    }
}

private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    OrderLineItem item = sender as OrderLineItem;
    if (item != null) _addedItems.Add(item);
}

Any time I add an item to the collection I add the PropertyChanged handler to it:

...

item.PropertyChanged += ItemOnPropertyChanged;

...

This collection gets displayed in a DataGrid via the .ItemsSource property.

The issue in this question's title only happens after implementing the INotifyPropertyChanged and its associated methods/interfaces etc.

The stack trace I was able to get does not provide me with any useful information (line number, file, method etc) to attempt to debug this. I did enable all of the debugging options to make sure I wasn't ignoring any potential exceptions. Here is a pastebin of the exception. The line number indicated is this:

 AppDomain.CurrentDomain.UnhandledException += 
  (sender, args) => throw new Exception("Unhandled exception: " + args.ExceptionObject);

If I do not have this line, or comment it out this is a pastebin of the exception I get instead.

Any idea of how I should proceed trying to solve this, besides not implementing the CollectionChanged event?

like image 341
Anders Avatar asked Mar 21 '26 02:03

Anders


1 Answers

There seem to be a relation with INotifyPropertyChanged and Binding rules but I don't know which one.

I got into the same issue: implementing INotifyPropertyChanged on a PropertyGrid item triggered the Key cannot be null binding error. By the way, it was one of the most useless and random error logs I have seen.

Cannot save value from target back to source.
BindingExpression:[info on an unrelated parent binding...]
ArgumentNullException:'System.ArgumentNullException: Key cannot be null.

After some time, I found out about weird Binding behaviors in old wpf versions where adding Path= to the bindings could fix it, but that wasn't it.

I actually had a Binding in code-behind like so:

void GetBinding(PropertyItem propertyItem)
{
  return new Binding($"({propertyItem.PropertyName})")
  {
    //[Source, Mode, etc...] 
  }
}

And the code worked for both properties and attached properties, but stopped working right after implementing the property notifier on this class. Maybe a more strict rule got triggered and flagged my property as not being an "attached" one, but it worked before without the interface.

So, I removed the parenthesis and it works now.

  return new Binding($"{propertyItem.PropertyName}")
like image 164
ChoKaPeek Avatar answered Mar 23 '26 17:03

ChoKaPeek