2 pagine in totale: <<Indietro 1 [2]
Mantenere lo stato tra i PostBack
Eppure questi controlli mantengono intatto il loro aspetto. Non lo fanno memorizzando la sorgente dati, ma piuttosto salvando nel ViewState le minime informazioni necessarie a ricreare la stessa struttura di controlli. Nel caso del Repeater, queste informazioni si limitano solo al numero di righe della sorgente dati. Ricreando lo stesso numero di righe sfruttando più volte il template, il control ricreerà i controlli che a sua volta si "rigeneranno" con le informazioni memorizzate nel ViewState. Ad esempio, se il nostro template crea un Label, la proprietà Text verrà salvata e mantenuta tra i PostBack.
Tenendo in considerazione questo aspetto, ridefiniamo CreateChildControls, che viene invocato qualora si accede alla proprietà Controls:
protected override void CreateChildControls()
{
this.Controls.Clear();
if (this.ViewState["_!ItemCount"] != null)
this.CreateControlHierarchy(false);
else
this.itemsArray = new ArrayList();
base.ClearChildViewState();
}Con _!ItemCount memorizzeremo il numero di record. Al primo richiamo della pagina questo valore non sarà presente, quindi non richiameremo CreateControlHierarchy. Quest'ultimo metodo si occupa di generare tutti i controlli in funzione o meno della sorgente presente e verrà invocato anche quando chiameremo il metodo DataBind. Vediamo cosa questo metodo deve fare:
- mantenere una collezione itemsArray per permettere a chi usa il controllo di accedere agli Items;
- memorizzare una collezione groupsIndex, per gli indici corrispondenti all'inizio di un nuovo gruppo;
- risolvere la sorgente dati o crearne una fittizia in funzione del numero di righe, per poter creare la tabella;
- sfogliare ogni record e usare ItemTemplate o alternatingItemTemplate per creare i controlli, GroupTemplate qualora il campo di gruppo sia diverso da quello precedente;
- memorizzare indici e numero di righe
Ed ecco l'implementazione di quanto detto:
protected override void CreateControlHierarchy(bool useDataSource)
{
IEnumerable enumerable = null;
int itemCount = -1;
// Svuoto l'array
if (this.itemsArray != null)
this.itemsArray.Clear();
else
this.itemsArray = new ArrayList();
// Indici dei gruppi
ArrayList groupsIndex = null;
// Vuol dire che devo ricostruire gli items
if (!useDataSource)
{
// Recupero numero di record e indici di gruppi
itemCount = (int)this.ViewState["_!ItemCount"];
groupsIndex = (ArrayList)this.ViewState["groupsIndex"];
if (itemCount != -1)
{
// Creo una finta sorgente enumerabile in funzione
// del numero di items
enumerable = createDummy(itemCount);
this.itemsArray.Capacity = itemCount;
}
}
else
{
// Risolvo i dati
enumerable = getResolvedDataSource(this.DataSource, this.DataMember);
if (enumerable is ICollection)
this.itemsArray.Capacity = ((ICollection)enumerable).Count;
}
if (groupsIndex == null)
groupsIndex = new ArrayList();
if (useDataSource)
groupsIndex.Clear();
if (enumerable != null)
{
ControlCollection controls = this.Controls;
int count = 0;
bool hasSeparator = this.SeparatorTemplate != null;
bool hasGroup = this.GroupTemplate != null;
itemCount = 0;
// Creo il template per l'header
if (this.HeaderTemplate != null)
this.CreateItem(-1, ListItemType.Header, useDataSource, null);
object lastGroup = null;
string dataGroupField = DataGroupField;
// Itero su ogni record sorgente
foreach (object obj in enumerable)
{
bool newGroup = false;
// Prelevo il gruppo attuale
if (useDataSource && dataGroupField.Length > 0)
{
object cGroup = DataBinder.GetPropertyValue(obj, dataGroupField);
// Guardo se è diverso da quello precedente
newGroup = !cGroup.Equals(lastGroup);
// Salvo l'ultimo gruppo
lastGroup = cGroup;
}
if (!useDataSource)
newGroup = groupsIndex.Contains(itemCount);
if (hasSeparator && (itemCount > 0) && !newGroup)
this.CreateItem(count - 1, ListItemType.Separator, useDataSource, null);
// Se è un nuovo gruppo lo creo
if (newGroup && hasGroup)
{
this.CreateGroupItem(count, useDataSource, obj);
// Azzero il contatore di gruppo per alternare gli stili di riga
count = 0;
groupsIndex.Add(itemCount);
}
// Creo il normale riga col ItemTemplate (o alternate a seconda di count)
ListItemType itemType = ((count % 2) == 0) ? ListItemType.Item : ListItemType.alternatingItem;
RepeaterItem item = this.CreateItem(count, itemType, useDataSource, obj);
this.itemsArray.Add(item);
itemCount++;
count++;
}
// Creo la riga di fine sorgente
if (this.FooterTemplate != null)
this.CreateItem(-1, ListItemType.Footer, useDataSource, null);
}
// Memorizzo numero di righe e indici
if (useDataSource)
{
this.ViewState["_!ItemCount"] = (enumerable != null) ? itemCount : -1;
this.ViewState["groupsIndex"] = groupsIndex;
}
}Il codice è commentato in ogni sua parte, quindi di facile comprensione. Da notare la presenza di useDataSource, per differenziare la creazione delle righe in funzione di una sorgente o meno e del metodo CreateItem, che mantiene l'implementazione originale sfruttando i templates con il metodo InstantiateIn.
Uso del controllo
Una volta compilato il tutto, per utilizzare il control dovremo creare una pagina di prova. Registriamo tramite la direttiva Register l'assembly, per poi dichiararlo nel codice:
<%@ Register TagPrefix="aspitalia" Assembly="GroupRepeater" Namespace="ASPItalia.com.GroupRepeater" %>
<aspitalia:GroupRepeater runat="server" id="list" DataGroupField="Group">
<HeaderTemplate>
Nome file<br/>
</HeaderTemplate>
<ItemTemplate>
<%#DataBinder.Eval(Container.DataItem, "FileName")%><br/>
</ItemTemplate>
<alternatingItemTemplate>
. <%#DataBinder.Eval(Container.DataItem, "FileName")%><br/>
</alternatingItemTemplate>
<GroupTemplate>
<b><%#DataBinder.Eval(Container.DataItem, "Group")%></b>
<hr/>
</GroupTemplate>
</aspitalia:GroupRepeater>Conclusioni
L'uso del control nella pagina non è molto diverso dal normale Repeater. Abbiamo infatti aggiunto, come previsto, il nome del campo che identifica il gruppo tramite DataGroupField e definito GroupTemplate.
Come per tutti i controlli verificheremo comunque per la fase di produzione il peso della pagina e soprattutto del ViewState e cercheremo di trovare un compromesso tra comodità e carico della pagina.
Più si conosce il .NET Framework, più ci si rende conto di quanto è potente e quanto possa offrire. In questo esempio abbiamo ridefinito il comportamento di un controllo con poche righe di codice, inserendo una funzionalità che possiamo riutilizza facilmente anche per gli altri data controls.
Approfondimenti
- Databinding e templating con ASP.NET
- #680 - Costruire controls con template: un LoginView per ASP.NET 1.1
- Costruzione di custom web controls con supporto a design time
- Costruzione di custom controls con supporto a design time: Property Editor e Component Editor
- Custom Controls con supporto per l'Intellisense
2 pagine in totale: <<Indietro 1 [2]
Attenzione: Questo articolo contiene un allegato
Contenuti dell'articolo
Aggiungi un nuovo commento »»»
Per inserire un commento, devi registrarti alla nostra community.







Difficoltà
Utilità

Stampa
Download



