c# - 基于 ItemsControl 中的验证项的 WPF 启用/禁用按钮

标签 c# wpf validation

我正在尝试根据 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

    }
}

完整项目位于:WPF Project using attribute validation

关于c# - 基于 ItemsControl 中的验证项的 WPF 启用/禁用按钮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41680409/

相关文章:

jquery - 使用 Jquery 验证插件 Ajax 远程验证 WordPress 用户名和电子邮件

c# - Xamarin.forms 在 windows8 模拟器中给出 "invalid cross thread access"异常

c# - 为什么窗口的卸载事件不会在 WPF 中触发?

c++ - 如何检查日期格式的有效性(例如,%d/%m/%Y-%H :%M:%S)?

java - 在 Hibernate Validator 4.1+ 中,@NotNull、@NotEmpty 和 @NotBlank 有什么区别?

c# - ExcelWorksheet 维度 NullReferenceException

c# - 如何通过c#访问localhost连接MySQL数据库?

c# - 非静态字段需要对象引用

c# - 如何显示标准 SharePoint "Access Denied"消息

wpf - 依赖属性回调不起作用