在运行时定位Windows窗体上的ErrorProvider

3

我正在制作一个扩展方法库,用于在Windows窗体应用程序中使用。我打算创建的其中一个方法将使设置输入控件的错误状态变得更加容易,例如:

public static void SetError(this System.Windows.Forms.TextBox textBox, string errorMessage)
{
    if (string.IsNullOrEmpty(errorMessage))
    {
        //reset control state
        textBox.BackColor = System.Drawing.SystemColors.WindowText;
    }
    else
    {
        //set background colour to a nice shade of red
        textBox.BackColor = System.Drawing.Color.MistyRose;
    }

    //try to locate an ErrorProvider on the control's containing form.
    var errorProvider = LocateErrorProvider(textBox);

    if (errorProvider != null)
    {
        //set error message on error provider (or clear it)
        errorProvider.SetError(textBox, errorMessage);
    }
}

我正在尝试弄清楚LocateErrorProvider方法。我想做的是检查我的表单上是否存在一个ErrorProvider,如果存在则仅在其存在时使用它。

ErrorProvider是一个Component而不是Control,所以我不能通过form.Controls属性访问它。我已经尝试将父表单转换为各种对象,但均无效。

更新:我已经使用以下代码反射方式获得了ErrorProvider:

private static System.Windows.Forms.ErrorProvider GetErrorProvider(System.Windows.Forms.Control control)
{
    //get the containing form of the control
    var form = control.GetContainerControl();

    //use reflection to get to "components" field
    var componentField = form.GetType().GetField("components", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

    if (componentField != null)
    {
        //get the component collection from field
        var components = componentField.GetValue(form);

        //locate the ErrorProvider within the collection
        return (components as System.ComponentModel.IContainer).Components.OfType<System.Windows.Forms.ErrorProvider>().FirstOrDefault();
    }
    else
    {
        return null;
    }
}

个人而言,我不太喜欢使用硬编码字段名来访问字段。但在这种情况下,它似乎可以很好地解决问题。有没有更好的方法来实现相同的结果?


有一个“components”集合,它在“Dispose”方法的默认实现中使用。我不记得它是“private”还是“protected”,但这并不重要,因为您是从同一类的对象访问它。 - Cody Gray
@Cody Gray:谢谢,我已经尝试获取该字段,但这证明是一个挑战。它不是一个隐式绑定到System.Windows.Forms.Form的集合,而是由Visual Studio添加到Form类的设计时变量。我尝试使用反射来获取该字段,并成功“瞥见”了ErrorProvider,但我还没有真正“获取”它。 - tobias86
我已经通过反射成功访问到错误提供程序。如果有其他方法,请发表您的答案! - tobias86
3个回答

5

到目前为止,这似乎解决了我的问题:

private static System.Windows.Forms.ErrorProvider GetErrorProvider(System.Windows.Forms.Control control)
{
    //get the containing form of the control
    var form = control.GetContainerControl();

    //use reflection to get to "components" field
    var componentField = form.GetType().GetField("components", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

    if (componentField != null)
    {
        //get the component collection from field
        var components = componentField.GetValue(form);

        //locate the ErrorProvider within the collection
        return (components as System.ComponentModel.IContainer).Components.OfType<System.Windows.Forms.ErrorProvider>().FirstOrDefault();
    }
    else
    {
        return null;
    }
}

感谢Hans和Cody提出的精彩想法。

1

这就是接口设计的目的。它们强制一个类实现某种行为。在这里你所需要的行为就是一个表单拥有一个 ErrorProvider。因此,可以编写如下的接口:

public interface IHasErrorProvider {
    ErrorProvider Provider { get; }
}

并让带有错误提供程序的表单实现该接口:

public partial class Form1 : Form, IHasErrorProvider {
    public ErrorProvider Provider {
        get { return errorProvider1; }
    }
    // etc..
}

现在获取错误提供程序非常简单:

    private static ErrorProvider GetErrorProvider(Control control) {
        var impl = control.FindForm() as IHasErrorProvider;
        return impl != null ? impl.Provider : null;
    }

我喜欢这个想法,唯一的问题是我不能总是控制表单本身。不过还是谢谢! - tobias86
2
如果您无法控制表单,则也无法控制其是否具有ErrorProvider。结果是完全相同的,您将得到一个null。 - Hans Passant
同意,这就是为什么我只在有ErrorProvider的情况下使用它,但我不希望用户在表单上改变任何东西;我的方法需要自己找出来。谢谢。 - tobias86

1
在VS2005中,这个可以工作:

    private static System.Windows.Forms.ErrorProvider GetErrorProvider(System.Windows.Forms.Control control)
    {
        try
        {
            //get the containing form of the control
            System.Windows.Forms.IContainerControl form = control.GetContainerControl();

            //use reflection to get to "components" field
            System.Reflection.FieldInfo componentField = form.GetType().GetField("components", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            if (componentField != null)
            {
                //get the component collection from field
                object components = componentField.GetValue(form);
                object oReturn = null;
                //locate the ErrorProvider within the collection
                foreach (object o in ((System.ComponentModel.Container)components).Components)
                {
                    if (o.GetType() == typeof(System.Windows.Forms.ErrorProvider))
                    {
                        oReturn = o;
                        break;
                    }
                }
                return (ErrorProvider)oReturn;
            }
        }
        catch 
        {
            return null;
        }
        return null;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接