tags:

views:

147

answers:

1

I have a component derived from ErrorProvider. I have a BaseForm and BaseUserControl that each have this custom ErrorProvider added in the designer. I have many nested BaseUserControls on a BaseForm and on other BaseUserControls, and I need to find all of the custom ErrorProviders on the nested controls from a top level form or user control in order to access special functionality.

How can I walk through all nested controls and find all the ErrorProvider components? For example:

List<ErrorProviderEx> customProviders = GetErrorProviders(myUserControl);

The closest I've come is a recursive method that loops through myUserControl.Controls and inspects each control's Control.Container.Components collection, but that leads to some crazy logic, having to look backward in the recursive call at a control's Container. I'm also trying to avoid using reflection.

+1  A: 

The components of a Form or UserControl aren't exposed in any collection. You'll have to do this yourself.

First, declare an interface that your UserControls will implement:

public interface IComponentList
{
  List<Component> Components
  {
    get;
  }
}

Next, in your UserControl, add the components to a list in the constructor, and expose this list through the interface property Components:

public partial class UserControl1 : UserControl, IComponentList
{
  private List<Component> _components = new List<Component>();

  public UserControl1()
  {
    InitializeComponent();

    _components.Add(errorProvider1);
  }

  List<Component> IComponentList.Components
  {
    get
    {
      return _components;
    }
  }
}

Now, the following function should get you a list of ErrorProviders on all UserControls that implement the IComponentList interface:

public List<ErrorProvider> GetErrorProviders(Control myControl)
{
  List<ErrorProvider> foundErrorProviders = new List<ErrorProvider>();
  GetErrorProviders(myControl, foundErrorProviders);
  return foundErrorProviders;
}

protected void GetErrorProviders(Control myControl, List<ErrorProvider> foundErrorProviders)
{
  if (foundErrorProviders == null)
  {
    throw new ArgumentNullException("foundErrorProviders");
  }
  if (myControl is IComponentList)
  {
    foreach (Component component in ((IComponentList) myControl).Components)
    {
      if (component is ErrorProvider)
      {
        foundErrorProviders.Add((ErrorProvider) component);
      }
    }
  }
  foreach (Control control in myControl.Controls)
  {
    GetErrorProviders(control, foundErrorProviders);
  }
}

Call the method like this:

GetErrorProviders(myUserControl);

Note: The Windows Forms Designer places all components in a variable called components in the .designer.cs file. You could choose to just add all components in the components collection to the _components list, but I read the following here:

[...] I've realized that the components member is actually used only when a component having a specific constructor is placed onto a Form. [...] If the component exposes a constructor with the specific signature Public Sub New(ByVal c As IContainer), then the components Form member is instantiated and passed to the component's constructor

...which is why I'm hesitating in utilizing the components collection, since it will not necessarily contain all your components. It will work with the ErrorProvider, though.

You might want to take a look at this forum post as well.

Bernhof
Thanks for the example. I started to go that route but didn't want the coupling. It looks like it's probably the best way to go, though. I also knew about the private components collection in the .Desinger.cs file, but didn't want to reflect into a private field for this.
Lee Gray