- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
警告:很长很详细的帖子。
好的,使用 MVVM 时在 WPF 中进行验证。我现在读了很多东西,看了很多 SO 问题,并尝试了很多方法,但在某些时候,一切都感觉有些 hacky,我真的不知道如何以正确的方式做到这一点™。
理想情况下,我希望使用 IDataErrorInfo
在 View 模型中进行所有验证;所以这就是我所做的。然而,有不同的方面使该解决方案不是整个验证主题的完整解决方案。
情况
让我们采用以下简单的形式。如您所见,这没什么特别的。我们只有两个文本框,它们分别绑定(bind)到 View 模型中的 string
和 int
属性。此外,我们有一个绑定(bind)到 ICommand
的按钮。
所以对于验证我们现在有两个选择:
ValidatesOnDataErrors
的普通数据绑定(bind),这是默认行为。因此,当文本更改时,绑定(bind)会更新源并触发该属性的
IDataErrorInfo
验证;错误被报告回 View 。到现在为止还挺好。
IDataErrorInfo
的工作方式,主要是向 View 报告错误。因此 View 可以轻松查看是否有任何错误,显示它们,甚至使用
Validation.Errors
显示注释。此外,验证总是在查看单个属性时发生。
IDataErrorInfo
验证。这通常使用单独的
IsValid
属性来完成。好处是这也可以很容易地用于禁用命令。缺点是这可能会过于频繁地对所有属性运行验证,但大多数验证应该足够简单,不会损害性能。另一种解决方案是使用验证记住哪些属性产生了错误并只检查这些属性,但这在大多数情况下似乎有点过于复杂且不必要。
IDataErrorInfo
提供对所有属性的验证,我们可以简单地在 View 模型本身中使用该接口(interface)来运行整个对象的验证。问题介绍:
int
。然而, View 中使用的文本框本身只支持文本。因此,当绑定(bind)到 View 模型中的
int
时,数据绑定(bind)引擎将自动执行类型转换——或者至少它会尝试。如果您可以在用于数字的文本框中输入文本,则内部并不总是有效数字的可能性很高:因此数据绑定(bind)引擎将无法转换并抛出
FormatException
。
Binding.ValidatesOnExceptions
,这对于在 setter 中抛出的异常是必需的。错误消息确实有一个通用文本,所以这可能是一个问题。我通过使用
Binding.UpdateSourceExceptionFilter
处理程序为自己解决了这个问题,检查抛出的异常并查看源属性,然后生成一个不太通用的错误消息。所有这些都封装到我自己的 Binding 标记扩展中,所以我可以拥有我需要的所有默认值。
IDataErrorInfo
验证显然不适用。
Validation.HasError
属性数据绑定(bind)回 View 模型来完成(这不能直接实现),因此 View 模型可以首先检查 View 的状态。
Binding.UpdateSourceExceptionFilter
中处理的异常中继到 View 模型,因此它也会收到通知。 View 模型甚至可以为绑定(bind)提供一些接口(interface)来报告这些事情,允许自定义错误消息而不是通用的每个类型的错误消息。但这会创建从 View 到 View 模型的更强耦合,我通常希望避免这种情况。
string
属性并在 View 模型中进行转换。这显然会将所有验证转移到 View 模型,但也意味着数据绑定(bind)引擎通常会处理大量重复的事情。此外,它会改变 View 模型的语义。对我来说, View 是为 View 模型构建的,而不是相反——当然 View 模型的设计取决于我们想象 View 要做什么,但 View 如何做到这一点仍然存在普遍的自由。所以 View 模型定义了一个
int
属性,因为有一个数字; View 现在可以使用文本框(允许所有这些问题),或者使用 native 处理数字的东西。所以不,将属性类型更改为
string
对我来说不是一个选择。
IDataErrorInfo
和抛出的异常。如果 View 模型可用,它们甚至可以检查所有这些验证源的验证状态,例如使用
CommitEdit
。
if (bindingGroup.CommitEdit())
SaveEverything();
CommitEdit
只有在所有验证都成功时才会返回 true。它将考虑
IDataErrorInfo
并检查绑定(bind)异常。这似乎是选项 2 的完美解决方案。唯一有点麻烦的是使用绑定(bind)管理绑定(bind)组,但我已经为自己构建了一些主要解决这个问题的东西 (
related)。
UpdateSourceTrigger
。要使用绑定(bind)组实现上面的选择 1,我们基本上必须更改触发器。因为我有一个自定义绑定(bind)扩展,所以这很简单,我只是将它设置为
LostFocus
。
IDataErrorInfo
将照常运行。如果无法更新, View 仍然可以看到它。如果我们点击我们的按钮,底层命令可以调用
CommitEdit
(虽然不需要提交任何东西)并获得总验证结果,看看它是否可以继续。
Validation.HasError
添加一个触发器来禁用 View 中的按钮,所以这并非不可能。
IDataErrorInfo
进行数据验证,我个人认为这是进行验证工作最舒适的实用程序。我正在使用类似于 Sheridan 在下面的回答中建议的实用程序,因此维护工作也很好。
UpdateSourceExceptionFilter
来检测 View 端的绑定(bind)异常。在那里,我可以从绑定(bind)表达式的
DataItem
获得对 View 模型的引用。然后我有一个接口(interface)
IReceivesBindingErrorInformation
,它将 View 模型注册为可能的接收器,以获取有关绑定(bind)错误的信息。然后我使用它来将绑定(bind)路径和异常传递给 View 模型:
object OnUpdateSourceExceptionFilter(object bindExpression, Exception exception)
{
BindingExpression expr = (bindExpression as BindingExpression);
if (expr.DataItem is IReceivesBindingErrorInformation)
{
((IReceivesBindingErrorInformation)expr.DataItem).ReceiveBindingErrorInformation(expr.ParentBinding.Path.Path, exception);
}
// check for FormatException and produce a nicer error
// ...
}
HashSet<string> bindingErrors = new HashSet<string>();
void IReceivesBindingErrorInformation.ReceiveBindingErrorInformation(string path, Exception exception)
{
bindingErrors.Add(path);
}
IDataErrorInfo
重新验证属性时,我就知道绑定(bind)有效,我可以从哈希集中清除该属性。
最佳答案
Warning: Long answer also
IDataErrorInfo
接口(interface)进行验证,但我已根据需要对其进行了自定义。我想你会发现它也解决了你的一些问题。与您的问题的一个区别是我在我的基本数据类型类中实现了它。
protected ObservableCollection<string> errors = new ObservableCollection<string>();
public virtual ObservableCollection<string> Errors
{
get { return errors; }
}
protected ObservableCollection<string> externalErrors = new ObservableCollection<string>();
public ObservableCollection<string> ExternalErrors
{
get { return externalErrors; }
}
HasError
属性,它查看我的集合:
public virtual bool HasError
{
get { return Errors != null && Errors.Count > 0; }
}
Grid.Visibility
将其绑定(bind)到
BoolToVisibilityConverter
,例如。显示一个
Grid
,里面有一个集合控件,当有错误时会显示错误。它还允许我将
Brush
更改为
Red
以突出显示错误(使用另一个
Converter
),但我想您明白了。
Errors
属性并实现
Item
索引器(在此示例中简化):
public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
errors.AddUniqueIfNotEmpty(this["Name"]);
errors.AddUniqueIfNotEmpty(this["EmailAddresses"]);
errors.AddUniqueIfNotEmpty(this["SomeOtherProperty"]);
errors.AddRange(ExternalErrors);
return errors;
}
}
public override string this[string propertyName]
{
get
{
string error = string.Empty;
if (propertyName == "Name" && Name.IsNullOrEmpty()) error = "You must enter the Name field.";
else if (propertyName == "EmailAddresses" && EmailAddresses.Count == 0) error = "You must enter at least one e-mail address into the Email address(es) field.";
else if (propertyName == "SomeOtherProperty" && SomeOtherProperty.IsNullOrEmpty()) error = "You must enter the SomeOtherProperty field.";
return error;
}
}
AddUniqueIfNotEmpty
方法是自定义的
extension
方法,并且“按照 jar 头上所说的去做”。请注意它将如何依次调用我想要验证的每个属性并从中编译一个集合,而忽略重复的错误。
ExternalErrors
集合,我可以验证无法在数据类中验证的内容:
private void ValidateUniqueName(Genre genre)
{
string errorMessage = "The genre name must be unique";
if (!IsGenreNameUnique(genre))
{
if (!genre.ExternalErrors.Contains(errorMessage)) genre.ExternalErrors.Add(errorMessage);
}
else genre.ExternalErrors.Remove(errorMessage);
}
int
字段中输入字母字符的情况的观点,我倾向于对
IsNumeric AttachedProperty
使用自定义
TextBox
,例如。我不会让他们犯这种错误。我总觉得与其让它发生然后再修复它,不如阻止它。
INotifyDataErrorInfo
接口(interface),其中包含一些附加功能。您可以从 MSDN 上的
INotifyDataErrorInfo
Interface 页面中找到更多信息。
ExternalErrors
属性只是让我添加与该对象外部的数据对象相关的错误...抱歉,我的示例不完整...如果我向您展示了
IsGenreNameUnique
方法,您就会看到它在集合中的所有
LinQ
数据项上使用
Genre
来确定对象的名称是否唯一:
private bool IsGenreNameUnique(Genre genre)
{
return Genres.Where(d => d.Name != string.Empty && d.Name == genre.Name).Count() == 1;
}
int
/
string
问题,我能看到您在数据类中收到这些错误的唯一方法是,如果您将所有属性声明为
object
,那么您将有很多转换要做。也许你可以像这样将你的属性加倍:
public object FooObject { get; set; } // Implement INotifyPropertyChanged
public int Foo
{
get { return FooObject.GetType() == typeof(int) ? int.Parse(FooObject) : -1; }
}
Foo
并且在
FooObject
中使用了
Binding
,你可以这样做:
public override string this[string propertyName]
{
get
{
string error = string.Empty;
if (propertyName == "FooObject" && FooObject.GetType() != typeof(int))
error = "Please enter a whole number for the Foo field.";
...
return error;
}
}
关于c# - 使用 MVVM 进行正确验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19498485/
这个问题已经有答案了: How to do case insensitive string comparison? (23 个回答) 已关闭 3 年前。 用户在我的输入栏中写入“足球”,然后执行第 6
啊,不习惯 javascript 中的字符串。 character_id= + id + correct= + correctOrIncorrect 这就是我需要制作成字符串的内容。如果您无法猜测字符
$(function() { var base_price = 0; CalculatePrice(); $(".math1").on('change', function(e) { Calc
我找不到任何文章回答问题:将Spinnaker部署到Spinnaker将管理的同一Kubernetes集群是否安全/正确?我主要是指生产,HA部署。 最佳答案 我认为Spinnaker和Kuberne
我正在使用MSVC在Windows上从源代码(官方源代码发布,而不是从仓库中)构建Qt5(Qt 5.15.0)。 我正在设置环境。变量,依赖项等,然后运行具有1600万个选项的configure,最后
我需要打印一个包含重复单词的数组。我的数组已经可以工作,但我不知道如何正确计算单词数。我已经知道,当我的索引计数器 (i) 为 49 时,并且当 (i) 想要计数到 50 时,我会收到错误,但我不知道
我正在遵循一个指南,该指南允许 Google map 屏幕根据屏幕尺寸禁用滚动。我唯一挣扎的部分是编写一个代码,当我手动调整屏幕大小时动态更改 True/False 值。 这是我按照说明操作的网站,但
我有一个类“FileButton”。它的目的是将文件链接到 JButton,FileButton 继承自 JButton。子类继承自此以使用链接到按钮的文件做有用的事情。 JingleCardButt
我的 friend 数组只返回一个数字而不是所有数字。 ($myfriends = 3) 应该是…… ($myfriends = 3 5 7 8 9 12). 如果我让它进入 while 循环……整个
这个问题在这里已经有了答案: Is there a workaround to make CSS classes with names that start with numbers valid?
我正在制作一个 JavaScript 函数,当调整窗口大小时,它会自动将 div 的大小调整为与窗口相同的宽度/高度。 该功能非常基本,但我注意到在调整窗口大小时出现明显的“绘制”滞后。在 JS fi
此问题的基本视觉效果可在 http://sevenx.de/demo/bootstrap-carousel/inc.carousel/tabbed-slider.html 获得。 - 如果你想看一看。
我明白,如果我想从函数返回一个字符串文字或一个数组,我应该将其声明为静态的,这样当被调用的函数被返回时,内容就不会“消亡”。 但我的问题是,当我在函数内部使用 malloc 分配内存时会怎样? 在下面
在 mySQL 数据库中存储 true/false/1/0 值最合适(读取数据消耗最少)的数据字段是什么? 我以前使用过一个字符长的 tinyint,但我不确定它是否是最佳解决方案? 谢谢! 最佳答案
我想一次读取并处理CSV文件第一行中的条目(例如打印)。我假设使用Unix风格的\n换行符,没有条目长度超过255个字符,并且(现在)在EOF之前有一个换行符。这意味着它是fgets()后跟strto
所以,我们都知道 -1 > 2u == true 的 C/C++ 有符号/无符号比较规则,并且我有一种情况,我想有效地实现“正确”比较。 我的问题是,考虑到人们熟悉的尽可能多的架构,哪种方法更有效。显
**摘要:**文章的标题看似自相矛盾。 本文分享自华为云社区《Java异常处理:如何写出“正确”但被编译器认为有语法错误的程序》,作者: Jerry Wang 。 文章的标题看似自相矛盾,然而我在“正
我有一个数据框,看起来像: dataDemo % mutate_each(funs(ifelse(. == '.', REF, as.character(.))), -POS) # POS REF
有人可以帮助我使用 VBScript 重新格式化/正确格式化带分隔符的文本文件吗? 我有一个文本文件 ^分界如下: AGREE^NAME^ADD1^ADD2^ADD3^ADD4^PCODE^BAL^A
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我是一名优秀的程序员,十分优秀!