4

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;

        }

    }          
Brian Var
  • 6,029
  • 25
  • 114
  • 212
  • can you provide the relevant `xaml`? – Ethan Cabiac Nov 19 '15 at 13:23
  • @EthanCabiac just edited the post to include the xaml. – Brian Var Nov 19 '15 at 13:29
  • 1
    I Guess you're missing something on your **Window.DataContext**, I copied your source and couldn't reproduce the problem. Also remove the Manual Column definition **AutoGenerateColumns="True"** is enough to see your datagrid works – Hossein Shahdoost Nov 19 '15 at 15:49
  • @HosseinShahdoost I added the code to my question, showing how I set the data context. I'm confused as to what the issue is here, the grid is bound to the Customer collection in the MainVM. The grid columns are then bound to each field from that collection. But I still get this binding path expression. Should I be defining each of the Model's fields in the VM also? – Brian Var Nov 19 '15 at 16:41
  • 1
    Try filling your observable collection with constant data. remove the **CustomerDataService** from the code. and fill it with something like : Customers = new ObservableCollection { new CustomerModel() {FirstName = "myname", LastName = "myfamily"}, new CustomerModel() {FirstName = "yourname", LastName = "yourfamily"} }; – Hossein Shahdoost Nov 19 '15 at 17:21
  • okay so I tried the above, filling the Customers collection with constant data, and the data is showing in the grid when I run. Guess that means there is a problem with the `CustomerDataService` ? Any suggestions how I can debug that – Brian Var Nov 19 '15 at 17:45
  • @BrianJ Definitely, Show me the code. and I'll tell the problem – Hossein Shahdoost Nov 19 '15 at 18:49
  • @HosseinShahdoost this is the data service from my repo on GitHub: https://github.com/BrianJVarley/MongoDB_App/blob/master/MongoDBApp/Services/CustomerDataService.cs It delegates calls to the CustomerRespository – Brian Var Nov 19 '15 at 18:51

5 Answers5

9

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.

Community
  • 1
  • 1
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • I've since fixed that error, I had to set the binding as SelectedCustomer.FirstName etc. I encountered another problem with the data service that pulls in the data from the repository, see my comments above – Brian Var Nov 19 '15 at 21:24
  • @BrianJ If you have a different issue with obtaining records from the DataService, it would probably be best to open a different question about it. This code and question is related to your first issue, so you're unlikely to get the attention you need about your second issue. – Rachel Nov 19 '15 at 21:33
1

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>
Ethan Cabiac
  • 4,943
  • 20
  • 36
  • I tried that solution previously, setting `Binding="{Binding FirstName}"` etc.. but I get the error: `"System.Windows.Data Error: 40 : BindingExpression path error: 'FirstName' property not found on 'object' ''MainViewModel' (HashCode=19160433)'. BindingExpression:Path=FirstName; DataItem='MainViewModel' (HashCode=19160433); target element is 'TextBox' (Name='fNameTbx'); target property is 'Text' (type 'String')"` Any other ideas? – Brian Var Nov 19 '15 at 14:19
  • @BrianJ so you get the same exact error regardless of whether you use `Customers.` or not? – Ethan Cabiac Nov 19 '15 at 14:26
  • yes, if I use Customer.FirstName or FirstName, same for all fields I get this error..I don't have the individual properties defined in the VM, but I set the data grid binding instead to an observable collection of type CustomerModel which implements INPC in the VM, as you can see in my code above ^^ – Brian Var Nov 19 '15 at 14:28
  • I see that you are binding the DataGrid to the `Customers` property, but the error message is saying that the binding engine is looking for those properties on the VM (which don't exist there, obviously). – Ethan Cabiac Nov 19 '15 at 14:31
  • 1
    Also I notice you are using `AutoGenerateColumns="True"` but then you are manually defining columns? – Ethan Cabiac Nov 19 '15 at 14:32
  • Okay I've corrected that property to `AutoGenerateColumns="False"`, I've added the data context code in the View code behind to my question above. What I understood is that I set the ItemSource of the grid to the Customer collection, which is in the MainVM data context for that View. Then the properties from that collection will be available to the UI? Can you advise on my mistake here.. – Brian Var Nov 19 '15 at 16:37
0

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.

MattE
  • 1,044
  • 1
  • 14
  • 34
0
public Window()
{
      this.DataContext = this;
      InitializeComponent();
}
public string Name {get;set;}
//xaml
<TextBlock Text="{Binding Name}"/>

this.DataContext = this; before InitializeComponent();
(DataContext need available before load xaml in InitializeComponent())

Properties Name should be public and { get; }
(If private then wpf can't access)

0

I used to have the same trouble and the solution to make it work was only to use a property instead of a class field. The Class field seem to have trouble with the binding. Maybe you should use property in your view model instead of fields.

// This field not working

public SolidColorBrush BrushColor;

// But this property the binding worked

public SolidColorBrush BrushColor { get; set; }