- 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/
可不可以命名为MVVM模型?因为View通过查看模型数据。 View 是否应该只与 ViewModelData 交互?我确实在某处读到正确的 MVVM 模型应该在 ViewModel 而不是 Mode
我正在阅读有关设计模式的文章,虽然作者们都认为观察者模式很酷,但在设计方面,每个人都在谈论 MVC。 我有点困惑,MVC 图不是循环的,代码流具有闭合拓扑不是很自然吗?为什么没有人谈论这种模式: mo
我正在开发一个 Sticky Notes 项目并在 WPF 中做 UI,显然将 MVVM 作为我的架构设计选择。我正在重新考虑我的模型、 View 和 View 模型应该是什么。 我有一个名为 Not
不要混淆:How can I convert List to Hashtable in C#? 我有一个模型列表,我想将它们组织成一个哈希表,以枚举作为键,模型列表(具有枚举的值)作为值。 publi
我只是花了一些时间阅读这些术语(我不经常使用它们,因为我们没有任何 MVC 应用程序,我通常只说“模型”),但我觉得根据上下文,这些意味着不同的东西: 实体 这很简单,它是数据库中的一行: 2) In
我想知道你们中是否有人知道一些很好的教程来解释大型应用程序的 MVVM。我发现关于 MVVM 的每个教程都只是基础知识解释(如何实现模型、 View 模型和 View ),但我对在应用程序页面之间传递
我想realm.delete() 我的 Realm 中除了一个模型之外的所有模型。有什么办法可以不列出所有这些吗? 也许是一种遍历 Realm 中当前存在的所有类型的方法? 最佳答案 您可以从您的 R
我正在尝试使用 alias 指令模拟一个 Eloquent 模型,如下所示: $transporter = \Mockery::mock('alias:' . Transporter::class)
我正在使用 stargazer 创建我的 plm 汇总表。 library(plm) library(pglm) data("Unions", package = "pglm") anb1 <- pl
我读了几篇与 ASP.NET 分层架构相关的文章和问题,但是读得太多后我有点困惑。 UI 层是在 ASP.NET MVC 中开发的,对于数据访问,我在项目中使用 EF。 我想通过一个例子来描述我的问题
我收到此消息错误: Inceptionv3.mlmodel: unable to read document 我下载了最新版本的 xcode。 9.4 版测试版 (9Q1004a) 最佳答案 您没有
(同样,一个 MVC 验证问题。我知道,我知道......) 我想使用 AutoMapper ( http://automapper.codeplex.com/ ) 来验证我的创建 View 中不在我
需要澄清一件事,现在我正在处理一个流程,其中我有两个 View 模型,一个依赖于另一个 View 模型,为了处理这件事,我尝试在我的基本 Activity 中注入(inject)两个 View 模型,
如果 WPF MVVM 应该没有代码,为什么在使用 ICommand 时,是否需要在 Window.xaml.cs 代码中实例化 DataContext 属性?我已经并排观看并关注了 YouTube
当我第一次听说 ASP.NET MVC 时,我认为这意味着应用程序由三个部分组成:模型、 View 和 Controller 。 然后我读到 NerdDinner并学习了存储库和 View 模型的方法
Platform : ubuntu 16.04 Python version: 3.5.2 mmdnn version : 0.2.5 Source framework with version :
我正在学习本教程:https://www.raywenderlich.com/160728/object-oriented-programming-swift ...并尝试对代码进行一些个人调整,看看
我正试图围绕 AngularJS。我很喜欢它,但一个核心概念似乎在逃避我——模型在哪里? 例如,如果我有一个显示多个交易列表的应用程序。一个列表向服务器查询匹配某些条件的分页事务集,另一个列表使用不同
我在为某个应用程序找出最佳方法时遇到了麻烦。我不太习惯取代旧 TLA(三层架构)的新架构,所以这就是我的来源。 在为我的应用程序(POCO 类,对吧??)设计模型和 DAL 时,我有以下疑问: 我的模
我有两个模型:Person 和 Department。每个人可以在一个部门工作。部门可以由多人管理。我不确定如何在 Django 模型中构建这种关系。 这是我不成功的尝试之一 [models.py]:
我是一名优秀的程序员,十分优秀!