C# WinForm UI 验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/769184/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WinForm UI Validation
提问by Refracted Paladin
I need to implement input validation throughout my winform app. There are many different forms where data can be entered and I would like to not go control by control by form and create isValid etc per item. How have others dealt with this?
我需要在我的 winform 应用程序中实现输入验证。有许多不同的表单可以输入数据,我不想通过表单进行控制,并为每个项目创建 isValid 等。其他人是如何处理这个问题的?
I see that most related posts deal with Web Apps and/or mention Enterprise Library Validation Application Block. Now I admit I haven't thoroughly researched ELVAB but it seemslike overkill for what I need. My current thought is to write a class library with the various requirements and pass it a control as a parameter. I already have a Library of RegEx functions for things like isValidZipCodeand such so that may be a place for me to start.
我看到大多数相关帖子都涉及 Web Apps 和/或提到Enterprise Library Validation Application Block。现在我承认我还没有彻底研究过 ELVAB,但这对于我需要的东西来说似乎有点过分了。我目前的想法是编写一个具有各种要求的类库,并将其作为参数传递给它。我已经有一个 RegEx 函数库,用于诸如isValidZipCode 之类的东西,因此这可能是我开始的地方。
What I would like to have is a Validate button that onClick cycles through all the controls on that Form Page and performs the needed validation. How can I accomplish this?
我想要的是一个验证按钮,它 onClick 循环浏览该表单页面上的所有控件并执行所需的验证。我怎样才能做到这一点?
采纳答案by RS Conley
In my own application I need to validate dimensions as they are typed in. The sequence I used is as follows
在我自己的应用程序中,我需要在输入时验证尺寸。我使用的顺序如下
- The user selects or types then moves away from the control.
- The control loses focus and notifies the View sending it's ID and the entry text.
- The View checks what Shape Program (a class implementing a interface) created the Form and passes it the ID and entry text
- The Shape Program returns a response.
- If the Response is OK the View updates correct Entry of the Shape Class.
- If the Response is OK the View tells the Form through a Interface that it is OK to shift the focus to the next entry.
- If the Response is not OK, the View looks at the response and using the Form Interface tells the form what to do. This usually means the focus shifts back to the offending entry with a message displayed telling the user what happened.
- 用户选择或键入然后离开控件。
- 控件失去焦点并通知视图发送它的 ID 和条目文本。
- 视图检查什么形状程序(一个实现接口的类)创建了表单并将 ID 和条目文本传递给它
- 形状程序返回响应。
- 如果响应正常,则视图更新形状类的正确条目。
- 如果响应正常,则视图通过接口告诉表单可以将焦点转移到下一个条目。
- 如果响应不正确,视图会查看响应并使用表单界面告诉表单该做什么。这通常意味着焦点会转移回有问题的条目,并显示一条消息告诉用户发生了什么。
The advantage of this approach that validation is centralized in one location for a given Shape Program. I don't have to go modify each control or even really worry about the different types of controls on the form. Way back when I designed the software I decided how the UI going to work for textboxes, listboxes, combo boxes, etc. Also different levels of severity is handled differently.
这种方法的优势在于,对于给定的形状程序,验证集中在一个位置。我不必去修改每个控件,甚至不必担心表单上不同类型的控件。早在我设计软件时,我就决定了 UI 将如何用于文本框、列表框、组合框等。此外,不同严重程度的处理方式也不同。
The View takes care of that instructing the Form what to do through the Interface. How it actually is implemented is handled by the Form itself in it's implementation of the Interface. The View doesn't care if the Form is displaying yellow for warning and red for error. Only that it handles those two levels. Later if a better idea of displaying warning vs errors comes along I can make the change in the Form itself rather mucking around with the View logic or the validate in Shape Program.
视图负责指示表单通过接口做什么。它的实际实现方式由表单本身在接口的实现中处理。视图不关心表单是否显示黄色警告和红色错误。只是它处理这两个级别。稍后,如果出现显示警告与错误的更好想法,我可以在表单本身中进行更改,而不是使用视图逻辑或形状程序中的验证。
You are already halfway there if you are considering making a class to hold your validation logic this will get you the rest of the way in your new design.
如果您正在考虑创建一个类来保存您的验证逻辑,那么您已经成功了一半,这将使您在新设计中完成剩下的工作。
回答by Jamie Ide
We've had good luck with the Noogen ValidationProvider. It's simple for simple cases (data type checks and required fields) and easy to add custom validation for more complex cases.
Noogen ValidationProvider祝我们好运。对于简单的情况(数据类型检查和必填字段)来说很简单,并且很容易为更复杂的情况添加自定义验证。
回答by Joel Coehoorn
I would like to not have to go control by control by form and create isValid etc per item.
我想不必通过表单进行控制并为每个项目创建 isValid 等。
As some level you will have to define what it means to be valid
for each control, unless all you care about is that the control has a value of some kind.
作为某个级别,您必须定义valid
每个控件的含义,除非您只关心控件是否具有某种值。
That said, there's an ErrorProvider componentyou can use that works pretty well.
也就是说,您可以使用一个运行良好的ErrorProvider 组件。
回答by Kenneth Cochran
Cycling through controls can work but it's error prone. I worked on a project that used that technique (granted it was a Delphi project not C#) and it did work as expected but it was very difficult to update if a control was added or changed. This may have been correctible. I'm not sure.
循环浏览控件可以工作,但容易出错。我参与了一个使用该技术的项目(假设它是 Delphi 项目而不是 C#),它确实按预期工作,但如果添加或更改控件,则很难更新。这可能是可以纠正的。我不知道。
Anyway it worked by creating a single event handler which was then attached to each control. The handler would then use RTTI to determine the type of the control. Then it would use the control's name property in a large select statement to find the validation code to run. If the validation failed, an error message was sent to the user and the control was given focus. To make things more complex, the form was divided into several tabs and the proper tab had to be visible for it's child control to get the focus.
无论如何,它通过创建一个单独的事件处理程序来工作,然后将其附加到每个控件。然后处理程序将使用 RTTI 来确定控件的类型。然后它会在一个大的 select 语句中使用控件的 name 属性来查找要运行的验证代码。如果验证失败,则会向用户发送错误消息并为控件提供焦点。为了让事情变得更复杂,表单被分成了几个选项卡,并且正确的选项卡必须是可见的,因为它的子控件才能获得焦点。
So that's my experience.
所以这就是我的经验。
I would much rather use a Passive View design pattern to remove all business rules from the form and push them into a Presenter class. Depending on the state of your form that may be more work than your willing to invest.
我更愿意使用被动视图设计模式从表单中删除所有业务规则并将它们推送到 Presenter 类中。取决于您的表格状态,这可能比您愿意投资的工作更多。
回答by Will Eddins
Just a rough idea:
只是一个粗略的想法:
void btnValidate_Click(object sender, EventArgs e)
{
foreach( Control c in this.Controls )
{
if( c is TextBox )
{
TextBox tbToValidate = (TextBox)c;
Validate(tbToValidate.Text);
}
}
}
You could stick the textboxes inside a panel and only loop through controls in there if you want to avoid looping through other controls.
如果您想避免循环通过其他控件,您可以将文本框粘贴在面板内,并且只循环访问其中的控件。
回答by danish
Why are you not using Validating event? You can have a single validating event and validate the controls there. There will be no need of using loops and each control will be validated as the data is entered.
为什么不使用验证事件?您可以有一个验证事件并验证那里的控件。无需使用循环,并且每个控件都将在输入数据时进行验证。
回答by Steven Evers
In all of my forms, I implement the isValidating event for the particular control in question and if the data doesn't validate I have an errorProvider on the form and I use its SetError(...) method to set the error to the control in question with relevant information as to why it's wrong.
在我的所有表单中,我为所讨论的特定控件实现了 isValidating 事件,如果数据没有验证,我在表单上有一个 errorProvider 并使用它的 SetError(...) 方法将错误设置为控件有问题的相关信息,为什么它是错误的。
edit> I should note that I generally use the mvc pattern when doing this, so the specific validation for that control/member of the model happens at the model, so the isValidating looks kinda like this:
编辑> 我应该注意,我通常在执行此操作时使用 mvc 模式,因此模型的该控件/成员的特定验证发生在模型中,因此 isValidating 看起来有点像这样:
private uicontrol_isValidating(...)
{
if(!m_Model.MemberNameIsValid())
{
errorProvider.SetError(...);
}
}
回答by danish
Either that way. Or you can have a single validating event associated with all or controls which need similar validations. This will remove the looping from the code. Say you have four textboxes which can have integer only. What you can do is have a single event for each of them. I am not having any IDE so code below is the best I can come up with.
无论是那种方式。或者,您可以将单个验证事件与需要类似验证的所有或控件相关联。这将从代码中删除循环。假设您有四个只能包含整数的文本框。您可以做的是为每个事件设置一个事件。我没有任何 IDE,所以下面的代码是我能想到的最好的。
this.textbox1.Validated += <ValidatedEvent>
this.textbox2.Validated += <ValidatedEvent>
this.textbox3.Validated += <ValidatedEvent>
this.textbox4.Validated += <ValidatedEvent>
In the event:
在事件中:
- Cast sender as textbox.
- Check if the value in the textbox is numeric.
- 将发件人转换为文本框。
- 检查文本框中的值是否为数字。
And so forth you have events lined up.
等等,你有事件排队。
Hope this helps.
希望这可以帮助。
回答by Matt Brunell
Validation is already built into the WinForms library.
验证已内置于 WinForms 库中。
Each Control
-derived object has two events named Validating
and Validated
. Also it has a property called CausesValidation
. When this is set to true (it is true by default) then the control participates in validation. Otherwise, it does not.
每个Control
派生对象都有两个名为Validating
和 的事件Validated
。它还有一个名为CausesValidation
. 当此设置为 true(默认为 true)时,控件将参与验证。否则,它不会。
Validation occurs as part of focus. When you focus off of a control, its validation events are fired. In fact the focus events are fired in a specific order. From MSDN:
验证作为焦点的一部分发生。当您不关注控件时,将触发其验证事件。事实上,焦点事件是按特定顺序触发的。从MSDN:
When you change the focus by using the keyboard (TAB, SHIFT+TAB, and so on), by calling the Select or SelectNextControl methods, or by setting the ContainerControl..::.ActiveControl property to the current form, focus events occur in the following order:
- Enter
- GotFocus
- Leave
- Validating
- Validated
- LostFocus
When you change the focus by using the mouse or by calling the Focus method, focus events occur in the following order:
- Enter
- GotFocus
- LostFocus
- Leave
- Validating
- Validated
If the CausesValidation property is set to false, the Validating and Validated events are suppressed.
If the Cancel property of the CancelEventArgs is set to true in the Validating event delegate, all events that would usually occur after the Validating event are suppressed.
当您使用键盘(TAB、SHIFT+TAB 等)、通过调用 Select 或 SelectNextControl 方法或通过将 ContainerControl..::.ActiveControl 属性设置为当前窗体来更改焦点时,焦点事件发生在以下顺序:
- 进入
- 获得焦点
- 离开
- 证实
- 已验证
- 失去焦点
当您使用鼠标或通过调用 Focus 方法更改焦点时,焦点事件按以下顺序发生:
- 进入
- 获得焦点
- 失去焦点
- 离开
- 证实
- 已验证
如果 CausesValidation 属性设置为 false,则 Validating 和 Validated 事件将被抑制。
如果 CancelEventArgs 的 Cancel 属性在 Validating 事件委托中设置为 true,则通常会在 Validating 事件之后发生的所有事件都将被抑制。
Also a ContainerControl has a method called ValidateChildren()
which will loop through contained controls, and validate them.
此外,ContainerControl 有一个方法调用ValidateChildren()
,它将循环通过包含的控件,并验证它们。
回答by Bruce
I realize this thread is pretty old but I thought I'd post the solution I came up with.
我意识到这个线程已经很老了,但我想我会发布我想出的解决方案。
The biggest problem with validation on WinForms is the validation is only executed when the control has "lost focus". So the user has to actually click inside a text box then click somewhere else for the validation routine to execute. This is fine if your only concerned about the data that is entered being correct. But this doesn't work well if you're trying to make sure a user didn't leave a textbox empty by skipping over it.
WinForms 验证的最大问题是验证仅在控件“失去焦点”时执行。因此,用户必须实际在文本框内单击,然后单击其他位置才能执行验证例程。如果您只关心输入的数据是否正确,这很好。但是,如果您试图通过跳过文本框来确保用户没有将文本框留空,则这不起作用。
In my solution, when the user clicks the submit button for a form, I check each control on the form (or whatever container is specified) and use reflection to determine if a validating method is defined for the control. If it is, the validation method is executed. If any of the validations fail, the routine returns a failure and allows the process to stop. This solution works well especially if you have several forms to validate.
在我的解决方案中,当用户单击表单的提交按钮时,我检查表单上的每个控件(或指定的任何容器)并使用反射来确定是否为控件定义了验证方法。如果是,则执行验证方法。如果任何验证失败,例程将返回失败并允许过程停止。此解决方案效果很好,尤其是当您有多个表单需要验证时。
1) Just copy and paste this section of code to your project. We're using Reflection so you need to add System.Reflection to your using statements
1) 只需将这部分代码复制并粘贴到您的项目中即可。我们正在使用 Reflection,因此您需要将 System.Reflection 添加到您的 using 语句中
class Validation
{
public static bool hasValidationErrors(System.Windows.Forms.Control.ControlCollection controls)
{
bool hasError = false;
// Now we need to loop through the controls and deterime if any of them have errors
foreach (Control control in controls)
{
// check the control and see what it returns
bool validControl = IsValid(control);
// If it's not valid then set the flag and keep going. We want to get through all
// the validators so they will display on the screen if errorProviders were used.
if (!validControl)
hasError = true;
// If its a container control then it may have children that need to be checked
if (control.HasChildren)
{
if (hasValidationErrors(control.Controls))
hasError = true;
}
}
return hasError;
}
// Here, let's determine if the control has a validating method attached to it
// and if it does, let's execute it and return the result
private static bool IsValid(object eventSource)
{
string name = "EventValidating";
Type targetType = eventSource.GetType();
do
{
FieldInfo[] fields = targetType.GetFields(
BindingFlags.Static |
BindingFlags.Instance |
BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
if (field.Name == name)
{
EventHandlerList eventHandlers = ((EventHandlerList)(eventSource.GetType().GetProperty("Events",
(BindingFlags.FlattenHierarchy |
(BindingFlags.NonPublic | BindingFlags.Instance))).GetValue(eventSource, null)));
Delegate d = eventHandlers[field.GetValue(eventSource)];
if ((!(d == null)))
{
Delegate[] subscribers = d.GetInvocationList();
// ok we found the validation event, let's get the event method and call it
foreach (Delegate d1 in subscribers)
{
// create the parameters
object sender = eventSource;
CancelEventArgs eventArgs = new CancelEventArgs();
eventArgs.Cancel = false;
object[] parameters = new object[2];
parameters[0] = sender;
parameters[1] = eventArgs;
// call the method
d1.DynamicInvoke(parameters);
// if the validation failed we need to return that failure
if (eventArgs.Cancel)
return false;
else
return true;
}
}
}
}
targetType = targetType.BaseType;
} while (targetType != null);
return true;
}
}
2) Use the standard Validating event on any control you want to validate. Be Sure to use e.Cancel when the validation fails!
2) 在要验证的任何控件上使用标准 Validating 事件。 验证失败时一定要使用e.Cancel!
private void txtLastName_Validating(object sender, CancelEventArgs e)
{
if (txtLastName.Text.Trim() == String.Empty)
{
errorProvider1.SetError(txtLastName, "Last Name is Required");
e.Cancel = true;
}
else
errorProvider1.SetError(txtLastName, "");
}
3) Don't skip this step!Set the AutoValidateproperty on the form to EnableAllowFocusChange. This will allow tabbing to another control even when the validation fails.
3)不要跳过这一步!将表单上的AutoValidate属性设置为EnableAllowFocusChange。即使验证失败,这也允许跳转到另一个控件。
4) Finally, in your Submit Button method, call the Validation method and specify what container you want to check. It can be the whole form, or just a container on the form like a Panel or a Group.
4) 最后,在您的 Submit Button 方法中,调用 Validation 方法并指定您要检查的容器。它可以是整个表单,也可以只是表单上的一个容器,如面板或组。
private void btnSubmit_Click(object sender, EventArgs e)
{
// the controls collection can be the whole form or just a panel or group
if (Validation.hasValidationErrors(frmMain.Controls))
return;
// if we get here the validation passed
this.close();
}
Happy Coding!
快乐编码!