My question is how to trigger a ViewModel method from the model.
I am developing a WPF application using MVVM. So I have a button, SubmitMedPrescCommand
, (implemented using Relay Command) and a Combobox (SelectedMedPrescRepeat
) that is binded to a model. When the user selects the dropdown the PropertyChange event is raised in the property of the model but I need to call the CanExecute (in theViewModel) in order to enable the button.
A sample of my code is listed below. Any help would be appreciated ! Thanks in advance !
The viewmodel is this:
public class EpCreateMedicineViewModel : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public ICommand SubmitMedPrescCommand { get; set; }
public EpCreateMedicineViewModel()
{
SubmitMedPrescCommand = new RelayCommand<MedicinePrescriptionForSubmission>(ExecuteSubmitMedPrescCommand, CanExecuteSubmitMedPrescCommand);
}
private MedicinePrescriptionForSubmission _medicinePrescForSubm;
public MedicinePrescriptionForSubmission MedicinePrescForSubm
{
get { return _medicinePrescForSubm; }
set
{
if (value != this._medicinePrescForSubm)
{
this._medicinePrescForSubm = value;
OnPropertyRaised("MedicinePrescForSubm");
}
}
}
public bool CanExecuteSubmitMedPrescCommand(object parameter)
{
if (_medicinePrescForSubm.MedicineForSubmGeneralInfo.SelectedMedPrescRepeat!=null)
{
return true;
}
else
{
return false;
}
}
}
and the Model where the property belongs:
public class MedicinePrescriptionForSubmission
{
public MedicineForSubmGeneralInfo MedicineForSubmGeneralInfo { get; set; }
public class MedicineForSubmGeneralInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private MedicinePrescriptionRepeat _selectedMedPrescRepeat; // THE PROPERTY THAT THE COMBOBOX IS BINDED TO
public MedicinePrescriptionRepeat SelectedMedPrescRepeat
{
get { return _selectedMedPrescRepeat; }
set
{
_selectedMedPrescRepeat = value;
OnPropertyRaised("SelectedMedPrescRepeat");
//CanExecuteSubmitMedPrescCommand(_selectedMedPrescRepeat); // THE METHOD OF THE VIEWMODEL THAT I WANT TO BE TRIGERRED WHEN MedicinePrescriptionRepeat changes
}
}
private void OnPropertyRaised(string propertyname)
{
PropertyChangedEventHandler handle = PropertyChanged;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
}
}
EDIT to clarify: the only thing that makes the following example work as intended is raising the PropertyChanged
event on the model property and using the Framework's CommandBinding. All of the remaining code is absoluteley just to "have something to show" and separate Model from ViewModel logic.
Please consider this demonstration I put together for you (using MVVM Light Libs so I don't have to implement INotifyPropertyChanged myself).
Upon starting the Program you should see a ListView on the left with two entries. Click one to select one and "Edit" it on the right side.
Note the following behavior:
All i did was implement a "stupid" Model (no business logic) and a "silly" ViewModel (that translates int values to strings and vice-versa). Then I implemented the Command that is bound to the Button like this:
public ICommand DisplayGenderValueCommand { get { return new RelayCommand(this.DisplayGenderValueExecute, this.DisplayGenderValueCanExecute); } }
private void DisplayGenderValueExecute()
{
MessageBox.Show($"Gender {this.Gender} has a model value of {this._MyModel.Gender}");
}
private bool DisplayGenderValueCanExecute()
{
return this._MyModel.Gender > 0;
}
The property in the ViewModel does nothing, but update the model:
public string Gender
{
get
{
if (_MyModel.Gender == 0) { return "unknown"; }
if (_MyModel.Gender == 1) { return "female"; }
if (_MyModel.Gender == 2) { return "male"; }
if (_MyModel.Gender == 3) { return "diverse"; }
return "error";
}
set
{
if (value == "unknown") _MyModel.Gender = 0;
if (value == "female") _MyModel.Gender = 1;
if (value == "male") _MyModel.Gender = 2;
if (value == "diverse") _MyModel.Gender = 3;
}
}
While only the model raises the PropertyChanged Event (this is in line with your scenario that the dependent property "belongs to a different class").
The Framework takes care of the updates needed to re-check the dependencies. It's called "Magic"
There are ways to "force" a requery like here but if you feel the need to do that, then IMHO there is something wrong with your design.
Harvest the power of the Framework - even if it means a slower startup to your project because you have to learn and reas things. This is the easy way - believe me.