7

Having a heckuva time with this one, though I feel I'm missing something obvious. I have a control that inherits from System.Web.UI.WebControls.Button, and then implements an interface that I have set up. So think...

public class Button : System.Web.UI.WebControls.Button, IMyButtonInterface { ... }

In the codebehind of a page, I'd like to find all instances of this button from the ASPX. Because I don't really know what the type is going to be, just the interface it implements, that's all I have to go on when looping through the control tree. Thing is, I've never had to determine if an object uses an interface versus just testing its type. How can I loop through the control tree and yank anything that implements IMyButtonInterface in a clean way (Linq would be fine)?

Again, know it's something obvious, but just now started using interfaces heavily and I can't seem to focus my Google results enough to figure it out :)

Edit: GetType() returns the actual class, but doesn't return the interface, so I can't test on that (e.g., it'd return "MyNamespace.Button" instead of "IMyButtonInterface"). In trying to use "as" or "is" in a recursive function, the type parameter doesn't even get recognized within the function! It's rather bizarre. So

if(ctrl.GetType() == typeToFind) //ok

if(ctrl is typeToFind) //typeToFind isn't recognized! eh?

Definitely scratching my head over this one.

chicks
  • 2,393
  • 3
  • 24
  • 40
Chris
  • 4,030
  • 4
  • 47
  • 76

7 Answers7

7

Longhorn213 almost has the right answer, but as as Sean Chambers and bdukes say, you should use

ctrl is IInterfaceToFind

instead of

ctrl.GetType() == aTypeVariable  

The reason why is that if you use .GetType() you will get the true type of an object, not necessarily what it can also be cast to in its inheritance/Interface implementation chain. Also, .GetType() will never return an abstract type/interface since you can't new up an abstract type or interface. GetType() returns concrete types only.

The reason this doesn't work

if(ctrl is typeToFind)  

Is because the type of the variable typeToFind is actually System.RuntimeType, not the type you've set its value to. Example, if you set a string's value to "foo", its type is still string not "foo". I hope that makes sense. It's very easy to get confused when working with types. I'm chronically confused when working with them.

The most import thing to note about longhorn213's answer is that you have to use recursion or you may miss some of the controls on the page.

Although we have a working solution here, I too would love to see if there is a more succinct way to do this with LINQ.

chicks
  • 2,393
  • 3
  • 24
  • 40
Daniel Auger
  • 12,535
  • 5
  • 52
  • 73
5

You can just search on the Interface. This also uses recursion if the control has child controls, i.e. the button is in a panel.

private List<Control> FindControlsByType(ControlCollection controls, Type typeToFind)
{
    List<Control> foundList = new List<Control>();

    foreach (Control ctrl in this.Page.Controls)
    {
        if (ctrl.GetType() == typeToFind)
        {
            // Do whatever with interface
            foundList.Add(ctrl);
        }

        // Check if the Control has Child Controls and use Recursion
        // to keep checking them
        if (ctrl.HasControls())
        {
            // Call Function to 
            List<Control> childList = FindControlsByType(ctrl.Controls, typeToFind);

            foundList.AddRange(childList);
        }
    }

    return foundList;
}

// Pass it this way
FindControlsByType(Page.Controls, typeof(IYourInterface));
vrajs5
  • 4,066
  • 1
  • 27
  • 44
David Basarab
  • 72,212
  • 42
  • 129
  • 156
4

I'd make the following changes to Longhorn213's example to clean this up a bit:

private List<T> FindControlsByType<T>(ControlCollection controls )
{
    List<T> foundList = new List<T>();

    foreach (Control ctrl in this.Page.Controls)
    {
        if (ctrl as T != null )
        {
            // Do whatever with interface
            foundList.Add(ctrl as T);
        }

        // Check if the Control has Child Controls and use Recursion
        // to keep checking them
        if (ctrl.HasControls())
        {
            // Call Function to 
            List<T> childList = FindControlsByType<T>( ctrl.Controls );

            foundList.AddRange( childList );
        }
    }

    return foundList;
}

// Pass it this way
FindControlsByType<IYourInterface>( Page.Controls );

This way you get back a list of objects of the desired type that don't require another cast to use. I also made the required change to the "as" operator that the others pointed out.

Jason Diller
  • 3,360
  • 2
  • 24
  • 25
1

Interfaces are close enough to types that it should feel about the same. I'd use the as operator.

foreach (Control c in this.Page.Controls) {
    IMyButtonInterface myButton = c as IMyButtonInterface;
    if (myButton != null) {
        // do something
    }
}

You can also test using the is operator, depending on your need.

if (c is IMyButtonInterface) {
    ...
}
bdukes
  • 152,002
  • 23
  • 148
  • 175
1

Would the "is" operator work?

if (myControl is ISomeInterface)
{
  // do something
}
Sean Chambers
  • 8,572
  • 7
  • 41
  • 55
0

If you're going to do some work on it if it is of that type, then TryCast is what I'd use.

Dim c as IInterface = TryCast(obj, IInterface)
If c IsNot Nothing
    'do work
End if
hometoast
  • 11,522
  • 5
  • 41
  • 58
0

you can always just use the as cast:

c as IMyButtonInterface;

if (c != null)
{
   // c is an IMyButtonInterface
}
FlySwat
  • 172,459
  • 74
  • 246
  • 311