C# 如何使用INotifyDataErrorInfo接口验证可观察集合

C# 如何使用INotifyDataErrorInfo接口验证可观察集合,c#,wpf,validation,mvvm,inotifydataerrorinfo,C#,Wpf,Validation,Mvvm,Inotifydataerrorinfo,我正在使用MVVM模式和Prism框架开发WPF应用程序 我有一个基本的数据类,如下所示 public class ProductDecorator : DecoratorBase<Product> { private string _ProductShortName; private Boolean _IsSelected = false; // I have omitted some code for clarity here. [Requir

我正在使用
MVVM
模式和
Prism
框架开发
WPF
应用程序

我有一个基本的数据类,如下所示

public class ProductDecorator : DecoratorBase<Product>
{
    private string _ProductShortName;
    private Boolean _IsSelected = false;

    // I have omitted some code for clarity here.

    [Required]
    public int ProductID
    {
        get { return BusinessEntity.ProductID; }
        set
        {
            SetProperty(() => BusinessEntity.ProductID == value,
                            () => BusinessEntity.ProductID = value);
        }
    }

    public Boolean IsSelected
    {
        get { return _IsSelected; }
        set
        {
            SetProperty(ref _IsSelected, value);
        }
    }
 }
公共类ProductDecorator:DecoratorBase
{
私有字符串_ProductShortName;
私有布尔值_isselect=false;
//为了清楚起见,我省略了一些代码。
[必需]
公共int ProductID
{
获取{return BusinessEntity.ProductID;}
设置
{
SetProperty(()=>BusinessEntity.ProductID==值,
()=>BusinessEntity.ProductID=value);
}
}
公营部门选举产生
{
获取{return}IsSelected;}
设置
{
SetProperty(参考值为选定值);
}
}
}
我在ViewModel中创建上述数据类的可观察集合

public class SaleInvoiceViewModel {

    private ObservableCollection<ProductDecorator> _productDecorators;
    public ObservableCollection<ProductDecorator> ProductDecorators
    {
        get { return _productDecorators; }
        set { SetProperty(ref _productDecorators, value); }
    }
}
公共类SaleInvoiceViewModel{
私人可观察收集(productDecorators);;
公共可观察收集产品装饰器
{
获取{return\u productDecorators;}
集合{SetProperty(ref _productDecorators,value);}
}
}
我将这个可观察的集合绑定到视图中的列表框

<telerik:RadListBox ItemsSource="{Binding ProductDecorators}" HorizontalAlignment="Stretch" Margin="5,10,5,5" Grid.Column="1" VerticalAlignment="Top">
  <telerik:RadListBox.ItemTemplate>
     <DataTemplate>
         <StackPanel Orientation="Horizontal">
             <CheckBox Margin="2" IsChecked="{Binding IsSelected}" />
             <TextBlock Text="{Binding ProductShortName}" FontSize="14" />
         </StackPanel>
     </DataTemplate>
  </telerik:RadListBox.ItemTemplate>
</telerik:RadListBox>

从上面的上下文中,我想验证“用户必须在列表框中选择至少一项”。换句话说,
IsSelected
属性必须是可观察集合中的
ProductUmDecorators
类中的
true


目前我使用
INotifyDataErrorInfo
接口和
数据注释
作为验证规则。我已经失去了我应该如何实现我的问题以实现此验证

有关stackoverflow的问题很多,但没有确切的答案。所以我决定发布我的解决方案作为这个问题的答案。 问题的背景是勾选“用户必须在列表框中选择一个与可观察集合绑定的项目”

第一步,
ObservableCollection
need
IsSelected
属性中的项(实体)

public class ProductDecorator : DecoratorBase<Product>
{
     private string _ProductShortName;
     private Boolean _IsSelected = false;

     // I have omitted some code for clarity here.

     public Boolean IsSelected
     {
         get { return _IsSelected; }
         set
         {
             SetProperty(ref _IsSelected, value);
         }
     }
}
仔细查看上述代码中的
EntityPropertyChanged
方法。只要
ObservableCollection
中的任何项中的任何属性发生更改,就会触发此方法。然后,您只需在
EntityPropertyChanged
方法中调用
Validate
方法

