Wpf 当任何属性发生更改时,如何验证多个属性?

Wpf 当任何属性发生更改时,如何验证多个属性?,wpf,validation,idataerrorinfo,Wpf,Validation,Idataerrorinfo,我有两个日期字段:StartDate和EndDate。StartDate必须早于EndDate 如果用户将StartDate更改为大于EndDate的值,则该日期选择器周围会出现一个红色边框,反之亦然。如果用户更改第二个框,使日期范围现在正确,则第一个框仍然存在验证错误 当两个日期字段中的任何一个发生更改时,如何验证它们 我正在使用IDataErrorInfo public string GetValidationError(string propertyName) { switch

我有两个日期字段:StartDate和EndDate。StartDate必须早于EndDate

如果用户将StartDate更改为大于EndDate的值,则该日期选择器周围会出现一个红色边框,反之亦然。如果用户更改第二个框,使日期范围现在正确,则第一个框仍然存在验证错误

当两个日期字段中的任何一个发生更改时,如何验证它们

我正在使用
IDataErrorInfo

public string GetValidationError(string propertyName)
{
    switch (propertyName)
    {
        case "StartDate":
            if (StartDate > EndDate)
                s = "Start Date cannot be later than End Date";
            break;

        case "EndDate":
            if (StartDate > EndDate)
                s = "End Date cannot be earlier than Start Date";
            break;
    }

    return s;
}
我不能简单地引发PropertyChange事件,因为当其中一个字段发生更改时,我需要验证这两个字段,因此让这两个字段为另一个字段引发PropertyChange事件将陷入无限循环


我也不喜欢在另一个日期返回验证错误时清除日期字段的想法。

我通常会将所有验证错误添加到字典中,并让验证模板通过属性名订阅。在每个属性更改事件处理程序中,我可以检查任意数量的属性,并根据需要添加或删除它们的验证状态


检查我的实现的外观。很抱歉,它在VB.NET中,但应该相当简单。

最简单的方法是在setter中为两个需要验证的属性发出
PropertyChanged
通知,如

然而,如果这对您不起作用,我找到了一种方法来一起验证一组属性,尽管除了
INotifyPropertyChanged
(我使用EntityFramework,默认情况下,它们的类实现两个接口)之外,您的类还必须实现
INotifyPropertyChanged

扩展方法

public static class ValidationGroup
{
    public delegate string ValidationDelegate(string propertyName);
    public delegate void PropertyChangedDelegate(string propertyName);

    public static void AddValidationGroup<T>(this T obj, 
        List<string> validationGroup, bool validationFlag,
        ValidationDelegate validationDelegate, 
        PropertyChangedDelegate propertyChangedDelegate)
        where T : INotifyPropertyChanged, INotifyPropertyChanging
    {

        // This delegate runs before a PropertyChanged event. If the property
        // being changed exists within the Validation Group, check for validation
        // errors on the other fields in the group. If there is an error with one
        // of them, set a flag to true.
        obj.PropertyChanging += delegate(object sender, PropertyChangingEventArgs e)
        {
            if (validationGroup.Contains(e.PropertyName))
            {
                foreach(var property in validationGroup)
                {
                    if (validationDelegate(property) != null)
                    {
                        validationFlag = true;
                        break;
                    }
                }
            }
        };

        // After the Property gets changed, if another field in this group was
        // invalid prior to the change, then raise the PropertyChanged event for 
        // all other fields in the Validation Group to update them.
        // Also turn flag off so it doesn't get stuck in an infinite loop
        obj.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
        {
            if (validationGroup.Contains(e.PropertyName))
            {
                if (validationFlag && validationDelegate(e.PropertyName) == null)
                {
                    validationFlag = false;
                    foreach(var property in validationGroup)
                    {
                        propertyChangedDelegate(property);
                    }
                }
            }
        };
    }
}
公共静态类验证组
{
公共委托字符串验证委托(字符串属性名称);
公共委托void PropertyChangedDelegate(字符串propertyName);
公共静态void AddValidationGroup(此T对象,
列出validationGroup、bool validationFlag、,
ValidationDelegate ValidationDelegate,
PropertyChangedLegate属性yChangedLegate)
其中T:INotifyPropertyChanged,INotifyPropertyChanging
{
//此委托在PropertyChanged事件之前运行。如果
//验证组中存在正在更改的,请检查验证
//组中其他字段出错。如果其中一个字段出错
//其中,将标志设置为true。
obj.PropertyChanging+=委托(对象发送方,PropertyChangingEventArgs e)
{
if(validationGroup.Contains(e.PropertyName))
{
foreach(validationGroup中的var属性)
{
如果(validationDelegate(属性)!=null)
{
validationFlag=true;
打破
}
}
}
};
//属性更改后,如果此组中的另一个字段
//更改之前无效,然后引发的PropertyChanged事件
//要更新验证组中的所有其他字段。
//同时关闭标志,这样它就不会陷入无限循环
obj.PropertyChanged+=委托(对象发送方,PropertyChangedEventArgs e)
{
if(validationGroup.Contains(e.PropertyName))
{
if(validationFlag&&validationDelegate(e.PropertyName)==null)
{
validationFlag=false;
foreach(validationGroup中的var属性)
{
财产变更受遗赠人(财产);
}
}
}
};
}
}
要使用它,请将以下调用添加到任何类的构造函数中,这些类应该一起验证一组属性

this.AddValidationGroup(
    new List<string> { "StartDate", "EndDate" },
    GetValidationError, OnPropertyChanged);
this.AddValidationGroup(
新列表{“开始日期”、“结束日期”},
GetValidationError,OnPropertyChanged);

我在一个验证组中测试了多达3个属性,它似乎工作正常。

使用此技巧,它可以防止它们调用Property互相更改:

private bool RPCfromStartDate = false;
private bool RPCfromEndDate = false;

public string this[string columnName]
{
    get 
    {
                string result = null;
                switch (columnName)
                {
                    case "StartDate":
                        if (StartDate.Date >= EndDate.Date)
                        {
                            result = "Start Date cannot be later than End Date";
                        }
                        if (!RPCfromEndDate)
                        {
                            RPCfromStartDate = true;                            
                            OnPropertyChanged("EndDate");
                            RPCfromStartDate = false;
                        } 
                    case "EndDate":
                        if (StartDate.Date >= EndDate.Date)
                        {
                            result = "End Date cannot be earlier than Start Date";
                        }
                        if (!RPCfromStartDate)
                        {
                            RPCfromEndDate = true;                            
                            OnPropertyChanged("StartDate");
                            RPCfromEndDate = false;
                        } 
                        break;
                }
...

您还可以订阅SelectedDateChanged事件处理程序并更新所需的绑定

BindingExpression expression = datePickerStartDate.GetBindingExpression(DatePicker.SelectedDateProperty);
if (expression != null)
{
    expression.UpdateSource();
}

只是想提出另一种方法,当任何一条规则被打破时,你能将两个框都变成红色吗?如果是这样,您只需引发一个propertychanged事件。@Josh很乐意,但我不知道如何手动引发单个属性的验证事件。我退出使用验证事件。我和他们有太多的问题,我认为他们应该开火,但他们没有,或者他们确实开火,但边界没有突出显示。我使用绑定到valid的布尔表示的红色边框样式。我还将工具提示绑定到消息属性。然后,当一个值发生更改时,我会对其进行验证,并适当地设置属性。@Josh这不是意味着您需要对要验证的类中的每个属性使用
PropertyIsValid
PropertyErrorMessage
?或者验证发生在整个类而不是单个属性上?在GetValidationError方法中提升PropertyChanged将卡在无限循环中。。但是在开始日期和结束日期的设定者上提高财产的变化可能会解决你的问题…我和你有完全相同的问题。您使用了INotifyPropertyChanging解决方案还是找到了其他方法?@Björn在本例中,我使用了
INotifyPropertyChanging
,因为接口已经由实体框架实现。如果不是这样,我可能会在相关属性hanks的
集合
方法中引发PropertyChanged事件以获取信息。我删除了验证并更改了代码,因此如果StartDate>EndDate,我只需设置EndDate=StartDate。也许不是每个人都能做的事情,但它适合我的情况
BindingExpression expression = datePickerStartDate.GetBindingExpression(DatePicker.SelectedDateProperty);
if (expression != null)
{
    expression.UpdateSource();
}