如何在Windows窗体应用程序中实现控件验证?

时间:2011-03-25 16:36:26

标签: c# winforms events

我正在尝试更好地了解验证在Windows窗体应用程序中的工作原理。互联网上充满了琐碎的例子,但我找不到一个解释控制验证的非平凡的例子。无论如何,感谢SwDevMan81Hans Passant我从一个比昨天更好的地方开始。

“真正的应用程序”有一个包含许多TextBox控件的对话框。每个控件都实现Validating事件。正如您在示例中所看到的,ValidateChildren作为Click事件的结果被调用,导致Validating事件被发送到每个控件。该应用还使用ErrorProvider控件来向用户提供反馈。昨天,我不明白如何使用Ok按钮Click事件来执行此验证。今天,我的对话框按预期工作。单击“确定”按钮会导致ErrorProvider执行控制无效且对话框未意外关闭的操作。

因此,虽然这似乎有效,但我仍然觉得我“在线外”。在Windows窗体应用程序中是否有用于控制验证的“最佳实践”文档/站点?

在许多令我困惑的事情中,当Ok按钮DialogResult属性设置为返回DialogResult.OK时,我无法找到对话框行为的解释。为什么设置此属性会干扰验证? (尝试使用和不使用该行的示例,看看我的意思。)

我昨天遇到的问题(看起来似乎)主要源于不了解ValidateChildren方法以及我将Ok按钮DialogResult属性设置为DialogResult.OK。将此属性设置为DialogResult.None似乎更改了Form类的一些自动行为。

TIA

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace ConsoleApp
{
    class Program
    {
        static void Main( string[] args )
        {
            Dialog dialog = new Dialog();

            if( dialog.ShowDialog() == DialogResult.OK )
                Console.Beep();
        }
    }

    public class Dialog : Form
    {
        TextBox m_TextBox0;
        TextBox m_TextBox1; // not validated
        TextBox m_TextBox2;

        Button m_OkBtn;
        Button m_CancelBtn;

        ErrorProvider m_ErrorProvider;

        public Dialog()
        {
            m_TextBox0 = CreateTextBox( 0, "TextBox 0" );
            m_TextBox1 = CreateTextBox( 1, "TextBox 1" );
            m_TextBox2 = CreateTextBox( 2, "TextBox 2" );

            m_OkBtn     = CreateButton( 3, "Ok" );
            m_CancelBtn = CreateButton( 4, "Cancel" );

            m_ErrorProvider = new ErrorProvider( this );

            //m_BtnOk.DialogResult = DialogResult.OK;
            m_OkBtn.Click += new EventHandler( BtnOk_Click );
            m_OkBtn.CausesValidation = true;

            m_CancelBtn.DialogResult = DialogResult.Cancel;
            m_CancelBtn.CausesValidation = false;
        }

        void BtnOk_Click( object sender, EventArgs e )
        {
            if( ValidateChildren() )
            {
                DialogResult = DialogResult.OK;
                Close();
            }
        }

        void TextBox_Validating( object sender, CancelEventArgs e )
        {
            m_ErrorProvider.Clear();

            TextBox textBox = sender as TextBox;

            // m_TextBox1 is always valid, the others are valid if they have text.
            bool valid = textBox.TabIndex == 1 || textBox.Text.Length > 0;

            if( !valid )
                m_ErrorProvider.SetError( textBox, "Error " + textBox.Name );

            e.Cancel = !valid;
        }

        Button CreateButton( int index, string name )
        {
            Button button = new Button();

            button.TabIndex = index;
            button.Text = name;
            button.Location = new System.Drawing.Point( 0, index * 30 );

            Controls.Add( button );

            return button;
        }

        TextBox CreateTextBox( int index, string name )
        {
            Label label = new Label();
            label.Text = name;
            label.Location = new System.Drawing.Point( 0, index * 30 );

            TextBox textBox = new TextBox();

            textBox.TabIndex = index;
            textBox.CausesValidation = true;
            textBox.Validating += new CancelEventHandler( TextBox_Validating );
            textBox.Location = new System.Drawing.Point( 100, index * 30 );

            Controls.Add( label );
            Controls.Add( textBox );

            return textBox;
        }
    }
}

编辑:这是最终的解决方案。我认为它很容易使用,同时也满足所有其他要求。我提前道歉这个问题到底有多长时间了。如果我可以向您展示所有真正的应用程序,那么为什么这么重要会更有意义。无论如何,感谢帮助这位老狗学习新技巧。

