2009-04-28 20 views
27

He usado FindControl en el pasado, antes de .NET 2.0/3.0. Parece que ahora, por alguna razón, los ID de mis controles tienen un funky llamado asignado. Por ejemplo, asigné un id "cbSelect" a una casilla de verificación, pero FindControl no lo encuentra. Cuando veo el HTML, se le asignó ctl00_bodyPlaceHolder_ctl02_cbSelect.ASP.Net FindControl no funciona - ¿Cómo es posible?

No he encontrado un ejemplo de FindControl que mencione eso. De hecho, todo el mundo parece usar el control de encontrar como normal.

Entonces, ¿estoy haciendo algo mal? ¿Cambió .Net? ¿Alguien puede arrojar algo de luz sobre esto para mí, es realmente frustrante?

Respuesta

25

Probablemente esté utilizando una MasterPage o controles de usuario (ascx) y esta es la razón por la que los ID de los clientes cambian. Imagine que tiene un control en la página maestra con el mismo ID que uno en la página. Esto daría lugar a enfrentamientos. Los cambios de id. Aseguran que todas las propiedades de ClientID sean únicas en una página.

FindControl necesita atención especial cuando se trabaja con MasterPages. Eche un vistazo al ASP.NET 2.0 MasterPages and FindControl(). FindControl funciona dentro de un naming container. MastePage y la página son contenedores de nombres diferentes.

+9

La forma en que Microsoft implementó esto es una broma, debería funcionar. Oh, tienes una página maestra? ¿Qué hay de 500 páginas maestras anidadas? El método debería resolverlo y hacer lo necesario para encontrar el control, punto. –

+0

¿Sabes que estás usando "debería" y "Microsoft" en el mismo pensamiento? – user1566694

1

Cuando está renderizando el html, ASP.NET prefijará todos los ID de control con los ID de los contenedores de nombres (Controles de usuario, etc.) en una jerarquía que se remonta hasta el final del documento. Esto asegura que todos los ID sean únicos para los respaldos de los correos, etc.

Esto no afecta el uso de FindControl donde debe usar el ID en el marcado original.

8

que he tenido muy buena suerte de trabajar alrededor de este problema en la "mayoría" de los casos con un simple método de extensión

Puede llamarlo en el control de contenedor de nivel superior que considere mejor, incluida la página en sí, si desea escanear toda la jerarquía de control.

private static Control FindControlIterative(this Control control, string id) 
{ 
    Control ctl = control; 

    LinkedList<Control> controls = new LinkedList<Control>(); 

    while(ctl != null) 
    { 
    if(ctl.ID == id) 
    { 
     return ctl; 
    } 
    foreach(Control child in ctl.Controls) 
    { 
     if(child.ID == id) 
     { 
     return child; 
     } 
     if(child.HasControls()) 
     { 
     controls.AddLast(child); 
     } 
    } 
    ctl = controls.First.Value; 
    controls.Remove(ctl); 
    } 
    return null; 
} 
8

Puede escribir extensor para buscar cualquier control en la página utilizando recursividad. Esto podría estar en alguna clase Util/Helper.

public static Control FindAnyControl(this Page page, string controlId) 
    { 
     return FindControlRecursive(controlId, page.Form); 
    } 

    public static Control FindAnyControl(this UserControl control, string controlId) 
    { 
     return FindControlRecursive(controlId, control); 
    } 

    public static Control FindControlRecursive(string controlId, Control parent) 
    { 
     foreach (Control control in parent.Controls) 
     { 
      Control result = FindControlRecursive(controlId, control); 
      if (result != null) 
      { 
       return result; 
      } 
     } 
     return parent.FindControl(controlId); 
    } 
7

Durante la búsqueda de un control en una colección de controles, siempre utilizar el ID que ha asignado el control, no el que se ve en la entrada fuente render. Si FindControl() no encuentra el control que usted conoce, existe una buena posibilidad de que no esté buscando en la rama derecha de la jerarquía de control. Una función recursiva ha sido exitosa para mí.

Aquí está mi ejemplo de lo que yo uso para VB.NET 3.5:

Function FindControlRecursive(ByVal ctrl As Control, ByVal id As String) As Control 
    Dim c As Control = Nothing 

    If ctrl.ID = id Then 
     c = ctrl 
    Else 
     For Each childCtrl In ctrl.Controls 
      Dim resCtrl As Control = FindControlRecursive(childCtrl, id) 
      If resCtrl IsNot Nothing Then c = resCtrl 
     Next 
    End If 

    Return c 
End Function 

Aquí es un ejemplo de cómo iba a aplicar tópicamente esta función en mi clase de página base:

Dim form HtmlForm = CType(FindControlRecursive(Me, "Form"), HtmlForm) 
+1

+1 porque sin ver el suyo ya lo había implementado en C# con el mismo enfoque: 'public static System.Web.UI.Control FindControlRecursive (Control ctl, string id) {' if (ctl == null) return nulo; if (ctl.ID == id) return ctl; if (ctl.Controls.Count> 0) foreach (Control sc en ctl.Controls) { Control fnd = FindControlRecursive (sc, id); if (fnd! = Null) return fnd; } return null; } ' – gkakas

3

Este es el VB.Código NET que funcionó para mí:

<Extension()> _ 
Function FindChildControlById(ByVal controlToStartWith As Control, ByVal controlIdToFind As String) As Control 
    If controlToStartWith Is Nothing Then Return Nothing 
    If controlToStartWith.ID = controlIdToFind Then Return controlToStartWith 
    For Each childControl As Control In controlToStartWith.Controls 
     Dim resCtrl As Control = FindChildControlById(childControl, controlIdToFind) 
     If resCtrl IsNot Nothing Then Return resCtrl 
    Next childControl 
    Return Nothing 
End Function ' Function FindChildControlById(ByVal controlToStartWith As Control, ByVal controlIdToFind As String) As Control 

El crédito va a George por el código inicial de VB.NET. Solo lo modifiqué un poco, con 2 cambios funcionales: el mío no falla si/cuando nulo/Nada pasa como control de entrada, y el mío se implementa como una extensión. Mis otros 3 cambios menores no afectan la funcionalidad, pero para mí, fueron simplificaciones de código. Pero sé que es muy subjetivo.

Así que este método se puede utilizar con:

Dim c1 As Control = Page.FindChildControlById("aspControlID") 

Y si quieres convertirlo en una clase secundaria específica de un control, así:

Dim c1 As Control = Page.FindChildControlById("aspControlID") 
Dim c As HyperLink = TryCast(c1, HyperLink) 

Actualización: Mi función es ahora llamado 'FindChildControlById' (anteriormente era 'FindMiControl'). Me gustó más la sugerencia de SpeedNet.

+0

Esto es perfecto, mejor que las otras soluciones, ¡gracias! Cambié el nombre a FindChildControlByID(). – Speednet