Saturday, February 14, 2009

Fare un pò di spazio nel GridView

. Saturday, February 14, 2009

I clienti si sà non sono semplici da accontentare. Sempre più spesso mi capita di sentire per web application intranet che amministratori e non solo, devono poter visionare venti o più colonne in una griglia, nonostante la possibilità di generare report in Excel o la possibilità di utilizzare custom applicazioni sviluppate tramite VSTO. Come si può facilmente immaginare oltre alla dubbia utilità, c’è comunque un problema legato alla leggibilità dei dati.
E’ anche vero che è possibile trovare controlli, che tra le tante feature, permettono di ridimensionare a piacimento la dimensione delle righe/colonne oppure poter cambiare la posizione delle colonne all’interno della griglia ecc.
Qualche esempio:
RadGrid della Telerik
jqGrid
AJAX Grid della Ccomponentart
Ma volendo qualcosa si può facilmente creare con pochissimo sforzo.
Un’idea potrebbe essere quella di poter “rimuovere” la colonna desiderata, cliccando semplicemente sull’header di essa, per poi “rinserirla” tramite un apposito pulsante.


SchemaVisibilityGrid
Per semplicità il controllo GridView è composto da quattro TemplateColumn:

   1: <table cellpadding="2" cellspacing="2" width="100%">
   2:   <tr>
   3:     <td align="right">
   4:       <asp:Panel ID="pnlBox" runat="server">
   5:         <table>
   6:           <tr>
   7:             <td><img id="imgCollapse" src="img/plus.gif" alt="Collapse" style="cursor:pointer" 
onclick="modifiedImage()"/></td>
   8:           </tr>
   9:           <tr>
  10:             <td><asp:Panel ID="pnlColumns" runat="server" style="display:none"></asp:Panel></td>
  11:           </tr>
  12:         </table>
  13:       </asp:Panel>
  14:     </td>
  15:   </tr>
  16:   <tr>
  17:     <td>
  18:       <asp:GridView ID="dgrDelete" runat="server" AutoGenerateColumns="false" Width="100%" 
OnPreRender="dgrDelete_PreRender" CssClass="GridViewStyle">
  19:         <Columns>
  20:           <asp:TemplateField HeaderText="Col1">
  21:             <ItemTemplate>
  22:               <asp:Label ID="lblCol1" runat="server" Text='<%#Eval("Col1") %>'></asp:Label>
  23:             </ItemTemplate>
  24:           </asp:TemplateField>
  25:           <asp:TemplateField HeaderText="Col2">
  26:             <ItemTemplate>
  27:               <asp:Label ID="lblCol2" runat="server" Text='<%#Eval("Col2")%>'></asp:Label>
  28:             </ItemTemplate>
  29:           </asp:TemplateField>
  30:           <asp:TemplateField HeaderText="Col3">
  31:             <ItemTemplate>
  32:               <asp:Label ID="lblCol3" runat="server" Text='<%#Eval("Col3")%>'></asp:Label>
  33:             </ItemTemplate>
  34:           </asp:TemplateField>
  35:           <asp:TemplateField HeaderText="Col4">
  36:             <ItemTemplate>
  37:               <asp:Label ID="lblCol4" runat="server" Text='<%#Eval("Col4")%>'></asp:Label>
  38:             </ItemTemplate>
  39:           </asp:TemplateField>
  40:         </Columns>
  41:         <RowStyle CssClass="RowStyle" />
  42:         <HeaderStyle CssClass="HeaderStyle" />
  43:         <AlternatingRowStyle CssClass="AltRowStyle" />
  44:       </asp:GridView>
  45:     </td>
  46:   </tr>
  47: </table>

Gli elementi principali del markup appena visto sono il controllo GridView e un Panel. Quest’ultimo con id pnlColumns può essere minimizzato tramite l’immagine con id imgCollapse.
Sull’evento PreRender del controllo GridView aggiungo per ogni intestazione di colonna l’evento onclick e l’attributo id tramite la collection Attributes:

   1: protected void dgrDelete_PreRender(object sender, EventArgs e)
   2: {
   3:   if (dgrDelete.HeaderRow != null && dgrDelete.HeaderRow.Cells.Count > 0)
   4:   {
   5:     int count = 0;
   6:     foreach (TableCell cell in dgrDelete.HeaderRow.Cells)
   7:     {
   8:       cell.Attributes.Add("id", "th" + count.ToString());
   9:       cell.Attributes.Add("onclick", "removeColumns(" + count.ToString() + ")");
  10:      count += 1;
  11:     }
  12:   }
  13: }

L’id di ogni header di colonna è composto dalla concatenazione di due elementi: “th” e un indice di posizione. Sull’evento onclick viene invece, richiamata la funzione removeColumns passando come unico paramentro l’indice, che come abbiamo visto in precedenza compone parte dell’id dell’intestazione. In questo modo possiamo facilmente recuperare la colonna che si desidera “rimuovere”.