答案是为每个需要验证的控件创建一个ErrorProvider(相对于整个对话框的一个ErrorProvider。之后,一切都非常简单。

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace ConsoleApp
{
    class Program
    {
        static void Main( string[] args )
        {
            Dialog dialog = new Dialog();

            if( dialog.ShowDialog() == DialogResult.OK )
                Console.Beep();
        }
    }

    public class CompositeControl
    {
        Label         m_Label;
        TextBox       m_TextBox;
        ErrorProvider m_ErrorProvider;

        Dialog m_Dialog;

        public CompositeControl( int index, string name, Dialog dialog )
        {
            m_Label = new Label();
            m_Label.Text = name;
            m_Label.Location = new System.Drawing.Point( 0, index * 30 );

            m_TextBox = new TextBox();

            m_TextBox.TabIndex = index;
            m_TextBox.CausesValidation = true;
            m_TextBox.Validating += new CancelEventHandler( TextBox_Validating );
            m_TextBox.Location = new System.Drawing.Point( 100, index * 30 );

            m_Dialog = dialog;

            m_ErrorProvider = new ErrorProvider( m_Dialog );

            m_Dialog.Controls.Add( m_Label );
            m_Dialog.Controls.Add( m_TextBox );
        }

        void TextBox_Validating( object sender, CancelEventArgs e )
        {
            TextBox textBox = sender as TextBox;

            if( !m_Dialog.IsClosing && textBox.Text.Length == 0 )
                return;

            // m_TextBox1 is always valid, the others are valid if they have text.
            bool valid = textBox.TabIndex == 1 || textBox.Text.Length > 0;

            if( !valid )
                m_ErrorProvider.SetError( textBox, "Error " + textBox.Name );
            else
                m_ErrorProvider.Clear();

            e.Cancel = !valid;
        }
    }

    public class Dialog : Form
    {
        CompositeControl m_CompositeControl0;
        CompositeControl m_CompositeControl1; // not validated
        CompositeControl m_CompositeControl2;

        Button m_OkBtn;
        Button m_CancelBtn;

        bool m_IsClosing = false;

        public Dialog()
        {
            m_CompositeControl0 = new CompositeControl( 0, "TextBox 0", this );
            m_CompositeControl1 = new CompositeControl( 1, "TextBox 1", this );
            m_CompositeControl2 = new CompositeControl( 2, "TextBox 2", this );

            m_OkBtn     = CreateButton( 3, "Ok" );
            m_CancelBtn = CreateButton( 4, "Cancel" );

            //m_BtnOk.DialogResult = DialogResult.OK;
            m_OkBtn.Click += new EventHandler( BtnOk_Click );
            m_OkBtn.CausesValidation = true;

            m_CancelBtn.DialogResult = DialogResult.Cancel;
            m_CancelBtn.CausesValidation = false;
        }

        void BtnOk_Click( object sender, EventArgs e )
        {
            m_IsClosing = true;

            if( ValidateChildren() )
            {
                DialogResult = DialogResult.OK;
                Close();
            }

            m_IsClosing = false;
        }

        Button CreateButton( int index, string name )
        {
            Button button = new Button();

            button.TabIndex = index;
            button.Text = name;
            button.Location = new System.Drawing.Point( 0, index * 30 );

            Controls.Add( button );

            return button;
        }

        public bool IsClosing { get { return m_IsClosing; } }
    }
}

这个问题是我昨天问过的one的后续问题。

2 个答案:

答案 0 :(得分:5)

分配DialogResult属性是关闭对话框的原因。它在设置为None时保持运行。您不需要Close()来电。调用ShowDialog()的代码获取您指定为返回值的DialogResult值。因此,它知道对话框是否已关闭,或者只是取消了。

另请注意,编写验证事件处理程序的方式不需要ValidateChildren()。您设置e.Cancel = true以防止用户离开文本框。这意味着当文本框被验证为正常时,她只能进入“确定”按钮。但是,您必须确保在显示对话框时首先选择具有验证的控件。

友好的对话框是指用户可以在控件之间自由选项并选择“简单”对话框。您现在需要两个验证,一个验证输入的值是否有效,另一个验证是否没有丢失值。您可以通过在Validation事件处理程序中接受空字符串来获得此结果。但Winforms不能很好地支持后者,你需要代码。

答案 1 :(得分:2)

我知道这有些晚了,但我会再向Hans回答一下。创建文本框事件验证并将m_ErrorProvider.Clear()移动到Validated事件。验证完成时(e.cancel == false),将触发验证的事件。所以你会有这样的事情:


void TextBox_Validating( object sender, CancelEventArgs e ) {
    TextBox textBox = sender as TextBox;

    bool valid = textBox.TabIndex == 1 || textBox.Text.Length > 0;

    if( !valid )
        m_ErrorProvider.SetError( textBox, "Error " + textBox.Name );

    e.Cancel = !valid;
}

private void TextBox_Validated(object sender, System.EventArgs e) {
    TextBox textBox = sender as TextBox;
    m_ErrorProvider.SetError(textBox, "");
}
相关问题