Wednesday, April 25, 2012

Don’t use non indexed “Item” CLR property in binding

Today I found rather strange problem.

I was getting strange exception from MS internal code related to bindings.

The exception was raised when code was trying to set SelectedItem of DataGrid control.

Stack Trace is very large but most important lines are as follows:

Message: Object reference not set to an instance of an object.
ExceptionType: System.NullReferenceException
Details
   at MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid(Object item)
   at MS.Internal.Data.PropertyPathWorker.DetermineWhetherDBNullIsValid()
   at MS.Internal.Data.PropertyPathWorker.get_IsDBNullValidForUpdate()
   at MS.Internal.Data.ClrBindingWorker.get_IsDBNullValidForUpdate()
   at System.Windows.Data.BindingExpression.ConvertProposedValue(Object value)
   at System.Windows.Data.BindingExpressionBase.UpdateValue()
   at System.Windows.Data.BindingExpression.UpdateOverride()
   at System.Windows.Data.BindingExpressionBase.Update()
   at System.Windows.Data.BindingExpressionBase.Dirty()
   at System.Windows.Data.BindingExpressionBase.SetValue(DependencyObject d, DependencyProperty dp, Object value)
   at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
   at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
   at MyNamespace.BusinessDataGrid.set_SelectedItem(Object value)

I could not understand the reason so I started to dig inside MS code using Reflector.

After some time I noticed the following piece of code inside method causing error:

private bool DetermineWhetherDBNullIsValid(object item)
{
    PropertyInfo info;
    PropertyDescriptor descriptor;
    DependencyProperty property;
    DynamicPropertyAccessor accessor;
    this.SetPropertyInfo(this._arySVS[this.Length - 1].info, out info, out descriptor, out property, out accessor);
    string columnName = (descriptor != null) ? descriptor.Name : ((info != null) ? info.Name : null);
    object arg = ((columnName == "Item") && (info != null)) ? this._arySVS[this.Length - 1].args[0] : null;
    return SystemDataHelper.DetermineWhetherDBNullIsValid(item, columnName, arg);
}

I assumed this could be problem – args of PropertyInfo where null.

It seems MS have hardcoded name “Item” as indexed property and assumed no one will ever name CLR property with “Item”.

After that I found the binding which was causing this code to execute:

image

Once commented everything worked fine.

To replicate the problem use this code:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            SetBinding(TagProperty, new Binding("Item")
            {
                RelativeSource = RelativeSource.Self,
                Mode = BindingMode.TwoWay,
            });

            Tag = 1;
            Tag = null;
        }

        public object Item { get; set; }
    }

 

To summarize – the rule to keep in mind – don’t name CLR property with name “Item” if you may use it in binding

 

Happy Coding

Kirill

1 comment:

  1. Thanks very much for this - saved me a load of grief.

    PS you will probably get more comments if your captchas weren't so hard to read :)

    ReplyDelete