Search code examples
c#wpfmvvmviewmodelicommand

WPF - MVVM - View doesn't update when changing properties of a VM instance


I'm fairly new to MVVM and WPF so take everything what I'm saying with a grain of salt. My problem is quite complex and I've been trying to find a solution for the past day now. To no avail...

Context

I'm trying to make a POS system by myself. Everything was going according to plan. Until this: When there's a barcode being scanned that's too short I open up a small view that asks for the size and color-code of the article. This is done via an ICommand.

I can make an article and add it to my ObservableCollection. I access this observable collection with the instance I created off the ViewModel.

But now the weird thing happens, I update a "total" texblock and a "amount" textblock as well. These changes go through and I can see the properties being filled and my OnPropertyChanged is being triggered but there's nothing to see on the view. The weird thing is that my ObservableCollection is changing but my textboxes aren't. I'm pretty convinced there's nothing going wrong with my ICommand because I can create the actual article and add it to my cart. I've been trying to debug for a while now and I can see that it's going through the setter of my properties with the correct value. And the weird part is, if I added an article like that the textblock's will be wrong but when I add a new article in another way (via the original VM - not a new window) it takes the correct total and amount. It's like my view doesn't want to update and I have no clue on how I can force it to update.

I've been trying to find a way to make everything work and hopefully somebody can give me an insight on what I'm doing wrong because I'm pretty much clueless! :D

There might be typing errors or stuff like that in the code below but I'm quite sure you can ignore the most basic stuff. I'm implementing everything, the change is going through it's just NOT SHOWING UP.

Code

Example property that is refusing to show up after changing it:

(these properties are inside my main VM)

private static string _totalstr { get; set; }
    public string TotalStr
    {
        get { return _totalstr; }
        set
        {
            _totalstr = value;
            OnPropertyChanged(nameof(TotalStr));
        }
    }

Cart that is showing changes after I add an article

private static ObservableCollection<Art> _cart;
    public ObservableCollection<Art> Cart
    {
        get
        {
            return _cart;
        }
        set
        {
            _cart = value;
            OnPropertyChanged(nameof(Cart));

        }
    }

Asking for size - VM

This is a small version because there's more than that happening but this is the gist of it. There's parts in my native language as well so I tried translating to the best of my abilities.

public ICommand SizeColorCommand { get; set; }
public SizeColorViewModel()
        {
            this.SizeColorCommand = new SizeColorCommand(this);
        }

    public void function(object parameter)
    {
        MainViewModel.Instance.Cart.Add(artikel);
        MainViewModel.Instance.Total += (double)article.EVkp;
        MainViewModel.Amount++;
        MainViewModel.Instance.TotaalStr = String.Format("{0:c}", MainViewModel.Instance.Total);
    }
                

Size Color Command

class SizeColorCommand: ICommand
{
    public SizeColorViewModel VM { get; set; }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public SizeColorCommand(SizeColorViewModel vm)
    {
        VM = vm;
    }

    public bool CanExecute(object parameter)
    {
        string query = parameter as string;

        if (string.IsNullOrWhiteSpace(query))
            return false;
        return true;
    }

    public void Execute(object parameter)
    {
        VM.function(parameter);
    }
}

Asking for size - XAML

            <TextBox>
                <TextBox.InputBindings>
                    <KeyBinding Key="Enter" Command="{Binding SizeColorCommand}" CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" />
                </TextBox.InputBindings>
            </TextBox>

MainVM - XAML

            <TextBlock Text="{Binding TotalStr, Mode=TwoWay}"/>

Screenshot#

picture of the POS, it's in Dutch so sorry in advance, Stuks/Aantal = AMOUNT

Thank you so much if you've come this far to try and help me. I hope I can learn and fix this issue. I've read a lot this afternoon and singleton might not be the best thing to do but I can't seem to think of a better way.


Solution

    1. Ensure that MainViewModel.Instance is the DataContext of your MainWindow. The easiest way to do this is by specifying the attribute: DataContext="{x:Static local:MainViewModel.Instance}"

    2. You need to specify your TextBox.Text bindings as {Binding PropertyName, Mode=TwoWay}.

    Edit:

    If Cart is being exposed in the UI via a ListBox or a ComboBox, you can also specify that the TextBoxes should use their selected value as a reference point for the Binding like this: Text="{Binding ElementName=nameOfTextBoxInXAML, Path=SelectedItem.PropertyOnSelectedValue"

    Using this syntax ensures that your TextBoxes always work with the values of the selected item.