Quest'oggi mi sono scontrato con un problema che pareva di
banale soluzione ma che si è rivelato essere decisamente subdolo. L'obbiettivo
da raggiungere era di caricare le webparts di una pagina dinamicamente da un
database invece che inserirle nel markup come di consueto. A prima vista può
sembrare che il framework offra quanto necessario per compiere questo compito.
Il WebPartManager ad esempio espone il metodo AddWebPart() che sembra fatto
a posta per quello, ma dopo un po' di tentativi si intuisce che c'è qualcosa che
non va.
Il problema non è che il metodo in questione non funzioni, ma
anzi, che funziona troppo. Infatti il difetto che si evidenzia quasi subito è
che ad ogni postback le webpart vengono nuovamente aggiunte causando una
crescita smisurata e inarrestabile. Qualunque tentativo di gestire questo
comportamento infausto non ha i risultati sperati. A partire dal consueto
"IsPostBack", fino alla verifica controllo per controllo che non si stiano
inserendo duplicati tutti i tentativi falliscono meramente.
Il fatto è che, quando si aggiungono le webpart queste vengono
persistite nel database di personalizzazioni. Perciò la volta successiva che la
pagina viene costruita le personalizzazioni salvate causano la duplicazione
delle webpart. Naturalmente a questopunto verrà persistita anche la
duplicazione, con l'ovvio risultato che la volta successiva ci si ritrova con
una triplicazione... e così via.
L'unica soluzione a questo problema l'ho trovata spulciando tra
le sessioni della PDC'05. Il trucco sta nel creare un
WebPartManager derivato da quello consueto e gestire al suo interno l'aggiunta
delle webparts dinamiche nell'OnInit della pagina. In questo modo sarà possibile
sfruttare la classe WebPartManagerInternals esposta per mezzo della proprietà
Internals per settare le proprietà delle webpart aggiunte e farle per così dire
"rientrare" nel normale ciclo di vita delle webpart.
Ecco un breve spezzone di codice:
// metodo chiamato nel Page.OnInit()
private void AddExternalWebParts()
{
// legge le webpart dallo storage
List<WebPartData> parts =
WebPartProvider.GetWebParts(this.Page);
// cicla le webpart
foreach (WebPartData data in parts)
{
// istanzia lo usercontrol
Control uc =
this.Page.LoadControl(data.AscxVirtualPath);
// crea la webPart che incapsula lo usercontrol
GenericWebPart webPart =
WebPartManager.CreateWebPart(uc);
// setta le proprietà
Internals.SetZoneID(webPart, data.ZoneID);
Internals.SetZoneIndex(webPart, data.ZoneIndex);
Internals.SetIsShared(webPart, true);
// Aggiunge una WebPart statica
Internals.AddWebPart(webPart);
}
}
Nell'esempio che ho linkato, nel folder ExternalWebPart è presente
anche il codice per creare le connessioni tra webpart automaticamente. Ma questa
è un'altra storia.
powered by IMHO 1.3