Binding failures

Daz66

Active member
Joined
May 17, 2020
Messages
40
Programming Experience
Beginner
I'm a novice trying to conform to MVVM pattern, and understand that the main purpose of the ViewModel is to act as an intermediary between the Model and the View.
My issue is, I get binding errors when I target properties that I have exposed to my ViewModel.

I have a DataAccess method in my Model (LoadInvoices). In my ViewModel I call it like so:

C#:
public InvoicingViewModel()
        {
            LoadinvoiceData();
        }
        public void LoadinvoiceData()
        {
            _invoicingModel = new InvoicingModel();
            List<InvoicingModel> invoiceList = _invoicingModel.LoadInvoices();
            GetInvoiceData = new ObservableCollection<InvoicingModel>(invoiceList);
        }

        public ObservableCollection<InvoicingModel>? GetInvoiceData
        {
            get { return _invoicing; }
            set
            {
                _invoicing = value;
                OnPropertyChanged(nameof(GetInvoiceData));
            }
        }
This works as it should and I use it to populate a DataGrid. I use the SelectedItem of the grid to send SelectedItem to a child window.


The child window DataContext is the corresponding ViewModel, but when I try to bind a control in my child view I get the binding error:

CompanyId property not found on object of type InvoicingViewModel.

C#:
<Label Content="Company Id:"
                           FontSize="14"
                           FontWeight="Bold"
                           HorizontalAlignment="Right" />
                    <TextBlock Text="{Binding Path=CompanyId}" />

The program compiles, runs and returns correct data but with binding failures which is niggling me.

If I use the Model DataContext, no errors but I feel that's not what I should be doing. The obvious solution is to create the model properties in the viewModel, which would question the need for a model?

Any help appreciated.
 
The view model should not be constructing an instance of the model. The model should be given to the view model via dependency injection.

Anyway, you are still not giving us enough information. You are showing us code from your InvoicingViewModel, but you are saying that the error happens in the child window and child view model. Where is the code for that child view model? How is the DataContext set in that child view?
 
I only have the InvoicingViewModel for both windows, the parent view is simply a DataGrid that is populated with invoices.
On row selection the data is sent to the child window to create an invoice. There are a few simple controls in the child, the DataContext of the child is set in the xaml, I don't have any code behind at all:

:
 <Window.DataContext>
        <viewmodels:InvoicingViewModel />
    </Window.DataContext>
I thought using 'Binding Path' was sufficient to bind to the property via the ViewModel:
C#:
<StackPanel>
                <Label Content="Company Id:"
                       FontSize="14"
                       FontWeight="Bold"
                       HorizontalAlignment="Right" />
                <TextBlock Text="{Binding Path=CompanyId}" />
            </StackPanel>

Although the Data is passed to the child window, the Binding Failure is telling me the 'CompanyId property not found on object of type InvoicingViewModel'.

I was under the impression creating an instance of the Model in the ViewModel was the correct approach?
If I change DataContext to the Model I get no errors as I'm obviously accessing the properties directly, but as i'v been learning I have been under the assumption all 'transactions' should go though the ViewModel?

Let me know if you need any more info, I'd be keen to learn how the Model is passed to the ViewModel via Dependancy Injection if you have the time please.
 
Based on the code you presented in post #1, I don't see a CompanyId public property or field on your view model, so the error looks to be accurate.
 
No there isn't, it's the general premise of my post.

I have been under the assumption I can access the property via my ViewModel using the method in my first post. If that's not the case; and probably not the first time the question has been asked, why have a Model?

I'm obviously barking up the wrong tree, I'll research dependancy Injection, thanks.
 
The View Model adapts a Model for what the View needs. For example, a Model may have both the given name and the surname:
C#:
record class Person(string GivenName, string Surname);
but the View just needs a name:
XML:
<TextBlock Text="{Binding Name}" />
And so the View Model would be created as:
C#:
record class PersonViewModel(Person person)
{
    public string Name => $"{_person.GivenName} {_person.Surname}";
}
 
Thank you @Skydiver, I'm deep in the Dependancy Injection rabbit hole at the moment based on your suggestion.
Packages installed, and initiated...I think.
App.xaml.cs:
public partial class App : Application
{
    public static IHost? AppHost { get; private set; }

