
Editorial illustration.
Originally published 22 June 2023; rewritten and reviewed on 15 October 2026.
A page contains required validators inside several user controls. Looking only at the first level of Page.Controls misses some of them, and the browser IDs do not always match their server IDs. The problem is easier to reason about when the control tree remains explicit.
Traverse nested controls deliberately
This legacy Web Forms helper collects the relevant controls rather than guessing their rendered identifiers:
static IEnumerable<RequiredFieldValidator> FindRequired(
Control parent)
{
foreach (Control child in parent.Controls)
{
if (child is RequiredFieldValidator validator)
yield return validator;
foreach (var nested in FindRequired(child))
yield return nested;
}
}
It requires the appropriate System.Web.UI, System.Web.UI.WebControls and collection namespaces. Start traversal after the controls you need have been created; dynamically added controls affect what the tree contains.
Choose the identifier for the consumer
Use ID for the server-side naming context and ClientID when a browser-side operation needs the rendered DOM identifier. Naming containers can change the latter. Keep the collected controls long enough to select the appropriate property rather than assuming the two are interchangeable.
Not every validator on the page belongs to the same action. Inspect validation groups and enabled state before applying group-specific logic.
Keep validation authoritative
Finding a validator does not run it. Trigger the relevant server validation and inspect Page.IsValid before processing the operation. Client-side convenience can be bypassed.
Test a nested user control, a dynamically created validator and two validation groups. The helper is useful when it reflects the actual page lifecycle and naming rules, not when it merely produces a list that happened to work on the simplest page.