Stamane ho fatto una scoperta che definirei
sconcertante. La semplice lettura della proprietà Controls, in alcuni WebControl
di ASP.NET causa notevoli malfunzionamenti al ciclo di vita della pagina. In
particolare stavo utilizzando il metodo di cui ho parlato in un post precedente, per
cercare dei controlli all'interno della gerarchia della pagina. Questa ricerca,
se eseguita all'interno dell'OnInit, fa si che alcuni controlli non manifestino
più gli eventi tipici. Ad esempio, un LinkButton non notificava più il "Click",
e una FormView non era più in grado di gestire il postback
correttamente.
Dopo una lunga estenuante ricerca, sono riuscito
ad individuare la riga che era origine del problema, e con mio stupore mi sono
reso contro che in quel punto non facevo altro che leggere la proprietà Controls
per enumerare i controlli figlio. C'è voluta una breve indagine on il Reflector
per arrivare ad una possibile spiegazione. La proprietà Controls non legge
semplicemente il valore di un field, ma si occupa anche di inizializzare la
collection nel caso in cui essa non sia già popolata; ecco il codice estratto da
reflector:
public virtual ControlCollection Controls
{
get
{
if ((this._occasionalFields == null) ||
(this._occasionalFields.Controls == null))
{
this.EnsureOccasionalFields();
this._occasionalFields.Controls =
this.CreateControlCollection();
}
return this._occasionalFields.Controls;
}
}
Come appare chiaro questo codice si occupa di istanziare una
ControlCollection di default qualora essa non sia già presente. A quanto pare
questa inizializzazione causa problemi se effettuata esternamente. Suppongo
che da qualche parte, all'interno del ciclo di vita di un chiamata, esista una
porzione di codice che verifica se tale proprietà è già inizializzata
e si comporta di conseguenza in modi del tutto diversi. In effetti, a ben
guardare la classe Control espone un metodo che compie questa verifica e che
torna utile per aggirare questo malfunzionamento. Si tratta di HasControls(), un
metodo che restituisce false se la proprietà non è inizializzata. Evidentemente
in Microsoft il problema ho avevano previsto, e quindi hanno fornito un metodo
per aggirarlo. Ecco quindi come dovrebbe essere modificato il codice
dell'esempio precedente:
public static T FindControlRecursive<T>(Control root, string id)
where T : Control
{
if (root.ID == id && root is T)
return root as T;
if (root.HasControls())
{
foreach (Control child in root.Controls)
{
T foundControl = FindControlRecursive<T>(child, id);
if (foundControl != null)
return foundControl;
}
}
return default(T);
}
In questo modo non si modificherà lo stato del controllo. Rimane
semplicemente da rilevare che si tratta di un comportamento decisamente subdolo,
che andrebbe affrontato e corretto. Probabilmente sollevare un'eccezione sarebbe
stato preferibile. A me è costato 3 ore di lavoro. Spero che a voi vada
meglio.