MVVM 获取 ContentPresenter 和 CancelEdits 的验证

MVVM get Validation of ContentPresenter and CancelEdits

我已经为你构建了一个简单的测试类:

以下类:

public abstract class Person : INotifyPropertyChanged

public class Adult : Person

public class Child : Person

我将此数据存储在 ObservableCollection<Person> 中,并希望在我的 Window 中显示它:

<ListView ItemsSource="{Binding People}" SelectedItem="{Binding SelectedPerson}" Grid.Column="0">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock>
                    <Run Text="{Binding FirstName}"/>
                    <Run Text="{Binding LastName}"/>
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

我选择的显示在ContentPresenter:

<ContentPresenter Content="{Binding SelectedPerson}">
    <ContentPresenter.Resources>
        <DataTemplate DataType="{x:Type local:Adult}">
            <StackPanel>
                <TextBlock Text="First Name:"/>
                <TextBox>
                    <TextBox.Text>
                        <Binding Path="FirstName">
                            <Binding.ValidationRules>
                                <local:NotEmptyRule/>
                            </Binding.ValidationRules>
                        </Binding>
                    </TextBox.Text>
                </TextBox>
                <TextBlock Text="Last Name:"/>
                <TextBox Text="{Binding LastName}"/> <!-- Validation same as FirstName --> 
                <TextBlock Text="Company:"/>
                <TextBox Text="{Binding Company}"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Child}">
            <StackPanel>
                <TextBlock Text="First Name:"/>
                <TextBox Text="{Binding FirstName}"/> <!-- Validation same as above--> 
                <TextBlock Text="Last Name:"/>
                <TextBox Text="{Binding LastName}"/> <!-- Validation same as above--> 
                <TextBlock Text="School:"/>
                <TextBox Text="{Binding School}"/>
            </StackPanel>
        </DataTemplate>
    </ContentPresenter.Resources>
</ContentPresenter>

现在谁能告诉我如何取消编辑 (CancelCommand) 或正确保存编辑 (SaveCommand) MVVM-way。

现在我的程序会在 TextBox 失去焦点时保存它们并且它们无法撤消。

有人可以post给我举个例子吗?

此外,我不知道我的输入无效: 我试过:

private void SaveCommand_Execute()
{
    //this is the current window
    MessageBox.Show(string.Format("Entry is {0}valid", IsValid(this) ? "" : "not "), "Validation", MessageBoxButton.OK, MessageBoxImage.Information);
}


private bool IsValid(DependencyObject obj)
{
        return !Validation.GetHasError(obj) && LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>().All(IsValid);
}

但即使我的 TextBox 显示错误,我的函数也会告诉我输入有效。

谢谢你帮助我!

如果你想要验证,你需要在你的实体上实现 INotifyDataErrorInfo。如果要还原更改,则需要实施 IRevertibleChangeTracking。两者都不是简单的任务,如果你以前从未做过。

还有另一种解决Accept/Cancel问题的方法。当您开始编辑 Person 时,您将所有数据复制到 PersonViewModel。然后,您将数据绑定到 PersonViewModel,当用户单击保存时,将数据复制回 Person,当用户单击取消时,忽略更改。

PersonViewModel class 不是强制性的,您可以只创建新实例 Person,但是 PersonViewModel 在 UI 逻辑中为您提供了更大的灵活性。例如,您可以在表示层有密码和重复密码字段,但在业务实体中您只想有密码。

我撤消更改的解决方案:

public abstract class Person : INotifyPropertyChanged, ICloneable, IEditableObject

实现的接口成员:

Person _Backup = null;

    public object Clone()
    {
        return MemberwiseClone();
    }

    public void BeginEdit()
    {
        _Backup = Clone() as Person;
        HasChanges = false;
    }


    public void CancelEdit()
    {
        foreach (var Prop in GetType().GetProperties())
        {
            Prop.SetValue(this, Prop.GetValue(_Backup));
        }
        HasChanges = false;
    }

    public void EndEdit()
    {
        _Backup = null;
        HasChanges = false;
    }

在视图模型中:

    private const string SelectedPersonPropertyName = "SelectedPerson";
    private Person _SelectedPerson;
    public Person SelectedPerson
    {
        get
        {
            return _SelectedPerson;
        }
        set
        {
            if (_SelectedPerson != null)
            {
                _SelectedPerson.EndEdit();
            }
            if (value != null)
            {
                value.BeginEdit();
            }
            _SelectedPerson = value;
            RaisePropertyChanged(SelectedPersonPropertyName);
        }
    }

实现我的验证规则:

    private const string FirstNamePropertyName = "FirstName";
    private string _FirstName;
    public string FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            if (_FirstName == value)
                return;
            ValidationResult _Result = _NotEmptyRule.Validate(value, System.Globalization.CultureInfo.CurrentCulture);
            if (!_Result.IsValid)
            {
                AddError(FirstNamePropertyName, ValueIsNullOrBlank);
            }
            else
            {
                RemoveError(FirstNamePropertyName, ValueIsNullOrBlank);
            }
            _FirstName = value;
            HasChanges = true;
            RaisePropertyChanged(FirstNamePropertyName);
        }
    }

    private const string ValueIsNullOrBlank = "ValueIsNullOrBlank";

    private NotEmptyRule _NotEmptyRule = new NotEmptyRule();
    private Dictionary<string, List<object>> _Errors = new Dictionary<string, List<object>>();
    protected void AddError(string PropertyName, object Error)
    {
        if (!_Errors.ContainsKey(PropertyName))
        {
            _Errors[PropertyName] = new List<object>();
        }
        if (!_Errors[PropertyName].Contains(Error))
        {
            _Errors[PropertyName].Add(Error);
            RaiseErrorsChanged(PropertyName);
        }
    }

    protected void RemoveError(string PropertyName, object Error)
    {
        if (_Errors.ContainsKey(PropertyName) && _Errors[PropertyName].Contains(Error))
        {
            _Errors[PropertyName].Remove(Error);
            if (_Errors[PropertyName].Count == 0)
            {
                _Errors.Remove(PropertyName);
            }
            RaiseErrorsChanged(PropertyName);
        }
    }

    public void RaiseErrorsChanged(string PropertyName)
    {
        if (ErrorsChanged != null)
            ErrorsChanged(this, new DataErrorsChangedEventArgs(PropertyName));
    }

    public IEnumerable GetErrors(string PropertyName)
    {
        if (String.IsNullOrEmpty(PropertyName) ||
            !_Errors.ContainsKey(PropertyName)) return null;
        return _Errors[PropertyName];
    }

    public bool HasErrors
    {
        get { return _Errors.Count > 0; }
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    private const string HasChangesPropertyName = "HasChanges";
    private bool _HasChanges;
    public bool HasChanges
    {
        get
        {
            return _HasChanges;
        }
        set
        {
            _HasChanges = value;
            RaisePropertyChanged(HasChangesPropertyName);
        }
    }

我这里唯一的问题是:我没有克隆回我的私有字段(只是我的属性)有人知道这方面的帮助吗?