I'm trying to write an extension method for System.Web.UI.Control
which will search its ControlCollection
for an instance of specific Type
, and return the first instance found. Depth-first is simple, however I want to search breadth first so that higher collections take priority.
My current method is flawed and will exit early, returning null in certain cases where the entire search wasn't completed. I'm hoping I'm close to the right solution but need some fresh eyes on it. Any suggestions?
public static T FindFirstControlOfType<T>(this Control rootControl, bool searchRecursively) where T : Control
{
// list for container controls
List<Control> controlsWithChildren = new List<Control>();
// iterate the current control collection first
foreach (Control child in rootControl.Controls)
{
if (child.GetType().IsAssignableFrom(typeof(T)))
{
return (T)child;
}
// track those controls containing children
if (child.HasControls())
{
controlsWithChildren.Add(child);
}
}
// if recursion is enabled, search the child nodes
if (searchRecursively)
{
foreach (Control control in controlsWithChildren)
{
return FindFirstControlOfType<T>(control, true);
}
}
// if never found, return null
return null;
}
EDIT - Working Solution based on the marked answer, for anyone interested:
public static T FindFirstControlOfType<T>(this Control rootControl, bool searchNestedControls) where T : Control
{
Queue<Control> queue = new Queue<Control>();
EnqueueChildControls(queue, rootControl);
while (queue.Count > 0)
{
Control current = queue.Dequeue();
if (current.GetType() == typeof(T))
{
return (T)current;
}
if (searchNestedControls)
{
EnqueueChildControls(queue, current);
}
}
return null;
}
private static void EnqueueChildControls(Queue<Control> queue, Control control)
{
foreach (Control current in control.Controls)
{
queue.Enqueue(current);
}
}
Breadth first search (pseudo code)
There's no need for recursion for breadth first search.
Visit
is whatever you want to do with the current object. If you're searching for something thenVisit
would mean:if(current matches criteria) return current
Update: Working example: https://gist.github.com/1960108