Silverlight ValidationSummary未保留具有相同名称的ListBox元素的失败
本文关键字:ListBox 失败 元素 ValidationSummary 保留 Silverlight | 更新日期: 2023-09-27 17:58:48
概述:
我有一个Silverlight 4应用程序,当与ListBox一起使用时,我看到ValidationSummary控件中有问题的行为,希望有人能帮助我。
在高层,我有一个ListBox,其中每一行都用一个名为"txtInput"的文本框定义(通过ItemTemplate)。文本框绑定(TwoWay)到用DataAnnotation.Range属性标记的对象属性。ListBox绑定到这些对象的集合。在同一个父控件中,我还有一个ValidationSummary控件。
场景:
想象一个项目集合中有两个或多个对象的情况。用户将看到具有多行的ListBox,每行包含一个文本框。如果用户在第一个文本框中键入无效数据,则会按预期抛出ValidationException,ValidationSummary控件会按预期显示错误。文本框还获取验证错误样式(红色边框)。
然后,如果用户在第二行的文本框中输入无效数据(没有修复第一个文本框中的数据),第二个文本框也会抛出ValidationException并获得验证错误样式(红色边框),正如预期的那样,但是ValidationSummary控件只显示错误消息的一个实例。
然后,如果用户修复了其中一个(但不是两个)无效文本条目,则修复的文本框将删除验证样式(红色边框),ValidationSummary框将消失(这意味着它认为所有验证错误都已解决,并且HasErrors设置为false)。第二个(仍然无效)文本框仍然应用了验证错误样式(红色边框)。
我的预期是,仍然保留的无效文本框将导致ValidationSummary控件继续显示。我的假设是,ValidationSummary控件只是按属性名称跟踪失败,一旦成功尝试设置该名称的属性,它就会清除错误标记(即:它不考虑出现多个同名实例的情况)。
想要的结果:
最终,我要做的是防止用户在出现无效数据时单击屏幕上的"保存"按钮。我目前正在通过将按钮的IsEnabled属性绑定到ValidationSummary的HasErrors属性来实现这一点,但如果ValidationSummary显示上述行为,则此操作不起作用。
有人能告诉我一种方法,让ValidationSummary控件尊重同一(重复)文本框的多个故障,或者在存在这些故障时提供一种可行的替代方法来禁用"保存"按钮吗?(注意:在我实际的应用程序中,每一行都有多个输入控件,所以任何解决方案都需要考虑这一点)
XAML代码段:
<sdk:ValidationSummary x:Name="valSummary"/><ListBox ItemsSource="{Binding DomainObjectCollection,Mode=TwoWay,ValidatesDataErrors=True,ValidatesNotificationDataErrors=True,ValidatESOnException=True,NotifyOnValidationError=True}"><ListBox.ItemTemplate><数据模板><TextBox Name="txtInput"Text="{Binding DecimalValue,Mode=TwoWay,ValidatesDataErrors=True,ValidatesNotify DataErrors=True,Validates OnException=True,NotifyOnValidationError=True}"/><数据模板><ListBox.ItemTemplate><列表框><Button x:Name="btnSave"Content="Save"Command="{Binding SaveButtonCommand}"IsEnabled="{Binding HasErrors,ElementName=valSummary,Converter={StaticResource NotBoolConverter}"/>
域对象类:
[System.Runtime.Serialization.CollectionDataContractAttribute()]
public partial class DomainObjectCollection : System.Collections.ObjectModel.ObservableCollection<DomainObject>
{
}
[System.Runtime.Serialization.DataContractAttribute()]
public partial class DomainObject : System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.IDataErrorInfo, System.ComponentModel.INotifyDataErrorInfo
{
private int DomainObjectId_BackingField;
private decimal DecimalValue_BackingField;
private System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<object>> _errors;
[System.Runtime.Serialization.DataMemberAttribute()]
public virtual int DomainObjectId
{
get { return this.DomainObjectId_BackingField; }
set
{
if (!DomainObjectId_BackingField.Equals(value))
{
this.DomainObjectId_BackingField = value;
this.RaisePropertyChanged("DomainObjectId");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
[System.ComponentModel.DataAnnotations.RangeAttribute(typeof(decimal), "0", "100", ErrorMessage = "Value must be from 0 to 100.")]
public virtual decimal DecimalValue
{
get { return this.DecimalValue_BackingField; }
set
{
if (!DecimalValue_BackingField.Equals(value))
{
this.DecimalValue_BackingField = value;
this.RaisePropertyChanged("DecimalValue");
}
}
}
string System.ComponentModel.IDataErrorInfo.Error
{
get { return string.Empty; }
}
string System.ComponentModel.IDataErrorInfo.this[string propertyName]
{
get
{
var results = Validate(propertyName);
return results.Count == 0 ? null : string.Join(System.Environment.NewLine, results.Select(x => x.ErrorMessage));
}
}
private System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<object>> Errors
{
get
{
if (_errors == null)
_errors = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<object>>();
return _errors;
}
}
bool System.ComponentModel.INotifyDataErrorInfo.HasErrors
{
get { return Errors.Count > 0; }
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public event System.EventHandler<System.ComponentModel.DataErrorsChangedEventArgs> ErrorsChanged;
protected internal void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
private void Raise(string propertyName)
{
if (ErrorsChanged != null)
ErrorsChanged(this, new System.ComponentModel.DataErrorsChangedEventArgs(propertyName));
}
System.Collections.IEnumerable System.ComponentModel.INotifyDataErrorInfo.GetErrors(string propertyName)
{
System.Collections.Generic.List<object> propertyErrors;
if (Errors.TryGetValue(propertyName, out propertyErrors))
return propertyErrors;
return new System.Collections.Generic.List<object>();
}
public void AddError(string propertyName, object error)
{
System.Collections.Generic.List<object> propertyErrors;
if (!Errors.TryGetValue(propertyName, out propertyErrors))
{
propertyErrors = new System.Collections.Generic.List<object>();
Errors.Add(propertyName, propertyErrors);
}
if (propertyErrors.Contains(error))
return;
propertyErrors.Add(error);
Raise(propertyName);
}
public void RemoveError(string propertyName)
{
Errors.Remove(propertyName);
Raise(propertyName);
}
public virtual System.Collections.Generic.List<System.ComponentModel.DataAnnotations.ValidationResult> Validate(string propertyName)
{
var results = new System.Collections.Generic.List<System.ComponentModel.DataAnnotations.ValidationResult>();
var propertyInfo = GetType().GetProperty(propertyName);
if (propertyInfo == null)
return results;
RemoveError(propertyName);
var context = new System.ComponentModel.DataAnnotations.ValidationContext(this, null, null)
{
MemberName = propertyName
};
if (!System.ComponentModel.DataAnnotations.Validator.TryValidateProperty(propertyInfo.GetValue(this, null), context, results))
{
foreach (var validationResult in results)
AddError(propertyName, validationResult.ErrorMessage);
}
return results;
}
}
我不久前遇到了这个问题,并发现它之所以发生,是因为ValidationSummary控件使用控件的名称来查看其错误集合中是否已经存在错误。我正在研究一个解决方案,并想出了以下行为。由于我遇到了一些问题,我们最终走上了不同的道路,因为很多UI都是动态生成的。
你可以看看并尝试一下,看看它是否可以解决你的问题:
public class ValidationSummaryCountFixBehavior : Behavior<ValidationSummary>
{
private Dictionary<string, ValidationSummaryItem> _validationErrors = new Dictionary<string, ValidationSummaryItem>();
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
}
void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
var target = AssociatedObject.Target as FrameworkElement ?? VisualTreeHelper.GetParent(AssociatedObject) as FrameworkElement;
if (target != null)
{
target.BindingValidationError += new EventHandler<ValidationErrorEventArgs>(target_BindingValidationError);
}
AssociatedObject.Loaded -= new RoutedEventHandler(AssociatedObject_Loaded);
}
void target_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
FrameworkElement inputControl = e.OriginalSource as FrameworkElement;
if (((e != null) && (e.Error != null)) && ((e.Error.ErrorContent != null) && (inputControl != null)))
{
string message = e.Error.ErrorContent.ToString();
string goodkey = inputControl.GetHashCode().ToString(CultureInfo.InvariantCulture);
goodkey = goodkey + message;
if (e.Action == ValidationErrorEventAction.Added && ValidationSummary.GetShowErrorsInSummary(inputControl))
{
string messageHeader = null;
ValidationSummaryItem item = new ValidationSummaryItem(message, messageHeader, ValidationSummaryItemType.PropertyError, new ValidationSummaryItemSource(messageHeader, inputControl as Control), null);
_validationErrors[goodkey] = item;
}
else
{
_validationErrors.Remove(goodkey);
}
}
UpdateDisplayedErrors();
}
private void UpdateDisplayedErrors()
{
AssociatedObject.Errors.Clear();
foreach (ValidationSummaryItem item in _validationErrors.Values)
{
AssociatedObject.Errors.Add(item);
}
}
}