Para la posteridad, aquí es la función que terminé la escritura. Funciona muy bien (probado en un proyecto real):
public static Control PreviousControl(this Control control)
{
ControlCollection siblings = control.Parent.Controls;
for (int i = siblings.IndexOf(control) - 1; i >= 0; i--)
{
if (siblings[i].GetType() != typeof(LiteralControl) && siblings[i].GetType().BaseType != typeof(LiteralControl))
{
return siblings[i];
}
}
return null;
}
Para utilizarse como esto:
Control panel = textBox.PreviousControl();
y para el control siguiente:
public static Control NextControl(this Control control)
{
ControlCollection siblings = control.Parent.Controls;
for (int i = siblings.IndexOf(control) + 1; i < siblings.Count; i++)
{
if (siblings[i].GetType() != typeof(LiteralControl) && siblings[i].GetType().BaseType != typeof(LiteralControl))
{
return siblings[i];
}
}
return null;
}
La ventaja de esta solución sobre que de Atzoya es que, primero, no necesita el control original para tener una ID ya que hago la búsqueda en función de la instancia. En segundo lugar, debe saber que ASP.net genera varios controles Literales para renderizar su HTML estático entre sus controles "reales". Es por eso que los omito, o seguirás combinando basura. Por supuesto, la desventaja de esto es que no puedes encontrar un control si es un Literal. Esta limitación no fue un problema en mi uso.
Gracias por su sugerencia, pero tiene 2 números: en primer lugar se supone que el control original debe tener un ID, que no es bueno en mi caso. En segundo lugar, su función va a coincidir con los controles Literales que ASP.net genera para generar su HTML estático. Eso es un no ir. Vea mi propia solución para una versión que aborde todo eso. – md1337