2

我在 Prism / WPF 项目中有一个这样的 ViewModel 类。

public class ContentViewModel : ViewModelBase, IContentViewModel
{
    public ContentViewModel(IPersonService personService)
    {
        Person = personService.GetPerson();
        SaveCommand = new DelegateCommand(Save, CanSave);
    }

    public Person Person { get; set; }

    public DelegateCommand SaveCommand { get; set; }

    private void Save()
    {
        // Save actions here...
    }

    private bool CanSave()
    {
        return Person.Error == null;
    }
}

上述 ViewModel 中使用的 person 类型定义如下:

public class Person : INotifyPropertyChanged, IDataErrorInfo
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            OnPropertyChanged("FirstName");
        }
    }

    // other properties are implemented in the same way as above...

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    private string _error;
    public string Error
    {
        get
        {
            return _error;
        }
    }

    public string this[string columnName]
    {
        get
        {
            _error = null;
            switch (columnName)
            {
                // logic here to validate columns...
            }
            return _error;
        }
    }
}

ContentViewModel 的一个实例被设置为 View 的 DataContext。在视图中,我使用绑定到 Person 如下:

<TextBox Text="{Binding Person.FirstName, ValidatesOnDataErrors=True}" />
<Button Content="Save" Command="{Binding SaveCommand}" />

当我对绑定到 FirstName 等 Person 属性的 TextBox 进行更改并单击 Save 时,我可以看到 ViewModel 命令处理程序中的更改。但是,如果这些属性中的任何一个在验证中失败,则永远不会执行 CanSave 并且永远不会禁用按钮。

如何在上述场景中禁用基于 DelegateCommand 的 CanExecute 操作处理程序的按钮?

4

3 回答 3

4

在 ContentViewModel 的构造函数中添加这一行

public ContentViewModel(IPersonService personService)
{
    //GetPerson
    Person.PropertyChanged +=person_PropertyChanged;
}

并编写一个方法来处理您调用 CommandManager.InvalidateRequerySuggested() 或 SaveCommand.RaiseCanExecuteChanged() 的事件

private void person_PropertyChanged(object sender, EventArgs args)
{
   CommandManager.InvalidateRequerySuggested();
   //SaveCommand.RaiseCanExecuteChanged()
}

希望这有效。:-)

于 2012-03-14T09:36:36.993 回答
2

尝试使用所有可以更改错误的属性:

 public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        OnPropertyChanged("FirstName");

        OnPropertyChanged("Error");
    }
}

或者

        switch (columnName)
        {
            // logic here to validate columns...

            OnPropertyChanged("Error");
        }

您遇到的问题是错误更改时未调用 OnPropertyChanged。

下一步是在创建人员的 propertychanged 事件时订阅它,并创建一个处理程序来检查 propertychanged,然后更改命令使用的布尔变量。

 public ContentViewModel(IPersonService personService)
{
    Person = personService.GetPerson();
    Person.PropertyChanged+= PersonPropertyChangedHandler;
    SaveCommand = new DelegateCommand(Save, personHasError);
}

bool personHasError = false;
void PersonPropertyChangedHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Error")
    {
        if(Person.Error == null)
           personHasError = true;
        else
           personHasError = false;
    } 
}

希望这有效。我是手工制作的,没有检查它,所以让我知道它是否有错误或其他错误并纠正它

于 2012-03-15T05:44:17.653 回答
1

简而言之 -yourDelegateCommand.RaiseCanExecuteChanged()当您认为您的CanExecute()返回值可以更改时,您应该调用。

INotifyPropertyChanged在您的示例中,您应该通过接口通知您的Person.Error属性已更改,订阅类中的Person.PropertyChanged事件并在每次更改时ContentViewModel调用。请注意 -例如,当您的方案发生更改时,不会自动重新计算 - 您应该手动执行此操作。SaveCommand.RaiseCanExecuteChanged()Person.ErrorPerson.ErrorPerson.FirstName

更新:

public class ContentViewModel : ViewModelBase, IContentViewModel
{
    public ContentViewModel(IPersonService personService)
    {
        Person = personService.GetPerson();
        Person.PropertyChanged += Person_PropertyChanged;
        SaveCommand = new DelegateCommand(Save, CanSave);
    }

    private void PersonPropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        SaveCommand.RaiseCanExecuteChanged();
    }

    private void Save()
    {
        // Save actions here...
    }

    private bool CanSave()
    {
        return IsErrorPresented(Person);
    }

    private bool IsErrorPresented(object o)
    {
        if (!(o is IDataErrorInfo))
            return false;

        var propNames = o.GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Select(p => p.Name);

        var o2 = (o as IDataErrorInfo);

        var errors = propNames.Select(p => o2[p])
            .Where(p => !String.IsNullOrEmpty(p))
            .ToList();

        ValidationSummary.ErrorMessages = errors;

        return errors.Count > 0;
    }
}

<TextBox Text="{Binding Person.FirstName, 
                        UpdateSourceTrigger=PropertyChanged, 
                        ValidatesOnDataErrors=True, 
                        ValidatesOnExceptions=True, 
                        NotifyOnValidationError=True}" />
<Button Content="Save" Command="{Binding SaveCommand}" />

如果您还指定PropertyChangedUpdateSourceTrigger,则您的保存按钮将在您输入时更新。

于 2012-03-11T10:09:25.353 回答