0

I'm working on an MVVM application that has a Save button that I'd like to disable if the Title field is empty.

Here's the code for the delegatecommand:

        _clickSaveChangesCommand = new DelegateCommand<string>(
            (s) => { saveStudentRecord(); //execute },
            (s) => { return (_student.Title != null);  /*Can execute*/ }
            );

Here's the binding:

 <TextBox Name="fldTitle" Text="{Binding Path=Student.Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="300" Height="27" />

When I update the student object from within the viewmodel, it works as expected. If, however, I create a new record and type something into the textbox, the button remains not executable. In my testing, if I try to show the value of _student.Title it shows with the value as expected.

steam23
  • 69
  • 7

1 Answers1

1

You need to do something to raise the command's CanExecuteChanged event when _student.Title changes.

Are you using Prism's DelegateCommand? If so, Andy found this answer. If supported, it may be preferable to the suggestion below. But see this question, which is like your case in that the property is a "grandchild" property, not a direct property of the class that owns the command.

If you are using Prism and you can do that, do try replacing Student to see what happens. In 2016, that would have broken the command enable updating. It may still.

So if that doesn't work, this ought to.

Your DelegateCommand<T> class may have a method that does that; it's often called RaiseCanExecuteChanged() or something like that.

Likely, the best way to do this is in the setter for Student:

public Student Student
{
    get { return _student; }
    set
    {
        if (value != _student)
        {
            if (_student != null)
            {
                //  You do want to unhook this, otherwise there's a live reference 
                //  to the old _student and it won't be free to be garbage collected. 
                _student.PropertyChanged -= _student_PropertyChanged;
            }

            _student = value;

            if (_student != null)
            {
                _student.PropertyChanged += _student_PropertyChanged;
            }

            OnPropertyChanged();
        }
    }
}

private void _student_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Title")
    {
        ClickSaveChangesCommand.RaiseCanExecuteChanged();
    }
}
  • @Andy I missed where he said he's using Prism. I've seen classes named DelegateCommand which have nothing to do with Prism, they just implement ICommand with delegates. I updated the answer. – 15ee8f99-57ff-4f92-890c-b56153 May 24 '19 at 16:21
  • @Andy [This is three years old](https://stackoverflow.com/questions/33624193/prism-6-delegatecommand-observesproperty-code); does it still work that way? – 15ee8f99-57ff-4f92-890c-b56153 May 24 '19 at 16:38
  • Oh... Yes it seems they changed it. You can use raisepropertychanged explicitly or observesproperty raises it. – Andy May 24 '19 at 16:45
  • @Andy What happens when `Student` is replaced with a new instance? Will the command watch the new `Student.Title`, or keep watching the old `Student.Title`? *If* ObserveProperty works for OP in his case, it'll be preferable. I'm just concerned it won't. – 15ee8f99-57ff-4f92-890c-b56153 May 24 '19 at 16:46
  • Not using Prism for this one. I will experiment with RaiseCanExecuteChanged and report back. I see that my delegateCommand implementation does have a CanExecuteChanged event, so hoping I will be able to leverage that. – steam23 May 24 '19 at 17:22
  • @steam23 CanExecuteChanged is part of ICommand — that’s how Button knows to handle it — so you can rely on it being there. If your DelegateCommand class is under your control, you could just add your own method to raise the event. – 15ee8f99-57ff-4f92-890c-b56153 May 24 '19 at 17:24
  • 1
    OK. I think i have it sorted now. Thanks for pointing me at CanExecuteChanged. It turned out I'd created problems by creating new instances of the viewmodel in code rather than reusing the existing one. It took a bit of extra googling but eventually I figured out to add ` public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } }` to my delegate implementation and now it's working. – steam23 May 28 '19 at 18:57