How to resolve a “Binding Expression Path error” i

2019-04-11 14:55发布

问题:

I'm binding an observsable collection of model objects to a data grid. But when I set the binding to the collection, I get a path error to the peoprties.

In debugging this issue, I've checked that the public properties in the CustomerModel are correctly named in the DataGrid binding. And also that the collection being returned to the model isn't empty. I also checked that the data context is set correctly in the View's code behind.

I think it might be an error due to the way I've specified the binding path in the xaml..

The full details of the binding error is as follows, for each field:

System.Windows.Data Error: 40 : BindingExpression path error: 'FirstName' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=FirstName; DataItem='MainViewModel' (HashCode=55615518); target element is 'TextBox' (Name='fNameTbx'); target property is 'Text' (type 'String')

System.Windows.Data Error: 40 : BindingExpression path error: 'LastName' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=LastName; DataItem='MainViewModel' (HashCode=55615518); target element is 'TextBox' (Name='lNameTbx'); target property is 'Text' (type 'String')

System.Windows.Data Error: 40 : BindingExpression path error: 'Email' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=Email; DataItem='MainViewModel' (HashCode=55615518); target element is 'TextBox' (Name='emailTbx'); target property is 'Text' (type 'String')

Could anyone point me in the right direction, in order to debug this further?

DataGrid binding path and source are set as follows:

                   <DataGrid Name="infogrid"
                              Grid.Row="0"
                              Grid.RowSpan="3"
                              Grid.Column="1"
                              Grid.ColumnSpan="3"
                              AutoGenerateColumns="False"
                              ItemsSource="{Binding Customers}"
                              SelectedItem="{Binding SelectedCustomer}">
                        <DataGrid.Columns>
                            <DataGridTextColumn Binding="{Binding Customers.Id}" Header="ID" />
                            <DataGridTextColumn Binding="{Binding Customers.FirstName}" Header="First Name" />
                            <DataGridTextColumn Binding="{Binding Customers.LastName}" Header="Last Name" />
                            <DataGridTextColumn Binding="{Binding Customers.Email}" Header="Email" />
                        </DataGrid.Columns>
                    </DataGrid>

The View Model contains an Observable collection of type CustomerModel, called Customers. This is what I've set the DataGrid ItemSource to. (I've removed other code from VM for readability)

namespace MongoDBApp.ViewModels
{

    class MainViewModel : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        private ICustomerDataService _customerDataService;


        public MainViewModel(ICustomerDataService customerDataService)
        {
            this._customerDataService = customerDataService;
            QueryDataFromPersistence();
        }



        private ObservableCollection<CustomerModel> customers;
        public ObservableCollection<CustomerModel> Customers
        {
            get
            {
                return customers;
            }
            set
            {
                customers = value;
                RaisePropertyChanged("Customers");
            }
        }



        private void QueryDataFromPersistence()
        {
            Customers = _customerDataService.GetAllCustomers().ToObservableCollection();

        }



        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }



    }
}

And these are the fields that in the CustomerModel, so not sure why the properties are not being found during binding:

   public class CustomerModel : INotifyPropertyChanged
    {

        private ObjectId id;
        private string firstName;
        private string lastName;
        private string email;


        [BsonElement]
        ObservableCollection<CustomerModel> customers { get; set; }

        /// <summary>
        /// This attribute is used to map the Id property to the ObjectId in the collection
        /// </summary>
        [BsonId]
        public ObjectId Id { get; set; }

        [BsonElement("firstName")]
        public string FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
                RaisePropertyChanged("FirstName");
            }
        }

        [BsonElement("lastName")]
        public string LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
                RaisePropertyChanged("LastName");
            }
        }

        [BsonElement("email")]
        public string Email
        {
            get
            {
                return email;
            }
            set
            {
                email = value;
                RaisePropertyChanged("Email");
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

This is how the data context is set in the View's code behind:

    public partial class MainView : Window
    {
        private MainViewModel ViewModel { get; set; }
        private static ICustomerDataService customerDataService = new CustomerDataService(CustomerRepository.Instance);


        public MainView()
        {
            InitializeComponent();
            ViewModel = new MainViewModel(customerDataService);
            this.DataContext = ViewModel;

        }

    }          

回答1:

These binding errors are not related to your DataGrid.

They indicate that you have 3 TextBoxes somewhere of the names fNameTbx, lNameTbx, and emailTbx. A DataGrid does not generate it's items with a Name property, so it is not the one causing these binding errors.

When trying to read binding errors, it's best to break them up by semi-colons and read them backwards, as demonstrated here.

For example,

System.Windows.Data Error: 40 : BindingExpression path error: 'FirstName' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=FirstName; DataItem='MainViewModel' (HashCode=55615518); target element is 'TextBox' (Name='fNameTbx'); target property is 'Text' (type 'String')

Can also be read as

  • target property is 'Text' (type 'String')
  • target element is 'TextBox' (Name='fNameTbx');
  • DataItem='MainViewModel' (HashCode=55615518);
  • BindingExpression path error: 'FirstName' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=FirstName;

Meaning somewhere you have

<TextBox Name="fNameTbx" Text="{Binding FirstName}" />

Where the DataContext of this TextBox is of type MainViewModel. And MainViewModel does not have a property of FirstName.

I'd recommend searching your project for those names, or you could use a tool like Snoop to debug databindings and DataContext issues at runtime.



回答2:

The exceptions indicate that the DataBinding engine is looking for the fields FirstName, LastName, etc. on MainViewModel as opposed to CustomerModel.

You don't need to specify the property Customers in the individual binding expressions for the columns:

<DataGrid.Columns>
  <DataGridTextColumn Binding="{Binding Id}" Header="ID" />
  <DataGridTextColumn Binding="{Binding FirstName}" Header="First Name" />
  <DataGridTextColumn Binding="{Binding LastName}" Header="Last Name" />
  <DataGridTextColumn Binding="{Binding Email}" Header="Email" />
</DataGrid.Columns>


回答3:

I was having the same issue when I had the TextBlock Text Binding inside of a DataTemplate and I ended up having to do:

Text={Binding DataContext.SuccessTxt}

to get it to work properly. Try adding "DataContext." in front of the property and see if that works.