MVVM获得ContentPresenter和CancelEdits的验证

时间:2015-07-09 06:53:23

标签: c# wpf validation mvvm cancel-button

我为你构建了一个简单的TestClass:

以下课程:

failure
  • 人:FirstName + LastName
  • 成人:公司
  • 孩子:学校

我将这些数据存储在public abstract class Person : INotifyPropertyChanged public class Adult : Person public class Child : Person 中,并希望在我的窗口中显示:

ObservableCollection<Person>

我在<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

现在任何人都可以告诉我如何取消我的编辑(CancelCommand)或以正确的MVVM方式保存它们(SaveCommand)。

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

有人可以给我发一个例子吗?

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

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

但即使我的TextBox显示错误,我的函数也告诉我该条目有效。

感谢您帮助我!

2 个答案:

答案 0 :(得分:1)

如果您需要验证,则需要在实体上实施INotifyDataErrorInfo。如果要还原更改,则需要实现IRevertibleChangeTracking。如果你以前从未做过这两件事,那么这两件事都不容易。

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

PersonViewModel类不是必需的,您可以创建新的实例Person,但PersonViewModel为您提供了更多的UI逻辑灵活性。例如,您可以在表示层使用密码和重复密码字段,但在业务实体中只需要密码。

答案 1 :(得分:0)

我撤消更改的解决方案:

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

在ViewModel中:

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

我遇到的唯一问题是:我没有克隆我的私人信息(只是我的属性)有没有人知道这里的帮助?