    public App()
    {
        AppHost = Host.CreateDefaultBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddSingleton<MainWindow>();
                services.AddTransient<InvoicingViewModel>();
                services.AddSingleton<InvoicingModel>();
            })
            .Build();
    }
    protected override async void OnStartup(StartupEventArgs e)
    {
        await AppHost!.StartAsync();

        var startupWindow = AppHost.Services.GetRequiredService<MainWindow>();
        startupWindow.Show();

        base.OnStartup(e);
    }
    protected override async void OnExit(ExitEventArgs e)
    {
        await AppHost!.StopAsync();
        base.OnExit(e);
    }
}

I'm hoping this is a better approach, removing the 'new' instance of my Model. Unfortunately have no data, just a blank DataGrid.

ViewModel:
public InvoicingViewModel()
    {
   
    }
    public InvoicingViewModel(InvoicingModel invoicingModel)
    {
        _invoicingModel = invoicingModel ?? throw new ArgumentNullException(nameof(invoicingModel));
        LoadInvoiceData();
    }
   
    public void LoadInvoiceData()
    {
        List<InvoicingModel> invoiceList = _invoicingModel.LoadInvoices();
        GetInvoiceData = new ObservableCollection<InvoicingModel>(invoiceList);
    }

    public ObservableCollection<InvoicingModel>? GetInvoiceData
    {
        get { return _invoicing; }
        set
        {
            _invoicing = value;
            OnPropertyChanged(nameof(GetInvoiceData));
        }
    }

Any pointers please.
 
Last edited:
Update:
I have extracted interface from my Model and using it in App.xaml like so.
C#:
public App()
    {
        AppHost = Host.CreateDefaultBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddSingleton<MainWindow>();
                services.AddTransient<InvoicingViewModel>();
                //services.AddSingleton<InvoicingModel>();
                services.AddTransient<IInvoicingModel, InvoicingModel>();
            })
            .Build();
    }

Adding to MainWindow constructor
C#:
public partial class MainWindow : Window
{
    private readonly IInvoicingModel _invoicingModel;

    public MainWindow(IInvoicingModel invoicingModel)
    {
        InitializeComponent();
       
        Loaded += MainWindow_Loaded;
        this._invoicingModel = invoicingModel;
    }

This is where may be going wrong because I still have no data in my DataGrid
C#:
   public InvoicingViewModel()
    {
   

    }
    public InvoicingViewModel(InvoicingModel invoicingModel)
    {
        _invoicingModel = invoicingModel ?? throw new ArgumentNullException(nameof(invoicingModel));
        LoadInvoiceData();
    }

    public void LoadInvoiceData()
    {
        List<InvoicingModel> invoiceList = _invoicingModel.LoadInvoices();
        GetInvoiceData = new ObservableCollection<InvoicingModel>(invoiceList);
    }

    public ObservableCollection<InvoicingModel>? GetInvoiceData
    {
        get { return _invoicing; }
        set
        {
            _invoicing = value;
            OnPropertyChanged(nameof(GetInvoiceData));
        }
    }


C#:
<DataGrid x:Name="dgInvoices"
                  AutoGenerateColumns="False"
                  Grid.Row="1"
                  ItemsSource="{Binding GetInvoiceData}"
                  SelectedItem="{Binding SelectedInvoice, Mode=TwoWay}">

I am trying hard here, any help appreciated, thanks.

And presumably I don't need to touch this interface, as it inherits from my Model?


C#:
public interface IInvoicingModel
    {
        decimal? Amount { get; set; }
        string? City { get; set; }
        string? CompanyAddress { get; set; }
        string? CompanyId { get; set; }
        string? CompanyName { get; set; }
        string? CustomerId { get; set; }
        string? CustomerReference { get; set; }
        string? Description { get; set; }
        int Id { get; set; }
        DateTime? InvoiceDate { get; set; }
        int InvoiceNumber { get; set; }
        string? Postcode { get; set; }
        ICommand? SaveAsPdfCommand { get; }

        List<InvoicingModel> LoadInvoices();
    }
 
Did you set the data context?
 
Did you set the data context?

It's set in my ViewModel....or maybe this should be different now?

A DataTemplate for the grid maybe?

C#:
 <Window.DataContext>
        <viewModel:InvoicingViewModel />
    </Window.DataContext>

I've tried the in my ViewModel too, targeting the interface.
C#:
 public InvoicingViewModel()
    {
    
    }
    private readonly IInvoicingModel? _invoicingModel;

