C#ErrorProvider想知道是否有任何活动

时间:2012-09-07 17:52:24

标签: c# winforms validation errorprovider

我想知道我的表单中是否有任何ErrorProvider处于活动状态。 能够找到这个可能有助于减少我的代码..

我确实在Counting ErrorProvider

找到了这个东西

但是,如果有人知道更好的方式......那么就这样了。

好的,基本上我有一个WinForm,它有很多TextBoxes 现在,当用户输入值时,我使用Validating来执行验证,如果它与Regex不匹配,我为该Control设置了ErrorProvider ON。类似地,如果用户将值更改为可接受的值,我为该控件切换ErrorProvider OFF ..

但是当点击SAVE时我不得不另外检查一下,如果用户没有听我的话,改变他应该的东西,仍然点击SAVE ...我不想让事情崩溃..

soo mm就像我可以说ErrorProviders不活动然后继续保存其他消息框说更改它。

[另一个问题]

Umm验证时仅在控制失去焦点时验证...当用户停止输入时我有点想要验证...我希望你明白我的意思

当用户在I [DO NOT]中输入他/她的名字时,就像电子邮件地址(文本框)一样,但是当用户完成输入时正在等待ErrorProvider消失(但它不是因为只有在控制失去焦点时才这样做)输入后2个奇秒可以进行验证吗?

9 个答案:

答案 0 :(得分:12)

不幸的是,ErrorProvider控件不提供此类功能。您最好使用您发布的链接中的自定义错误提供程序类。

否则,您可以创建一个方法,而不是SetError

int errorCount;
void SetError(Control c, string message)
{
    if (message == "")
        errorCount--;
    else
        errorCount++;
    errorProvider.SetError(c, message);
}

或者你可以为ErrorProvider类创建一个extension method来设置错误并在这些行上增加一个计数器。

最后但并非最不重要的是,您可以遍历所有控件。慢,但它有效:

bool IsValid()
{
    foreach (Control c in errorProvider1.ContainerControl.Controls)
        if (errorProvider1.GetError(c) != "")
            return false;
    return true;
}

修改

我为错误提供程序编写了一个快速扩展类:

public static class ErrorProviderExtensions
{
    private static int count;

    public static void SetErrorWithCount(this ErrorProvider ep, Control c, string message)
    {
        if (message == "")
        {
            if (ep.GetError(c) != "")
                count--;
        }
        else
            count++;

        ep.SetError(c, message);
    }

    public static bool HasErrors(this ErrorProvider ep)
    {
        return count != 0;
    }

    public static int GetErrorCount(this ErrorProvider ep)
    {
        return count;
    }
}

我没有对它进行过广泛的测试,因此您可能需要在SetError上调用ErrorProvider之前进行更多验证。

答案 1 :(得分:4)

我知道这是一个较旧的问题并且扩展正在工作,除非有人为同一个对象尝试两次SetErrorWithCount,计数计数两次。 所以,在这里,我带来了基于Netfangled扩展的更新扩展基础

public static class ErrorProviderExtensions
{
   private static int count;

   public static void SetErrorWithCount(this ErrorProvider ep, Control c, string message)
   {
       if (message == "")
       {   
          if (ep.GetError(c) != "")
             count--;
       }
       else
          if (ep.GetError(c) == "")
             count++;

       ep.SetError(c, message);
   }

   public static bool HasErrors(this ErrorProvider ep)
   {
       return count != 0;
   }

   public static int GetErrorCount(this ErrorProvider ep)
   {
       return count;
   }
}

答案 2 :(得分:2)

好吧让我使用更简单的方法: 目前您正在使用隐式验证方法...立即验证控件。

我认为您要在执行某些操作之前检查表单中的所有控件是否都已经过验证,因此请检查是否已验证所有子控件。通过使用显式验证方法

在您可以使用的每个控件的验证事件中: -

    Private Sub ProductIDTextBox_Validating(sender As System.Object, e As System.ComponentModel.CancelEventArgs) Handles ProductIDTextBox.Validating
    If ProductIDTextBox.Text = "" Then
        ErrorProvider1.SetError(ProductIDTextBox, "you have to enter text")
        e.Cancel = True

        Return

    End If
    ErrorProvider1.SetError(ProductIDTextBox, "")

End Sub

然后您可以通过以下方式检查所有控件: -

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    If ValidateChildren() Then
        MessageBox.Show("Validation succeeded!")
    Else
        MessageBox.Show("Validation failed.")
    End If
End Sub

