Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Locate ErrorProvider on Windows Form at run-time

Tags:

c#

.net

winforms

I am making an extension method library to use in windows form applications. One of the methods I intend to create will make setting error states on input controls easier, e.g.

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

I am trying to figure out the LocateErrorProvider method. What I would like to do is check whether an ErrorProvider exists on my form and then only make use of it if it does exist.

ErrorProvider is a Component and not a Control, so I can't get to it via the form.Controls property. I have tried casting the parent form into a variety of objects, to no avail.

UPDATE: I have managed to get to the ErrorProvider using reflection using the following code:

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

Personally, I'm not too fond of using the hard-coded field name to get to the field. But in this case it seems to work just fine. Does anybody have a better way to achieve the same results?

like image 886
tobias86 Avatar asked Jan 19 '26 22:01

tobias86


2 Answers

So far, this seems to solve my problem:

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

Thanks to Hans & Cody for their excellent ideas.

like image 73
tobias86 Avatar answered Jan 22 '26 12:01

tobias86


This is what interfaces are designed to do. They enforce a class to implement behavior. The behavior you want here is for a form to have an ErrorProvider. So write an interface like this:

public interface IHasErrorProvider {
    ErrorProvider Provider { get; }
}

And have the forms with an error provider implement the interface:

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

Retrieving the error provider is now simple:

    private static ErrorProvider GetErrorProvider(Control control) {
        var impl = control.FindForm() as IHasErrorProvider;
        return impl != null ? impl.Provider : null;
    }
like image 40
Hans Passant Avatar answered Jan 22 '26 12:01

Hans Passant



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!