我有一个业务对象绑定到一个表单(每个属性绑定到一个控件)。有一些业务规范(例如此字段不应为空,此字段必须大于等等...)。检查所有规则的最佳方法是什么?
我目前在每个控件上都有一个验证器,所以我可以检查所有验证器是否正常,但我真的不喜欢这个解决方案。事实上,这些规则已被分发,并且不容易立即看到所有规则。
我可以使用检查所有规则的大方法CheckValidaty,但这会导致对验证器进行双重检查。
你会做什么,其他解决方案?
答案 0 :(得分:5)
我建议让BusinessObject实现IDataErrorInfo
。我认为这是处理业务错误的最简洁方法。
看一下这些链接:
答案 1 :(得分:3)
有两种验证:特定于数据的验证(在持久层中)和用户界面验证。我更喜欢将验证放在输入端附近,因为通常你想向用户显示错误,并且尝试将数据验证连接到用户界面会增加更多与数据绑定间接匹配的间接。
将数据验证放在控件类中似乎不是一个好主意。这基本上意味着控件类只能用于一个特定的字段。
标准Windows Forms的处理方式是将数据验证放在容器中。这样,验证可以检查其他属性的状态,并将特定控件连接到ErrorProvider对象以显示相关的错误消息。
class EmployeeForm : UserControl
{
EmployeeObject employee;
// ...
void employeeNameTextBox_Validating (object sender, CancelEventArgs e)
{
if ( employee.Name.Trim ().Length == 0 ) {
errorProvider.SetError (employeeNameTextBox, "Employee must have a name");
e.Cancel = true;
}
}
void employeeHireDateControl_Validating (...)
{
if ( employee.HireDate < employee.BirthDate ) {
errorProvider.SetError (employeeHireDateControl,
"Employee hire date must be after birth date");
e.Cancel = true;
}
}
}
class ExplorerStyleInterface : ...
{
// ...
bool TryDisplayNewForm (Form oldForm, Form newForm)
{
if ( ! oldForm.ValidateChildren () )
return false;
else {
HideForm (oldForm);
ShowForm (newForm);
return true;
}
}
}
标准WF方法是在控件失去焦点或在容器(或容器的容器)上调用ValidateChildren时触发特定控件的Validating事件。您可以通过容器上控件的事件属性为此事件设置处理程序;处理程序自动添加到容器中。
我不确定为什么这种方式不适合你,除非你不喜欢拒绝重新关注错误的默认行为,你可以通过设置容器的AutoValidate属性来改变它(或者容器的容器)到EnableAllowFocusChange。
请具体告诉我们您对标准Windows Forms服务方式不喜欢的内容,也许我们可以提供替代方案或说服您以标准方式执行您想要的操作。
答案 2 :(得分:1)
如果您遇到这种情况:
- Many controls in the Winform to
validate
- A validation rule for each control
- You want an overall validation within the Save() command
- You don't want validation when controls focus changes
- You also need an red icon showing errors in each control
然后你可以复制并粘贴以下代码,实现一个带有2个文本框和一个“保存”按钮的表单:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ValidationApp
{
public class ValidationTestForm : Form
{
private TextBox textBox1;
private TextBox textBox2;
private Button btnSave;
private ErrorProvider errorProvider1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox2 = new System.Windows.Forms.TextBox();
this.btnSave = new System.Windows.Forms.Button();
this.errorProvider1 = new System.Windows.Forms.ErrorProvider(this.components);
((System.ComponentModel.ISupportInitialize)(this.errorProvider1)).BeginInit();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(131, 28);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 0;
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(131, 65);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(100, 20);
this.textBox2.TabIndex = 1;
//
// btnSave
//
this.btnSave.Location = new System.Drawing.Point(76, 102);
this.btnSave.Name = "btnSave";
this.btnSave.Size = new System.Drawing.Size(95, 30);
this.btnSave.TabIndex = 2;
this.btnSave.Text = "Save";
this.btnSave.UseVisualStyleBackColor = true;
this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
//
// errorProvider1
//
this.errorProvider1.ContainerControl = this;
//
// ValidationTestForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(266, 144);
this.Controls.Add(this.btnSave);
this.Controls.Add(this.textBox2);
this.Controls.Add(this.textBox1);
this.Name = "ValidationTestForm";
this.Text = "ValidationTestForm";
((System.ComponentModel.ISupportInitialize)(this.errorProvider1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
public ValidationTestForm()
{
InitializeComponent();
// path validation
this.AutoValidate = AutoValidate.Disable; // validation to happen only when you call ValidateChildren, not when change focus
this.textBox1.CausesValidation = true;
this.textBox2.CausesValidation = true;
textBox1.Validating += new System.ComponentModel.CancelEventHandler(textBox1_Validating);
textBox2.Validating += new System.ComponentModel.CancelEventHandler(textBox2_Validating);
}
private void textBox1_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
if (textBox1.Text.Length == 0)
{
e.Cancel = true;
errorProvider1.SetError(this.textBox1, "A value is required.");
}
else
{
e.Cancel = false;
this.errorProvider1.SetError(this.textBox1, "");
}
}
private void textBox2_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
if (textBox2.Text.Length == 0)
{
e.Cancel = true;
errorProvider1.SetError(this.textBox2, "A value is required.");
}
else
{
e.Cancel = false;
this.errorProvider1.SetError(this.textBox2, "");
}
}
private void btnSave_Click(object sender, EventArgs e)
{
if (this.ValidateChildren()) //will examine all the children of the current control, causing the Validating event to occur on a control
{
// Validated! - Do something then
}
}
}
}
注意:
textBox1_Validating, textBox2_Validating...do the rule check
答案 3 :(得分:0)
验证器方法有什么问题?这是完全可以接受的,你可以编写自己的,以实现自己的规则。