希望这会有所帮助,因为我花了几个小时才找到合适的方法

答案 3 :(得分:0)

我有多个 Control元素(TextBox es)附加到相应的 ErrorProvider s

我试图找到一种方法 countAllErrors() ,甚至更好地找到 handleEachError()
这就是我想出来的:


在班级中:

internal TextBox email_textbox;
internal TextBox city_textbox;
internal TextBox address_textbox;
internal TextBox phone_textbox;
internal TextBox lastName_textbox;
internal TextBox firstName_textbox;
private ErrorProvider firstName_errPro;
private ErrorProvider lastName_errPro;
private ErrorProvider phone_errPro;
private ErrorProvider address_errPro;
private ErrorProvider city_errPro;
private ErrorProvider email_errPro;
internal Dictionary<ErrorProvider, Control> errors;

在Form的构造函数中:

errors = new Dictionary<ErrorProvider, Control>(6);
errors.Add( firstName_errPro ,firstName_textbox );
errors.Add( lastName_errPro  ,lastName_textbox  );
errors.Add( phone_errPro     ,phone_textbox     );
errors.Add( address_errPro   ,address_textbox   );
errors.Add( city_errPro      ,city_textbox      );
errors.Add( email_errPro     ,email_textbox     );

计算所有错误:

int countAllErrors()
{
    int numOfErrors = errors.Count<KeyValuePair<ErrorProvider, Control>>(ep => ep.Key.GetError(ep.Value) != "");
    return numOfErrors;
}

处理每个错误:

void handleEachError()
{

    foreach (KeyValuePair<ErrorProvider, Control> errPair in errors.Where(ep => ep.Key.GetError(ep.Value) != ""))
    {
        ErrorProvider   errorProvider   = errPair.Key;
        Control         control         = errPair.Value;
        string          errorStr        = errorProvider.GetError(control);

        // handle the error:
        // for example - show it's text in a MessageBox:
        MessageBox.Show(errorStr);
    }

}

lemme知道它是否有用..;)

答案 4 :(得分:0)

Some answers这里非常容易出错,因为它们在扩展方法中共享一个静态计数变量。不!

我的扩展方法使用我的Nuget包Overby.Extensions.Attachments来存储关联的控件以及ErrorProvider,以便计算错误数。

using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using Overby.Extensions.Attachments; // PM> Install-Package Overby.Extensions.Attachments

namespace MyApp
{
    public static class ErrorProviderExtensions
    {
        public static void TrackControl(this ErrorProvider ep, Control c)
        {
            var controls = ep.GetOrSetAttached(() => new HashSet<Control>()).Value;
            controls.Add(c);
        }

        public static void SetErrorWithTracking(this ErrorProvider ep, Control c, string error)
        {
            ep.TrackControl(c);          
            ep.SetError(c, error);
        }

        public static int GetErrorCount(this ErrorProvider ep)
        {
            var controls = ep.GetOrSetAttached(() => new HashSet<Control>()).Value;

            var errControls = from c in controls
                              let err = ep.GetError(c)
                              let hasErr = !string.IsNullOrEmpty(err)
                              where hasErr
                              select c;

            var errCount = errControls.Count();
            return errCount;
        }

        public static void ClearError(this ErrorProvider ep, Control c)
        {            
            ep.SetError(c, null);
        }
    }
}

答案 5 :(得分:0)

这似乎是合乎逻辑的事情,但遗憾的是它本身没有提供。

您可以像其他提到的那样扩展ErrorProvider,或者只是迭代其下的所有控件并查找错误,例如

bool IsValidationError(ErrorProvider errorProvider, Control.ControlCollection controlCollection)
{
    foreach(Control child in controlCollection)
    {
        // The child or one of its children has an error.
        if (!errorProvider.GetError(child).IsNullOrEmpty() || IsValidationError(errorProvider, child.Controls))
            return true;
    }

    return false;
}

您可以致电IsValidationError(errorProvider, errorProvider.ContainerControl.Controls),或传递更有限的控件集。

显然,您希望避免迭代大量的控件,但在很多情况下,这个简单的解决方案应该没问题。即使你确实有很多控件,你也可以使用PanelTabControlGroupBox来组合它们,这样你就可以很容易地避免所有的控件。

注意:这类似于https://stackoverflow.com/a/12327212/276648中描述的一种可能性,除了它同时查找null和empty,并且它递归地迭代可能的大子项。

答案 6 :(得分:0)

您也可以简单地创建一个继承的类。