private void EntityPropertyChanged( object sender, PropertyChangedEventArgs e )
{
      if (e.PropertyName == "IsSelected")
            this.Product.ValidateProperty("ProductUmDecorators");
}
如果更改的属性是
IsSelected
,则将运行
ValidatedProperty
方法。 我将省略
ValidateProperty
方法的详细实现。此方法将尝试使用
DataAnnotations
验证属性,并在出现任何错误时触发ErrorChanged事件。你可以了解细节

最后,您需要在Entity/ViewModel/Decorator类中实现自定义的
DataAnnotation
ValidationAttribute
,在该类中,
ObservableCollection
属性作为以下代码存在

public class SaleInvoiceViewModel {

     private ObservableCollection<ProductDecorator> _productDecorators;
     [AtLeastChooseOneItem(ErrorMessage = "Choose at least one item in the following list.")]
     public ObservableCollection<ProductDecorator> ProductDecorators
     {
         get { return _productDecorators; }
         set { SetProperty(ref _productDecorators, value); }
     }
}

public class AtLeastChooseOneItem : ValidationAttribute
{
    protected override ValidationResult IsValid( object value, ValidationContext validationContext )
    {
        ProductDecorator tmpEntity = (ProductDecorator) validationContext.ObjectInstance;
        var tmpCollection = (ObservableCollection<ProductUmDecorator>) value;

        if ( tmpCollection.Count == 0 )
            return ValidationResult.Success;

        foreach ( var item in tmpCollection )
        {
            if ( item.IsSelected == true )
                return ValidationResult.Success;
        }

        return new ValidationResult( ErrorMessage );
    }
}
公共类SaleInvoiceViewModel{
私人可观察收集(productDecorators);;
[AtLeastChooseOneItem(ErrorMessage=“在以下列表中至少选择一项。”)]
公共可观察收集产品装饰器
{
获取{return\u productDecorators;}
集合{SetProperty(ref _productDecorators,value);}
}
}
公共类至少选择一项:ValidationAttribute
{
受保护的重写ValidationResult有效(对象值,ValidationContext ValidationContext)
{
ProductDecorator tmpEntity=(ProductDecorator)validationContext.ObjectInstance;
var tmpCollection=(ObservableCollection)值;
if(tmpCollection.Count==0)
返回ValidationResult.Success;
foreach(tmpCollection中的var项)
{
如果(item.IsSelected==true)
返回ValidationResult.Success;
}
返回新的ValidationResult(ErrorMessage);
}
}

这是一个有点复杂的解决方案,但这是迄今为止我发现的最可靠的解决方案。

我会选择一个SelectedProductUmDecorator聚会,如果为空,则将其标记为无效。您必须有其他方法来指示集合中的选定元素[edit:duh,yeah],因此如果未选择任何元素,可以尝试将ProductUmDecorators标记为无效?
public class SaleInvoiceViewModel {

     private ObservableCollection<ProductDecorator> _productDecorators;
     [AtLeastChooseOneItem(ErrorMessage = "Choose at least one item in the following list.")]
     public ObservableCollection<ProductDecorator> ProductDecorators
     {
         get { return _productDecorators; }
         set { SetProperty(ref _productDecorators, value); }
     }
}

public class AtLeastChooseOneItem : ValidationAttribute
{
    protected override ValidationResult IsValid( object value, ValidationContext validationContext )
    {
        ProductDecorator tmpEntity = (ProductDecorator) validationContext.ObjectInstance;
        var tmpCollection = (ObservableCollection<ProductUmDecorator>) value;

        if ( tmpCollection.Count == 0 )
            return ValidationResult.Success;

        foreach ( var item in tmpCollection )
        {
            if ( item.IsSelected == true )
                return ValidationResult.Success;
        }

        return new ValidationResult( ErrorMessage );
    }
}