Building A Better FindControl
I’ve come across a lot of times when I wish I could just use the standard FindControl method on a Page or on a container control and have it be found without having to make sure that the control was a direct child of my current container. Also, I wish I could find all controls of a particular type on a page. Why couldn’t the Control class be given these gems out of the box? Well, thanks to extension methods, custom iterators and a dash of linq, I’ve built my own Control Finding mechanism that gives a broader search and adds some type safety to the mix.
FindControl 101
Let’s look at our basic implementation. Finding controls is a recursive process by nature, but any recursive algorithm can be turned into an iterative (and thus more performant) one, so for our first pass, let’s just implement the FindControl method as it is now but with a deeper searching algorithm:
In the most basic sense, that’s all we have to do. We can now add this to a helpers class and call it statically. But there’s so much more that can be done…
Pour Some Syntactic Sugar On Me
Wouldn’t it be nice if I could call my new method from ANY control as if Microsoft had actually thought I might want this much better working version of its FindControl method when they were building the Control class? well, extension methods allow me to fake it. This will require that I make a static container class for my new method and change the method signature a bit.. let’s take a look at our improved version:
Not much of a difference in code, but a huge difference in how I can use it. Now, instead of having to invoke our find this way:
We can now invoke it like this:
The key is the word this
in the signature of our method. It tells the compiler that any object that inherits from Control will be able to invoke this method as if it was a part of the class. Of course it isn’t really adding the new method to the class, but it makes it MUCH easier to get to our methods. I’m still not satisfied, because I’m looking for a textbox and I’m getting back a control that might be a textbox (or might not). I’d like not to have to cast the control when it comes back.
Generically Speaking…
We can now change our AdvancedFindControl to return any control we want by using generics… the new version is below:
Now, let’s say I want that textbox named TextBox1
;. I can call it this way:
Have It Your Way
Ok, now for the grand finale. It’s nice to be able to find a typed control by ID, but sometimes you want to find a control by more than one criteria. And, for that matter, what if I want to find all the controls that match a particular set of criteria. We can easily modify our function to do this with custom iterators and a bit of linq to make it fancy. Our final set of functions is below:
This final set of functions moves all the heavy lifting into a private method called DoTheWork, which now returns an IEnumerable of our searched control type. This is enabled by replacing our old return with yield return, a .NET 2.0 feature. Our original static extension method calls this private method and I renamed it AdvancedFindControls so that it’s clear it returns more than one. I also created another extension method called AdvancedFindControl which also makes use of the private method and then uses some linq filtering (in this case the FirstOrDefault() extension method) to give us only the first. You could easily write additional methods to give you the last, middle or even every other result of your search with similar functions available on the IEnumerable
So as you can plainly see the features of extension methods, custom iterators and linq can be combined to give us a significantly better way to search through controls.