我正在尝试根据 ItemsControl 中项目的验证结果设置按钮的启用状态(如果存在任何验证错误,用户应该无法保存)。
我已经使用其他输入字段成功完成了此操作,如下所示:
<Style x:Key="SaveButtonStyle" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MinValueTextBox, Path=(Validation.HasError)}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
但是现在我还有一个 ItemsControl 绑定(bind)到一个集合,我希望以相同的方式验证它。 集合对象实现 IDataErrorInfo。
由于我无法在按钮样式的 DataTrigger 中指定 ElementName,我该如何执行此操作?
如果并非所有集合项的所有输入字段都有效,我想禁用保存按钮。
这是带有 DataTemplate 的 ItemsControl 代码:
<ItemsControl ItemsSource="{Binding FeedTypes}" ItemTemplate="{StaticResource FeedTypeDataTemplate}"/>
<DataTemplate x:Key="FeedTypeDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Label Content="{x:Static p:Resources.FeedTypeNameLabel}"/>
<TextBox Name="FeedTypeNameTxtBox" Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=Name,
ValidatesOnDataErrors=true, NotifyOnValidationError=true}" Grid.Column="1"/>
<Label Content="{x:Static p:Resources.KgPerLitreLabel}" Grid.Column="2"/>
<TextBox x:Name="FeedTypeKgPerLitreTxtBox" Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=KgPerLitre,
ValidatesOnDataErrors=true, NotifyOnValidationError=true}" Grid.Column="3"/>
</Grid>
</DataTemplate>
问候!
最佳答案
我会将验证插入 ViewModel
使用基于属性的验证并检查验证状态作为 CanExecute
的一部分Command
的方法实现您的按钮。
这提供了最大的灵活性,然后只需使用用于验证目的的属性标记 UI 可编辑属性,并使用简单的界面来检查所有 ViewModel
属性(包括任何基于嵌套集合的项目)有效。
作为奖励,验证也是可单元测试的。
对于集合,我通常使用 CustomValidation
属性与内部方法相同ViewModel
作为正在验证的集合属性。
如果您需要一个基本的工作示例,请查看此类和接口(interface):
using System;
using System.ComponentModel;
using System.Linq.Expressions;
namespace MwoCWDropDeckBuilder.Infrastructure.Interfaces
{
public interface IValidatingBaseViewModel : IDataErrorInfo
{
bool IsValid();
bool IsValid<T>(Expression<Func<T>> propertyExpression);
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using MwoCWDropDeckBuilder.Infrastructure.Interfaces;
namespace MwoCWDropDeckBuilder.Infrastructure
{
public class ValidatingBaseViewModel<TModelType> : ValidatingBaseViewModel, IBaseViewModel<TModelType>
where TModelType : class
{
public TModelType Model { get; private set; }
public ValidatingBaseViewModel(TModelType modelObject)
{
Model = modelObject;
}
}
public class ValidatingBaseViewModel : BaseViewModel, IValidatingBaseViewModel
{
private Dictionary<string, bool> _validationResults = new Dictionary<string, bool>();
public string Error
{
get { return null; }
}
public string this[string propertyName]
{
get { return OnValidate(propertyName); }
}
public bool IsValid()
{
var t = GetType();
var props = t.GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(ValidationAttribute)));
return props.All(x => IsValid(x.Name));
}
public bool IsValid<T>(Expression<Func<T>> propertyExpression)
{
var propertyName = GetPropertyName(propertyExpression);
return IsValid(propertyName);
}
private bool IsValid(string propertyName)
{
OnValidate(propertyName);
return _validationResults[propertyName];
}
private string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
string error = string.Empty;
var value = GetValue(propertyName);
var t = GetType();
var props = t.GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(ValidationAttribute)));
if (props.Any(x => x.Name == propertyName))
{
var results = new List<ValidationResult>(1);
var result = Validator.TryValidateProperty(
value,
new ValidationContext(this, null, null)
{
MemberName = propertyName
},
results);
StoreValidationResult(propertyName, result);
if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;
}
}
return error;
}
private void StoreValidationResult(string propertyName, bool result)
{
if (_validationResults.ContainsKey(propertyName) == false)
_validationResults.Add(propertyName, false);
_validationResults[propertyName] = result;
}
#region Privates
private string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
throw new InvalidOperationException();
}
return memberExpression.Member.Name;
}
private object GetValue(string propertyName)
{
object value;
var propertyDescriptor = TypeDescriptor.GetProperties(GetType()).Find(propertyName, false);
if (propertyDescriptor == null)
{
throw new ArgumentException("Invalid property name", propertyName);
}
value = propertyDescriptor.GetValue(this);
return value;
}
#endregion
}
}
关于c# - 基于 ItemsControl 中的验证项的 WPF 启用/禁用按钮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41680409/