当模型的属性发生变化时,如何触发ViewModel的CanExecute方法?

How to trigger CanExecute method of ViewModel when a property of the Model changes?

我的问题是如何从模型中触发 ViewModel 方法。

我正在使用 MVVM 开发 WPF 应用程序。所以我有一个按钮,SubmitMedPrescCommand,(使用中继命令实现)和一个组合框(SelectedMedPrescRepeat)绑定到一个模型。当用户选择下拉菜单时,在模型的 属性 中引发 PropertyChange 事件,但我需要调用 CanExecute(在 ViewModel 中)以启用按钮。

下面列出了我的代码示例。任何帮助,将不胜感激 !提前致谢!

视图模型是这样的:

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;
        }
    }
}

和 属性 所属的型号:

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));
            }
        }
    }
}

编辑以澄清: 使以下示例按预期工作的事情是 提高 PropertyChanged 模型上的事件 属性 并使用框架的 CommandBinding。所有剩余的代码绝对只是为了“有东西要展示”并将模型与视图模型逻辑分开。

请考虑我为您整理的这个演示(使用 MVVM Light Libs 所以我不必自己实现 INotifyPropertyChanged)。

CanExecute example on Github

启动程序后,您应该会在左侧看到一个包含两个条目的 ListView。单击一个到 select 个并在右侧“编辑”它。

注意以下行为:

  • ViewModel 将拒绝在性别框中输入除“男性”、“女性”、“多样化”和“未知”以外的任何其他值
  • ListView 中的“性别”列不会更新,因为 ViewModel 不会引发 PropertyChanged 事件
  • 最重要的是:在性别框中输入“未知”,按钮将立即禁用,反之亦然

我所做的只是实现了一个“愚蠢的”模型(没有业务逻辑)和一个“愚蠢的”ViewModel(将 int 值转换为字符串,反之亦然)。 然后我像这样实现了绑定到按钮的命令:

    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;
    }

ViewModel 中的 属性 什么都不做,只是更新模型:

    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;
        }
    }

虽然 只有模型 引发了 PropertyChanged 事件(这符合您的场景,即从属 属性“属于不同的 class” ).

框架负责重新检查依赖项所需的更新。它叫做“魔法”

有一些方法可以“强制”重新查询 like here 但如果您觉得有必要这样做,那么恕我直言,您的设计有问题。

收获框架的力量 - 即使这意味着您的项目启动速度较慢,因为您必须学习和重新认识事物。这是简单的方法 - 相信我。