Search code examples
c#wpfprismdelegatecommand

How can I make Prism's DelegateCommand observe a child view-model's property?


My "parent" view-model (AnalyzeVm) has a child view-model (ScanVm) that represents something that can be saved to disk. I want to give the parent a command (SaveCmd) that is enabled only when the ScanVm.IsDirty property is true.

I already use DelegateCommand and its ObservesProperty functions all over the place so I tried to use it for this. But in this one case I cannot get the ObservesProperty expression to trigger a call to CanExecute.

The difference here (from all the other places I use ObservesProperty that work fine) is that I'm monitoring a property of the child object. Not of my own

I need to understand why this is not working. I thought this was a valid thing to do.

Here are the two view models (stripped down for clarity)

// Parent view-model.  Derives from a class that implements INotifyPropertyChanged.

public class AnalyzeVm : BaseViewModel
{
    private ScanVm _scan;

    public AnalyzeVm(ScanVm scan)
    {
        _scan = scan;

        // Set up the command.  Try to make it monitor a property of the ScanVm
        // object instead of one of our own.

        SaveScanCmd = new DelegateCommand(
            () => { _scan.Save() }                 // execute -- saves the scan
            () => { return _scan.IsDirty; })       // only valid when scan is dirty
            .ObservesProperty(() => scan.IsDirty); // So observe IsDirty property

        // Now just as a sanity check, add a handler for that property changing
        // to the PropertyChangedEventManager and log when it does.

        PropertyChangedEventManager.AddHandler(
            scan, 
            (_, _) => Debug.WriteLine("IsDirty changed"), 
            nameof(ScanContext.IsDirty));
    }

    public ICommand SaveScanCmd { get; }

    // other code...
}

// ScanVm class.  

public class ScanVm : BaseViewModel
{
    private bool _isDirty;
    public bool IsDirty
    { 
        get => _isDirty;
        set => SetProperty(ref _isDirty, value);  // Raises PropertyChanged event
    }
    public void Save()
    {
        // Code here to save the ScanVm
       
        IsDirty = false;  // No longer dirty
    }
    // ... other code
}

I am sure that the IsDirty property is properly changing and firing the event. I confirmed it many times. And I'm already using Observes property successfully when I observe my own class' properties. For example, I were to, take the SaveCmd and move it into the ScanVm class itself -- so that the property expression just observed the ScanVm's own property, then it works fine. I do that all over the place in my code.

This is the only place I'm trying to observe another object's property. Is it valid to do? if so, what am I doing wrong?


Solution

  • ObservesProperty primarily works on properties of the containing object

    public bool IsDirty {...}
    
    ...ObservesProperty( () => IsDirty );
    

    plus nested properties

    public ScanVM Child {...}
    
    ...ObservesProperty( () => Child.IsDirty );
    

    It just doesn't observe properties on any given instance, because it starts looking from the containing object (AnalyzeVm in this case).