Non ci resta che dare uno sguardo alla parte, forse più interessante, riguardante JavaScript. Con l’aiuto del fido JQuery partiamo dall’analizzare la funzione removeColumns:

   1: //Rendo non visibile la colonna selezionata nella griglia
   2: function removeColumns(index) {
   3:   $("#dgrDelete tbody tr").each(function(n) {
   4:     this.cells[index].style.display = 'none';
   5:   });
   6:   var title = $("#th" + index + "").text();
   7:   $("#th" + index + "").css("display", "none");
   8:   addBookmarksColumn(title, index);
   9: }

Per ogni riga che compone la griglia (vedere riga 3) imposto per la cella avente l’indice passato come parametro alla funzione, la proprietà display a none, cioè viene resa invisibile (riga 4). Stessa sorte spetta anche alla cella di instestazione della colonna (riga 7), ma prima recupero il testo contenuto (riga 6). Tale testo insieme all’indice, costituiscono i parametri della funzione addBookmarksColumn.

   1: //Aggiunge al panel info sulla colonna rimossa dalla griglia
   2: function addBookmarksColumn(header, index) {
   3:   $('#pnlColumns').append("<div id=\"div" + header + "\">");
   4:   $('#pnlColumns').append("<span id=\"sp" + header + "\" >" + header + " </span>");
   5:   $('#pnlColumns').append("<img id=\"img" + header + "\" style=\"cursor:pointer\" 
src=\"img/add.gif\" onclick=\"addColumns("
+ index + ")\" />");
   6:   $('#pnlColumns').append("</div>");
   7: }

Al pannello pnlColumns viene aggiunto a runtime del codice HTML. In pratica ogni volta che viene “rimossa” una colonna, questo pannello si popola di un tag div composto a sua volta da un tag span contenente il testo dell’header, più un’immagine che consente all’utente di rendere nuovamente visibile la colonna all’interno della griglia.
(NB: Ogni elemento presenta l’attributo id composto in parte dal testo della cella di intestazione.)
Sull’onclick dell’immagine di add viene richiamata la funzione addColumns:

   1: //Rendo visibile la colonna selezionata nella griglia
   2: function addColumns(index) {
   3: $("#dgrDelete tbody tr").each(function(n) {
   4:   if ($.browser.msie)
   5:     this.cells[index].style.display = 'block';
   6:   else
   7:     this.cells[index].style.display = 'table-cell';
   8:   });
   9:   var title = $("#th" + index + "").text();
  10:   if ($.browser.msie)
  11:     $("#th" + index + "").css("display", "block");
  12:   else
  13:     $("#th" + index + "").css("display", "table-cell");
  14:   removeBookmarksColumn(title);
  15: }

Essenzialmente questa runtine si comporta in modo opposto alla funzione removeColumns, in quanto rende visibile la colonna.
La differenza sostanziale riguarda il valore assegnato alla proprietà display. Poichè non tutti i browser presentano la stessa compatibilità con questa proprietà, eseguo un controllo sul tipo di browser (riga 4 e 10).
In caso di browser come Chrome, Firefox, Opera utilizzo il valore table-cell per una corretta visualizzazione delle celle (riga 7 e 13), nel caso invece di IE utilizzo il valore block.
Non rimane che ripulire il pannello dagli elementi che si riferiscono alla colonna che abbiamo reso visibile tramite la funzione removeBookmarksColumn:

   1: //Rimuove gli elementi che identificano la colonna nel panel
   2: function removeBookmarksColumn(header) {
   3:   $("#img" + header + "").remove();
   4:   $("#sp" + header + "").remove();
   5:   $("#div" + header + "").remove();
   6: }

Infine l’utente può “collassare” il pannello tramite l’immagine con id imgCollapse:
   1: //Apre o chiudi il panel contenente le colonne rimosse
   2: function modifiedImage() {
   3: if ($('#imgCollapse').attr('src') == "img/plus.gif") {
   4:     $('#pnlColumns').show('slow');
   5:     $('#imgCollapse').attr('src', 'img/min.gif');
   6:   }
   7: else {
   8:     $('#pnlColumns').hide('slow');
   9:     $('#imgCollapse').attr('src', 'img/plus.gif');
  10:   }
  11: }

Oltre alla funzione di minimizzazione viene modificato il valore dell’attributo src dell’immagine (add, min).
Il codice come ho già detto in altri post, si può notevolmente migliorare, con ulteriori controlli e personalizzazioni. Per concludere alcune immagini:
(NB: lo stile applicato alla griglia lo potete trovare al seguente indirizzo: Style GridView.)
VisibilityGrid1
VisibilityGrid2 
VisibilityGrid3

0 commenti:

Post a Comment