MVVM 获取 ContentPresenter 和 CancelEdits 的验证
MVVM get Validation of ContentPresenter and CancelEdits
我已经为你构建了一个简单的测试类:
以下类:
public abstract class Person : INotifyPropertyChanged
public class Adult : Person
public class Child : Person
- 人:名字 + 姓氏
- 成人:公司
- Child:学校
我将此数据存储在 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);
}
}
我这里唯一的问题是:我没有克隆回我的私有字段(只是我的属性)有人知道这方面的帮助吗?
我已经为你构建了一个简单的测试类:
以下类:
public abstract class Person : INotifyPropertyChanged
public class Adult : Person
public class Child : Person
- 人:名字 + 姓氏
- 成人:公司
- Child:学校
我将此数据存储在 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);
}
}
我这里唯一的问题是:我没有克隆回我的私有字段(只是我的属性)有人知道这方面的帮助吗?