    public InvoicingViewModel(IInvoicingModel invoicingModel)
    {
        _invoicingModel = invoicingModel ?? throw new ArgumentNullException(nameof(invoicingModel));
        LoadInvoiceData();
    }

    public void LoadInvoiceData()
    {
        List<InvoicingModel> invoiceList = _invoicingModel.LoadInvoices();

        // Convert List<InvoicingModel> to List<IInvoicingModel>
        List<IInvoicingModel> convertedInvoiceList = invoiceList.Cast<IInvoicingModel>().ToList();

        GetInvoiceData = new ObservableCollection<IInvoicingModel>(convertedInvoiceList);
    }

    private ObservableCollection<IInvoicingModel>? _invoicing;
    public ObservableCollection<IInvoicingModel>? GetInvoiceData
    {
        get { return _invoicing; }
        set
        {
            _invoicing = value;
            OnPropertyChanged(nameof(GetInvoiceData));
        }
    }

I think I need to set the ItemsSource of the DataGrid to something else but can't work out what.
 
Does your view model constructor get called?

Does your getter get called at the right time after you've loaded the data?

Personally, I think there is something very wrong with this code:
C#:
List<InvoicingModel> invoiceList = _invoicingModel.LoadInvoices();

// Convert List<InvoicingModel> to List<IInvoicingModel>
List<IInvoicingModel> convertedInvoiceList = invoiceList.Cast<IInvoicingModel>().ToList();

GetInvoiceData = new ObservableCollection<IInvoicingModel>(convertedInvoiceList);

Why are you calling LoadInvoices() on your model?

Why are you casting each of the items in the returned list of InvoicingModel to IInvoicingModel? Are you sure that conversion is returning you a non-empty list?

What most people normally do for view models that are intended to view collections of items T is to have an observable collection of type T. The constructor for the view model would take a repository (or DBContext for EF users, or a factory delegate for users who don't talk to a database). They would then call the repository (or DBContext, or factory method) to get back a collection of T. That returned value is passed on to the observable collection of T. (Typically, that call to the repository is also done in a lazy manner to only get the data on demand.)
 
LoadInvoices() is my DataAccess method, I use the Model for DataAccess/Save.

Tried this approach but no joy.


C#:
public class InvoicingViewModel : ObservableObject
{
    private readonly IInvoicingModel _invoicingModel;

    public InvoicingViewModel(IInvoicingModel invoicingModel)
    {
        _invoicingModel = invoicingModel ?? throw new ArgumentNullException(nameof(invoicingModel));
        GetInvoiceData = new ObservableCollection<InvoicingModel>();
    }

    public void LoadInvoiceData()
    {
        List<InvoicingModel> invoiceList = _invoicingModel.LoadInvoices();
        foreach (var invoice in invoiceList)
        {
            GetInvoiceData.Add(invoice);
        }
    }

    private ObservableCollection<InvoicingModel> _invoicing;
    public ObservableCollection<InvoicingModel> GetInvoiceData
    {
        get { return _invoicing; }
        set
        {
            _invoicing = value;
            OnPropertyChanged(nameof(GetInvoiceData));
        }
    }
}

I'm going to try a simple control and code behind to test the DI and I'll persevere, thanks for your help so far.
 
What most people normally do for view models that are intended to view collections of items T is to have an observable collection of type T. The constructor for the view model would take a repository (or DBContext for EF users, or a factory delegate for users who don't talk to a database). They would then call the repository (or DBContext, or factory method) to get back a collection of T. That returned value is passed on to the observable collection of T. (Typically, that call to the repository is also done in a lazy manner to only get the data on demand.)



C#:
public InvoicingViewModel(IRepository<InvoicingModel> repository)
    {
        _repository = repository;
        Invoices = new ObservableCollection<IInvoicingModel>(_repository.GetAll());
    }


    private ObservableCollection<IInvoicingModel>? _invoices;
    public ObservableCollection<IInvoicingModel>? Invoices
    {
        get { return _invoices; }
        set
        {
            _invoices = value;
            OnPropertyChanged(nameof(Invoices));
        }

Thanks for the nudge in the right direction, got a bit of rewinding to do now building a ClassLibrary, all good fun.
 

Latest posts

Back
Top Bottom