public class TrackedErrorProvider : ErrorProvider
{
    public TrackedErrorProvider() : base() { }

    public TrackedErrorProvider(ContainerControl parentControl) : base(parentControl) { }

    public TrackedErrorProvider(IContainer container) : base(container) { }

    public int ErrorsCount { get; protected set; } = 0;

    public bool HasErrors
    {
        get { return ErrorsCount > 0; }
    }

    public new void SetError(Control control, string message)
    {
        //Check if there is already an error linked to the control
        bool errorExistsForControl = !string.IsNullOrEmpty(GetError(control));

        //If removing error from the control
        if (string.IsNullOrEmpty(message))
        {
            /* Decreases the counter only if:
            *   - an error already existed for the control
            *   - the counter is not 0
            */
            if (errorExistsForControl && ErrorsCount > 0) ErrorsCount--;
        }
        else //If setting error message to the control
        {
            //Increments the error counter only if an error wasn't set for the control (otherwise it is just replacing the error message)
            if (!errorExistsForControl) ErrorsCount++;
        }

        base.SetError(control, message);
    }

    public void RemoveError(Control control)
    {
        SetError(control, null);
    }
}

答案 7 :(得分:0)

我的方法是使用扩展方法,因此我可以简单地调用errorProvider.Valid()。如果正确实现,ErrorProvider实际上会引用其主控件(窗体),因此它应该在具有单个实例的所有窗体上工作。 ValidateChildren()似乎没有返回有用的值。 这就是我用的:

public static bool Valid(this ErrorProvider ep)
{
  ep.ContainerControl.ValidateChildren();
  return ep.ChildrenAreValid(ep.ContainerControl);
}

private static bool ChildrenAreValid(this ErrorProvider ep, Control control)
{
  if (!string.IsNullOrWhiteSpace(ep.GetError(control))) return false;
  foreach (Control c in control.Controls)
    if (!(ep.ChildrenAreValid(c))) return false;
  return true;
}

通常,我有一种启用/禁用保存按钮或类似功能的方法:

private bool VerifyIntegrity() => (btnSave.Enabled = errorProvider.Valid());

在输入事件中运行哪个。

答案 8 :(得分:0)

就我而言,我不是使用静态类,而是使用错误计数器的实例。

public class ErrorCounter
{
    private List<string> _propertiesError = new List<string>();
    private static ObjectIDGenerator _IDGenerator = new ObjectIDGenerator();

    public bool HasErrors
    {
        get => ErrorCount != 0;
    }

    public int ErrorCount
    {
        get => _propertiesError.Count;
    }

    /// <summary>
    /// Record object validation rule state.
    /// </summary>
    /// <param name="sender">"this" object reference must be passed into parameter each time SetError is called</param>
    /// <param name="message"></param>
    /// <param name="property"></param>
    /// <returns></returns>
    public string SetError(object sender, string property, string message)
    {
        string propertyUniqueID = GetPropertyUniqueID(sender, property);

        if (string.IsNullOrWhiteSpace(message))
        {
            if (_propertiesError.Exists(x => x == propertyUniqueID))
            {
                _propertiesError.Remove(propertyUniqueID);
            }
        }
        else
        {
            if (!_propertiesError.Exists(x => x == propertyUniqueID))
            {
                _propertiesError.Add(propertyUniqueID);
            }
        }

        return message;
    }

    private string GetPropertyUniqueID(object sender, string property)
    {
        bool dummyFirstTime;

        return property + "_" + _IDGenerator.GetId(sender, out dummyFirstTime);
    }
}

用法: 在您的主要ViewModel中声明

public class MainViewModel : ViewModelBase, IDataErrorInfo
...
private ErrorCounter _errorCounter = new ErrorCounter();
...
// Entry validation rules
public string Error => string.Empty;
public string this[string columnName]
{
    get
    {
        switch (columnName)
        {
            case nameof(myProperty_1):
                if (string.IsNullOrWhiteSpace(myProperty_1))
                    return _errorCounter.SetError(this, columnName, "Error 1");
                break;
            case nameof(myProperty_2):
                if (string.IsNullOrWhiteSpace(myProperty_2))
                    return _errorCounter.SetError(this, columnName, "Error 2");
                break;
            default:
                break;
        }

        return _errorCounter.SetError(this, columnName, string.Empty);
    }
}

ObjectIDGenerator与属性名称结合使用时,每个属性只能计数一次。 如果需要在另一个类的对象集合成员中使用_errorCounter的相同实例,请将其传递给另一个类的构造函数。

仅此而已:-)