0

I am still not sure if my approach is correct, but in an attempt to implement the MVVM pattern, I have created a model class 'Test' in the following way:

public class Test : BindableBase
{
    private int testNumber;
    public int TestNumber
    {
        get { return testNumber; }
        set { SetProperty(ref testNumber, value) }
    }
    ...
}

Then I created an instance of this class in my ViewModel:

class ViewModel : BindableBase
{
    private Test testVM;
    public Test TestVM
    {
        get { return testVM; }
        set { SetProperty(ref testVM, value); }
    }
    ...

And in the XAML code of the View I bind all the properties of the Test class through the TestVM property. Although this works fine, I ran into a problem when trying to implement a DelegateCommad.

    public DelegateCommand StartTestCommand { get; private set; }

So far, when implementing DelegateCommands, if I want to trigger the CanExecute method when a property has changed, I include DelegateCommand.RaiseCanExecuteChanged() inside the property's setter. Like so:

    ...
    private bool duringTest;
    public bool DuringTest
    {
        get { return duringTest; }
        set
        {
            SetProperty(ref duringTest, value);
            StartTestCommand.RaiseCanExecuteChanged();
        }
    }
    ...

This works fine for properties declared in the ViewModel, but when using the same approach for the Test properties, this no longer works.

    ...
    private Test testVM;
    public Test TestVM
    {
        get { return testVM; }
        set
        {
            SetProperty(ref testVM, value);
            StartTestCommand.RaiseCanExecuteChanged();
        }
    }
}

I would expect that every time a property from TestVM was changed, the setter would be called, but instead the model is updated directly.

What am I doing wrong? What is the correct approach when using a Model object in the ViewModel?

r_laezza
  • 119
  • 1
  • 2
  • 14

1 Answers1

1

Changing a property value of an object doesn't change the object's reference.

Declaring this

public Test TestVM
{
    get { return testVM; }
    set
    {
        SetProperty(ref testVM, value);
        StartTestCommand.RaiseCanExecuteChanged();
    }
}

you are basically telling the compiler: when the reference to the TestVM object is changed (even to the same value), update the StartTestCommand's state.

But obviously you don't change the reference to that object once you assigned it.

If you want to update the commands in your parent view-model (ViewModel) when some child view-model's (Test) properties change, you can use the PropertyChanged event:

public Test TestVM
{
    get { return testVM; }
    set
    {
        Test oldValue = testVM;
        if (SetProperty(ref testVM, value))
        {
            if (oldValue != null)
            {
                oldValue.PropertyChanged -= TestPropertyChanged;
            }

            if (testVM!= null)
            {
                testVM.PropertyChanged += TestPropertyChanged;
            }
        }
    }
}

void TestPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    // filter if necessary
    if (e.PropertyName == "...")
    {
        StartTestCommand.RaiseCanExecuteChanged();
    }
}
dymanoid
  • 14,771
  • 4
  • 36
  • 64
  • "When the reference to the TestVM object is changed (even to the same value), update the StartTestCommand's state". So this means that the setter would be called if I wrote something like: TestVM = new Test(); – r_laezza Apr 26 '18 at 09:03
  • 1
    You should also leave `StartTestCommand.RaiseCanExecuteChanged()` in the `TestVM` setter, otherwise initial and subsequent setting of `TestVM` won't invalidate the command. – Grx70 Apr 26 '18 at 09:55
  • @Grx70, that's up to the requirement. Maybe, there's no need to do that. But it's worth noting, thanks. – dymanoid Apr 26 '18 at 11:12