Costruire un Repeater a gruppi

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

2 pagine in totale: <<Indietro 1 [2]

Attenzione: Questo articolo contiene un allegato

Contenuti dell'articolo

Commenti
Dai un voto a questo articolo, ci aiuterà a migliorare il nostro sito (1 è il voto minimo, 5 il massimo).

Per procedere al rating dell'articolo devi essere autenticato.

Aggiungi un nuovo commento »»»
Per inserire un commento, devi registrarti alla nostra community.


TUTORIALS
TOP TEN ARTICOLI
NOTIFICHE

Iscriviti alla nostra newsletter nuoviarticoli per ricevere e-mail le notifiche!

Indirizzo e-mail:
PROVIDER ASP.NET 2.0

Seleziona il database per avere il web.config pronto per Membership, Roles e Profile API.



IN EVIDENZA
MISC