5

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

我确实在这里找到了这个东西Counting ErrorProvider

但万一有人知道更好的方法......所以就这样吧。

好的,所以基本上我有一个 WinForm,它有很多 TextBox 现在,当用户输入值时,我使用 Validating 来执行验证,如果它与 Regex 不匹配,我将 ErrorProvider 设置为该控件的 ON .. 同样,如果用户将值更改为可接受的值我为该控件关闭 ErrorProvider ..

但是当单击“保存”时,无论如何我都必须进行另一次检查,以防用户不听我的声音并像他应该做的那样更改内容并仍然单击“保存”..我不希望事情崩溃..

soo mm 有没有像我可以说如果 ErrorProviders 未激活然后继续保存 else 消息框说更改它的事情。

[另一个问题]

嗯,验证时仅在控件失去焦点时验证...我有点希望它在用户停止输入时进行验证..我希望你明白我的意思

就像用户输入他/她的名字时的电子邮件地址(文本框)一样当控制失去焦点时)键入后 2 奇数秒我可以进行验证吗?

4

9 回答 9

15

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

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

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

或者,您可以为 ErrorProvider 类创建一个扩展方法,该方法将设置错误并增加一个计数器或类似的东西。

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

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.

于 2012-09-08T01:06:31.267 回答
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;
   }
}
于 2013-08-12T07:59:32.167 回答
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

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

于 2014-10-26T18:16:46.523 回答
1

拥有它似乎是一件合乎逻辑的事情,但不幸的是它并没有原生提供。

您可以像其他提到的那样扩展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),或者传递一个更有限的控件集合。

显然,您希望避免迭代大量控件,但这种简单的解决方案在很多情况下应该没问题。此外,即使您确实有大量控件,您也可能使用Panel, TabControl, GroupBox, ... 将它们组合在一起,这样您就可以轻松地避免绝对迭代所有控件。

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

于 2017-03-29T03:25:16.987 回答
0

我有多个 Control元素(TextBoxes)附加到它们对应 ErrorProvider的 s上。

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


在课堂里:

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;

在表单的构造函数中:

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);
    }

}

让我知道它是否有帮助.. ;)

于 2014-07-07T14:46:14.783 回答
0

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

我的扩展方法使用我的 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);
        }
    }
}
于 2016-10-27T15:09:26.440 回答
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);
    }
}
于 2017-04-06T11:21:54.553 回答
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());

在输入事件中运行。

于 2018-07-31T09:46:46.090 回答
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);
    }
}

用法:在主视图模型中声明

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 实例,请将其传递给另一个类的构造函数。

就这样 :-)

于 2020-02-19T16:34:59.283 回答