Search code examples
c#wpfxamldatacontext

Binding error: WPF is looking for property on wrong object


I have a user control called ObservationEditorHost which (surprisingly) serves as a host page for another user control called ObservationEditor. I am getting a binding error in the Output window and the in the XAML of ObservationEditorHost the instance of ObservationEditor is underlined and the tooltop shows "Object reference not set to an instance of an object".

The error message in the output window is:

System.Windows.Data Error: 40 : BindingExpression path error: 'DataProviderID' property not found on 'object' ''ObservationEditorViewModel' (HashCode=38856023)'. BindingExpression:Path=DataProviderID; DataItem='ObservationEditorViewModel' (HashCode=38856023); target element is 'ObservationEditor' (Name=''); target property is 'ExclusiveDataProviderID' (type 'Int32')

Question: My objective of course is to resolve the binding error, which I am unable to do. The error message is a result of this binding (complete statement is also shown in code below)

ExclusiveDataProviderID="{Binding DataProviderID}"

My confusion comes from this portion of the error message: 'DataProviderID' property not found on 'object' ''ObservationEditorViewModel'.

Please note the following from the code below:

  • DataProviderID is a property of ObservationEditorHost (not ObservationEditor or ObservationEditorViewModel as is stated in the error message).
  • The DataContext for ObservationEditorHost is set to itself in the constructor.

Given the two parameters above, I do not understand how/why WPF is looking on ObservationEditorViewModel for DataProviderID as is stated in the error message.

Troubleshooting I have already done:

  • Deleted bin and obj directories from all projects
  • Restarted Visual Studio
  • Changed the name of DataProviderID to zzzDataProviderID to ensure that the error message in fact relates to this property and not a different property of the same name in a child control.
  • Searched entire solution for ObservationEditorViewModel to make sure I was not setting it somewhere else
  • All other bindings between ObservationEditorHost and ObservationEditor appear to work and there are no other error messages. The target of the binding, ExclusiveDataProviderID, is not set when bound to DataProviderId as is illustrated in my code. However when I bind to a constant as shown below, ExclusiveDataProviderID is set correctly:

    < components:ObservationEditor ShowSeriesSelector="True"ExclusiveDataProviderID="10">

--- Code ---

ObservationEditorHost.xaml (this is the entire control. Only xaml namespaces have been removed)

<dxdo:DocumentPanel x:Class="VVM.ObservationEditorHost"
         mc:Ignorable="d" Caption="Observation Editor"
         Description="Observation Editor">
    <components:ObservationEditor ShowSeriesSelector="True" ExclusiveDataProviderID="{Binding DataProviderID}"></components:ObservationEditor>
</dxdo:DocumentPanel>

ObservationEditorHost.xaml.cs (verbatim)

public partial class ObservationEditorHost : DocumentPanel, INotifyPropertyChanged
{
    private int _DataProviderID;
    public int DataProviderID 
    {
        get { return _DataProviderID; }
        set
        {
            if (_DataProviderID != value)
            {
                _DataProviderID = value;
                RaisePropertyChanged();
            }
        }
    }

    public ObservationEditorHost(DataProvider dp)
    {
        InitializeComponent();
        DataProviderID = dp.ID;
        DataContext = this;
    }

    #region INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged([CallerMemberNameAttribute] string propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

I cannot provide the full code for ObservationEditor.xaml.cs because it is very large. However I will provide this relevant portion:

public partial class ObservationEditor : UserControl
{

    public int ExclusiveDataProviderID
    {
        get { return (int)GetValue(ExclusiveDataProviderIDProperty); }
        set { SetValue(ExclusiveDataProviderIDProperty, value); }
    }

    public static readonly DependencyProperty ExclusiveDataProviderIDProperty =
        DependencyProperty.Register("ExclusiveDataProviderID", typeof(int), typeof(ObservationEditor), new PropertyMetadata(0, ExclusiveDataProviderID_Changed));

    public ObservationEditor()
    {
        InitializeComponent();
        ObservationEditorViewModel vm = ObservationEditorViewModel.Create().Result;
        DataContext = vm;
    }
}   

Solution

  • The problem is that you are attempting to work with the property in ObservationEditorHost but your DataContext for is the ObservationEditorViewModel.

    I think you should be able to use FindAncestor to locate the right control:

    ExclusiveDataProviderID="{Binding DataProviderID, RelativeSource=
        {RelativeSource FindAncestor, AncestorType={x:Type DocumentPanel}}}"
    

    • ObservationEditorHost's DC is the codebehind for ObservationEditorHost.
    • ObservationEditor has it's DC set manually to the ObservationEditorViewModel:

      ObservationEditorViewModel vm = ObservationEditorViewModel.Create().Result;
      DataContext = vm;
      

    The binding engine is attempting to resolve the binding for DataProviderID using the context that you set in the constructor, the viewmodel.

    The binding in this line:

     <components:ObservationEditor ExclusiveDataProviderID="{Binding DataProviderID}" />
    

    is occurring in the DC of the ObservationEditor, not the DC of the parent control (ObservationEditorHost).