- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试实现 INotifyDataErrorInfo,我的模型有一些自定义类型,需要根据其使用情况进行不同的验证。我不确定如何实现此验证。
我试图在下面创建一个简单的示例来展示我正在尝试完成的工作。我不是在寻找有关更改模型的建议,因为我的实际模型要复杂得多。
我的示例模型适用于将有主持人和嘉宾的媒体事件。在安排媒体事件时,用户将输入姓名、最小和最大演示者以及最小和最大 guest 。通常,一个媒体必须有至少 1 名主持人且不超过 5 名,并且必须至少有 10 名嘉宾且不超过 50 名。
我有以下类,取自在线示例,用作我的模型类的基础。
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace NotifyDataErrorInfo
{
public class ValidatableModel : INotifyDataErrorInfo, INotifyPropertyChanged
{
public ConcurrentDictionary<string, List<string>> _errors = new ConcurrentDictionary<string, List<string>>();
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
ValidateAsync();
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public void OnErrorsChanged(string propertyName)
{
var handler = ErrorsChanged;
if (handler != null)
{
handler(this, new DataErrorsChangedEventArgs(propertyName));
}
}
public IEnumerable GetErrors(string propertyName)
{
if (propertyName == null) return null;
List<string> errorsForName;
_errors.TryGetValue(propertyName, out errorsForName);
return errorsForName;
}
public bool HasErrors
{
get
{
return _errors.Any(kv => kv.Value != null && kv.Value.Count > 0);
}
}
public Task ValidateAsync()
{
return Task.Run(() => Validate());
}
private object _lock = new object();
public void Validate()
{
lock (_lock)
{
var validationContext = new ValidationContext(this, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(this, validationContext, validationResults, true);
foreach (var kv in _errors.ToList())
{
if (validationResults.All(r => r.MemberNames.All(m => m != kv.Key)))
{
List<string> outLi;
_errors.TryRemove(kv.Key, out outLi);
OnErrorsChanged(kv.Key);
}
}
var q = from r in validationResults
from m in r.MemberNames
group r by m into g
select g;
foreach (var prop in q)
{
var messages = prop.Select(r => r.ErrorMessage).ToList();
if (_errors.ContainsKey(prop.Key))
{
List<string> outLi;
_errors.TryRemove(prop.Key, out outLi);
}
_errors.TryAdd(prop.Key, messages);
OnErrorsChanged(prop.Key);
}
}
}
}
}
因为我在两个地方使用最小值和最大值,所以我创建了以下类来存储最小值和最大值。这是我示例中过于简化的部分,但应该能说明问题。
namespace NotifyDataErrorInfo
{
public class MinMaxValues : ValidatableModel
{
private int min;
private int max;
public int Min
{
get
{
return min;
}
set
{
if (!min.Equals(value))
{
min = value;
RaisePropertyChanged(nameof(Min));
OnErrorsChanged(nameof(Min));
}
}
}
public int Max
{
get
{
return max;
}
set
{
if (!max.Equals(value))
{
max = value;
RaisePropertyChanged(nameof(Max));
OnErrorsChanged(nameof(Max));
}
}
}
public MinMaxValues()
{
Min = 0;
Max = 0;
}
}
}
这是我的 MediaEvent 类,您可以看到它正在为 MinMaxPresenters 和 MinMaxGuests 使用 MinMaxValues 类。
using System.ComponentModel.DataAnnotations;
namespace NotifyDataErrorInfo
{
public class MediaEvent: ValidatableModel
{
private string name;
private MinMaxValues minMaxPresenters;
private MinMaxValues minMaxGuests;
public MediaEvent()
{
name = string.Empty;
minMaxPresenters = new MinMaxValues();
minMaxGuests = new MinMaxValues();
this.Validate();
this.minMaxPresenters.Validate();
this.minMaxGuests.Validate(); }
}
[Required]
[StringLength(10, MinimumLength = 5)]
public string Name
{
get
{
return name;
}
set
{
if(!name.Equals(value))
{
name = value;
RaisePropertyChanged(nameof(Name));
}
}
}
public MinMaxValues MinMaxPresenters
{
get
{
return minMaxPresenters;
}
set
{
if (!minMaxPresenters.Equals(value))
{
minMaxPresenters = value;
RaisePropertyChanged(nameof(MinMaxPresenters));
}
}
}
public MinMaxValues MinMaxGuests
{
get
{
return minMaxGuests;
}
set
{
if (!minMaxGuests.Equals(value))
{
minMaxGuests = value;
RaisePropertyChanged(nameof(MinMaxGuests));
}
}
}
}
}
这是我的主窗口的 XAML
<Window
x:Class="NotifyDataErrorInfo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:NotifyDataErrorInfo"
mc:Ignorable="d"
Title="MainWindow"
Height="209" Width="525"
ResizeMode="NoResize">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="42*"/>
<RowDefinition Height="43*"/>
<RowDefinition Height="42*"/>
<RowDefinition Height="43*"/>
</Grid.RowDefinitions>
<Label
Content="Meeting Name: "
Grid.Row="0" Grid.Column="0"/>
<TextBox
Text="{Binding Name}"
Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3"/>
<Label
Content="Min Presenters: "
Grid.Row="1" Grid.Column="0"/>
<TextBox
Text="{Binding MinMaxPresenters.Min}"
Grid.Row="1" Grid.Column="1"/>
<Label
Content="Max Presenters: "
Grid.Row="1" Grid.Column="2"/>
<TextBox
Text="{Binding MinMaxPresenters.Max}"
Grid.Row="1" Grid.Column="3"/>
<Label
Content="Min Guests: "
Grid.Row="2" Grid.Column="0"/>
<TextBox
Text="{Binding MinMaxGuests.Min}"
Grid.Row="2" Grid.Column="1"/>
<Label
Content="Max Guests: "
Grid.Row="2" Grid.Column="2"/>
<TextBox
Text="{Binding MinMaxGuests.Max}"
Grid.Row="2" Grid.Column="3"/>
<Button
x:Name="TestButton"
Content="TEST"
Click="TestButton_Click"
Grid.Row="3" Grid.Column="3"/>
</Grid>
</Window>
使用
在 App.xaml.cs 中加载protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainWindow = new MainWindow();
var mediaEvent = new MediaEvent();
mainWindow.DataContext = mediaEvent;
mainWindow.Show();
}
在 MediaEvent 类中,我用 [Required] 和 [StringLength(10, MinimumLength = 5)] 属性修饰了 Name 属性。这些按预期工作。当输入的名称短于 5 个字符或长于 10 个字符时,我会在名称文本框周围看到一个红色框,表明存在错误。
现在我不确定如何对 MinMaxPresenters.Min、MinMaxPresenters.Max、MinMaxGuests.Min 和 MinMaxGuests.Max 进行验证
如果我用 [Range(1, 5)] 之类的东西装饰 MinMaxValues 类中的 Min 属性,我可以确认验证正在进行并且 UI 会相应更新。
问题是验证适用于演示者和 guest 的最小值。我需要为演示者和 guest 验证不同的最小值。
在 MediaEvent 中,我连接到 minMaxPresenters 的 PropertyChanged 事件。在该事件处理程序中,我尝试根据演示者规则(范围 = 1 到 5)验证最小值和最大值。如果验证失败,我会尝试添加到 _errors 集合。
在我的构造函数中我添加了
minMaxPresenters.PropertyChanged += MinMaxPresenters_PropertyChanged;
然后创建以下内容
private void MinMaxPresenters_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Min")
{
if (minMaxPresenters.Min < 1)
{
_errors.TryAdd("MinMaxPresenters.Min", new List<string> { "A media event requires at least 1 presenter" });
OnErrorsChanged("MinMaxPresenters.Min");
}
}
else if (e.PropertyName == "Max")
{
if (minMaxPresenters.Max <= minMaxPresenters.Min)
{
_errors.TryAdd("MinMaxPresenters.Max", new List<string> { "The max presenters must be greater than the min" });
OnErrorsChanged("MinMaxPresenters.Max");
}
else if (minMaxPresenters.Max > 5)
{
_errors.TryAdd("MinMaxPresenters.Max", new List<string> { "A media event can't have more than 5 presenters" });
OnErrorsChanged("MinMaxPresenters.Max");
}
}
}
当我输入超出演示者范围的最小值和最大值时,我可以看到我的错误被添加到我的模型中的 _errors 集合中,但我的 View 并不表示有任何错误。
我接近了吗?我这样做是不是错了?
我还需要根据其他属性值验证值,因此需要进行自定义验证并通过代码添加错误。一个例子是上面的最大值的验证。演示者的最大值需要小于 5,但也必须大于为最小值输入的值。
您可以忽略 MainWindow 中的按钮。只需单击并中断后面的代码,这样我就可以看到集合中有什么错误。
此外,如果有人对公开 _errors 发表评论,这只是尝试添加错误的一种快速方法。理想情况下,我会创建 AddError 和 RemoveError 方法。
最佳答案
你的问题在这里
_errors.TryAdd("MinMaxPresenters.Min", new List<string>
{ "A media event requires at least 1 presenter" });
您正在将错误添加到父对象,但 WPF 绑定(bind)在属性链中的最后一个对象上查找错误。验证和 WPF 是一个令人头疼的问题。使用您的模型,您应该这样做
MinMaxPresenters._errors.TryAdd("Min", new List<string>
{ "A media event requires at least 1 presenter" });
然后错误将被 UI 拾取。
在我开发的框架中,我能够执行您最初尝试的操作,但我解析了错误字符串 "MinMaxPresenters.Min",然后查找名为 "MinMaxPresenters 的属性" 并自动将验证错误转发给子对象。
我对 AddErrors 的实现是
public void AddErrors(string path, IEnumerable<Exception> errors, bool nest = true)
{
var exceptions = errors as IList<Exception> ?? errors.ToList();
var nestedPath = path.Split('.').ToList();
if (nestedPath.Count > 1 && nest)
{
var tail = string.Join(".", nestedPath.Skip(1));
// Try and get a child property as Maybe<INotifyDataExceptionInfo>
// and if it exists pass the error
// downwards after stripping off the first part of
// the path.
var notifyDataExceptionInfo = this.TryGet<INotifyDataExceptionInfo,INotifyDataExceptionInfo>(nestedPath[0]);
if(notifyDataExceptionInfo.IsSome)
notifyDataExceptionInfo.Value.AddErrors(tail, exceptions);
}
_Errors.RemoveKey(path);
foreach (var error in exceptions)
{
_Errors.Add(path, error);
}
RaiseErrorEvents(path);
}
** TryGet是一种通过反射获取属性值的方法
** 完整的实现可以在 this location 找到.
关于c# - 将 INotifyDataErrorInfo 与模型中需要自定义验证的子对象一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37602219/
我的一位教授给了我们一些考试练习题,其中一个问题类似于下面(伪代码): a.setColor(blue); b.setColor(red); a = b; b.setColor(purple); b
我似乎经常使用这个测试 if( object && object !== "null" && object !== "undefined" ){ doSomething(); } 在对象上,我
C# Object/object 是值类型还是引用类型? 我检查过它们可以保留引用,但是这个引用不能用于更改对象。 using System; class MyClass { public s
我在通过 AJAX 发送 json 时遇到问题。 var data = [{"name": "Will", "surname": "Smith", "age": "40"},{"name": "Wil
当我尝试访问我的 View 中的对象 {{result}} 时(我从 Express js 服务器发送该对象),它只显示 [object][object]有谁知道如何获取 JSON 格式的值吗? 这是
我有不同类型的数据(可能是字符串、整数......)。这是一个简单的例子: public static void main(String[] args) { before("one"); }
嗨,我是 json 和 javascript 的新手。 我在这个网站找到了使用json数据作为表格的方法。 我很好奇为什么当我尝试使用 json 数据作为表时,我得到 [Object,Object]
已关闭。此问题需要 debugging details 。目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and the
我听别人说 null == object 比 object == null check 例如: void m1(Object obj ) { if(null == obj) // Is thi
Match 对象 提供了对正则表达式匹配的只读属性的访问。 说明 Match 对象只能通过 RegExp 对象的 Execute 方法来创建,该方法实际上返回了 Match 对象的集合。所有的
Class 对象 使用 Class 语句创建的对象。提供了对类的各种事件的访问。 说明 不允许显式地将一个变量声明为 Class 类型。在 VBScript 的上下文中,“类对象”一词指的是用
Folder 对象 提供对文件夹所有属性的访问。 说明 以下代码举例说明如何获得 Folder 对象并查看它的属性: Function ShowDateCreated(f
File 对象 提供对文件的所有属性的访问。 说明 以下代码举例说明如何获得一个 File 对象并查看它的属性: Function ShowDateCreated(fil
Drive 对象 提供对磁盘驱动器或网络共享的属性的访问。 说明 以下代码举例说明如何使用 Drive 对象访问驱动器的属性: Function ShowFreeSpac
FileSystemObject 对象 提供对计算机文件系统的访问。 说明 以下代码举例说明如何使用 FileSystemObject 对象返回一个 TextStream 对象,此对象可以被读
我是 javascript OOP 的新手,我认为这是一个相对基本的问题,但我无法通过搜索网络找到任何帮助。我是否遗漏了什么,或者我只是以错误的方式解决了这个问题? 这是我的示例代码: functio
我可以很容易地创造出很多不同的对象。例如像这样: var myObject = { myFunction: function () { return ""; } };
function Person(fname, lname) { this.fname = fname, this.lname = lname, this.getName = function()
任何人都可以向我解释为什么下面的代码给出 (object, Object) 吗? (console.log(dope) 给出了它应该的内容,但在 JSON.stringify 和 JSON.parse
我正在尝试完成散点图 exercise来自免费代码营。然而,我现在只自己学习了 d3 几个小时,在遵循 lynda.com 的教程后,我一直在尝试确定如何在工具提示中显示特定数据。 This code
我是一名优秀的程序员,十分优秀!