<pedrocorreia.net ⁄>
corner
<mySearch ⁄> <mySearch ⁄>

corner
 
corner
<mySnippets order="rand" ⁄> <mySnippets order="rand" ⁄>

corner
 
corner
<myContacts ⁄> <myContacts ⁄>

<email ⁄>


pc@pedrocorreia.net

<windows live messenger ⁄>


pedrojacorreia@hotmail.com

<myCurriculum type="pdf" ⁄>


Download
corner
 
corner
<myBlog show="last" ⁄> <myBlog show="last" ⁄>

corner
 
corner
<myNews show="rand" ⁄> <myNews show="rand" ⁄>

corner
 
corner
<myNews type="cat" ⁄> <myNews type="cat" ⁄>

corner
 
corner
<myQuote order="random" ⁄> <myQuote order="random" ⁄>

Transportai um punhado de terra todos os dias e fareis uma montanha
corner
 
corner
<myPhoto order="random" ⁄> <myPhoto order="random" ⁄>

<pedrocorreia.net ⁄>
corner
 
corner
<myAdSense ⁄> <myAdSense ⁄>

corner
 
corner
<myVisitorsMap ⁄> <myVisitorsMap ⁄>

corner
 
 

<Construir dinâmicamente uma gridview mantendo o estado entre postbacks ⁄ >




clicks: 24054 24054 2008-03-22 2008-03-22 goto mySnippets mySnippets asp.net  Download  Bookmark This Bookmark This



Para quem anda por estas andanças do Web Development, saberá certamente que o protocolo HTTP é stateless, ou seja, não guarda estados entre postbacks.
Por exemplo quando o nosso browser recebe uma página, e posteriormente ao executar outro comando (por ex. requisitar outra página), o HTTP não terá qualquer conhecimento da página anterior, porque não guarda estados, cada pedido é como se se tratasse de um pedido completamente novo.


Existem várias formas de ultrapassar essa feature :) do HTTP, podemos por ex. recorrer ao cliente usando cookies; viewstate; passar elementos por GET ou POST; guardar os dados numa tabela, num ficheiro xml, etc; utilizar sessões, sendo que estas completamente geridas pelo servidor.


As sessões estão presentes nas linguagens server-side, tais como: asp.net, php, asp, jsp, etc, etc.


A plataforma .NET disponibiliza-nos a ASP.NET Session State, é nela que me vou basear para construir este snippet.


Explicando mais detalhadamente o snippet. Vai consistir em ir preenchendo uma gridview, mas sem a ir gravando para a base de dados, suponhamos que queremos introduzir vários valores e só no fim é que os queremos gravar na nossa BD (não está exemplificado neste snippet).
Como a inserção de dados na gridview vai sendo feito através de um formulário perfeitamente normal, no qual o utilizador preenche os dados e de seguida pressionará um botão de submit, será feito um postback, sendo que como já tinhamos visto antes o HTTP não guardar os estados, porém o ASP.NET disponibiliza-nos duas (entre outras) possiveis maneiras de ultrapassar isto - Viewstate e Session.


A Session é mantida a nivel do servidor, isto faz com que se o nosso site tiver muito tráfego/ requests poderá implicar um problema a nivel dos recursos disponiveis, ao passo que o Viewstate é enviado para o cliente e depois retornado ao servidor, para pequenas informações o Viewstate servirá perfeitamente, porém quando a quantidade de informação começar a crescer muito, irá aumentar o tamanho da página e consequentemente a informação transmitida entre cliente-servidor-cliente.


Utilizar a Session poderá ser mais eficaz se por ex. a quantidade de informação é grande, se os dados que queremos que persistam são "importantes", visto que se forem no viewstate ficarão facilmente acessiveis ao utilizador (se bem que o viewstate possui propriedades de encriptação, mas o que vai fazer com que aumente ainda mais :)) ou então se por ex. queremos des/serializar objectos, como por ex. DataSet, DataTable, visto ter um maior e melhor suporte do que no viewstate.


A nossa gridview vai ser alimentada por uma DataTable, DataTable essa que vai armazenando a informação (campos/colunas e registos). A DataTable vai pertencer a um objecto DynamicGridview, este objecto será constituído por vários atributos, propriedades e métodos, será o principal container, pois será o que vai ser armazenado na Session, isto é, quando fazemos um postback vamos guardar os dados submetidos no DynamicGridview, depois no evento Page_Load, vamos buscar à  Session esse objecto e re-construir dinâmicamente a gridview.



Existirão algumas pequenas nuances:
- a primeira vez que a página seja chamada (!IsPostBack) vamos construir o nosso objecto que vai conter a tal DataTable, responsável pelo preenchimento da gridview, como tal aproveitamos para indicar quais as colunas/ campos e os seus respectivos tipos e guardar o nosso objecto (DynamicGridview) na Session para que futuramente o possamos utilizar;
- depois de já ter a DynamicGridview inicializada e a DataTable com a informação da estrutura, vamos começar a preenchê-la com os inputs do utilizador;
- por último, o que queremos é ter sempre a gridview actualizada e com a última informação inserida/ actualizada, como tal vamos sempre verificando se a nossa Session já contém um objecto DynamicGridview, se contiver então vamos construir a nossa gridview. Isto vai permitir por exemplo navegar noutras páginas e quando regressarmos à  página com a gridview ela será preenchida automaticamente com os dados que já tinhamos inserido;




Esta será o esquema de ficheiros:





Este será +/- o esquema das classes e a interligação entre elas:






Cá vai o CSS do costume: style.css

  1. body{
  2. font-family: Tahoma, Verdana, Arial, sans-serif;font-size: 11px;color: #000;
  3. margin: 8px;text-align: center;
  4. }
  5.  
  6.  
  7. /****** <campos input > ******/
  8. select, input{background-color: #ffffff;font-size: 8pt;border: 1px solid #1b5b7e;padding-left: 1px;}
  9. select:hover, input:hover{color: #000080;background-color: #99CCFF}
  10. select:focus, input:focus{color: #000;background-color:#ffffd4; }
  11.  
  12. input[type="text"]:hover{background: #99CCFF url("images/pencil.png") no-repeat bottom right;}
  13. input[type="text"]:focus{background: #ffffd4 url("images/edit.png") no-repeat bottom right;}
  14.  
  15. input[type="submit"]{background: #ffffff url("images/save.png") no-repeat center right;padding-right: 18px;height: 20px;}
  16. input[type="submit"]:hover{background: #ffffd4 url("images/submit.png") no-repeat center right;padding-right: 18px;height: 20px;}
  17. /****** <campos input /> ******/
  18.  
  19.  
  20. /****** <tabelas> ******/
  21. table{border: solid 1px #1b5b7e; margin: 0 auto;}
  22. th{border: solid 1px #3C7088;color: #fff;font-weight: bold;text-align: center;
  23. background: #3C7088 url("images/bg.gif") repeat-x left center; width: 190px;padding: 2px;
  24. }
  25. td{background: #EFEBE7 url("images/bg2.gif") repeat-x left center;border:solid 1px #1b5b7e;padding: 2px;}
  26. a{color: #000080;text-decoration: underline;}
  27. /****** <tabelas/> ******/




Para preencher a DataTable vamos construir 3 objectos: Field; Fields e Record. O objecto Fields será um conjunto de 1 ou mais objectos Field. Aquando do preenchimento da DataTable vamos recorrer ao Fields (contém os nomes dos campos e o seu tipo) e ao Records (contém os registos a serem inseridos).






Field.cs

  1. using System;
  2. using System.Data;
  3. using System.Configuration;
  4. using System.Web;
  5. using System.Web.Security;
  6. using System.Web.UI;
  7. using System.Web.UI.HtmlControls;
  8. using System.Web.UI.WebControls;
  9. using System.Web.UI.WebControls.WebParts;
  10.  
  11. /// <summary>
  12. /// Summary description for Field
  13. /// </summary>
  14. public class Field
  15. {
  16. private string _name;
  17. private Type _type;
  18.  
  19. /// <summary>
  20. /// Método construtor
  21. /// </summary>
  22. /// <param name="name">Nome do Campo</param>
  23. /// <param name="systemType">Tipo do Campo</param>
  24. public Field(string name, string systemType){
  25. this._name = name;
  26. this._type = System.Type.GetType(systemType);
  27. }
  28.  
  29. /// <summary>
  30. /// Obter nome do campo
  31. /// </summary>
  32. public string FieldName {get{return this._name;}}
  33.  
  34. /// <summary>
  35. /// Obter tipo do campo
  36. /// </summary>
  37. public Type FieldType {get{return this._type;}}
  38. }




Fields.cs, uma pequena chamada de atenção para esta class, como podem serão implementadas as interfaces IEnumerator, IEnumerable. Isto é para que possamos usar o foreach, claro que não seria necessário, poderiamos por ex. usar um ciclo for, mas fica apenas como exemplo da implementação destas duas interfaces.

  1. using System;
  2. using System.Data;
  3. using System.Configuration;
  4. using System.Web;
  5. using System.Web.Security;
  6. using System.Web.UI;
  7. using System.Web.UI.HtmlControls;
  8. using System.Web.UI.WebControls;
  9. using System.Web.UI.WebControls.WebParts;
  10.  
  11. using System.Collections;
  12.  
  13. /// <summary>
  14. /// Summary description for Fields
  15. /// </summary>
  16. public class Fields : IEnumerator, IEnumerable {
  17.  
  18. private ArrayList _fields=new ArrayList();
  19. private int _pos=-1;
  20.  
  21. /// <summary>
  22. /// Método construtor
  23. /// </summary>
  24. public Fields(){ }
  25.  
  26. /// <summary>
  27. /// Método construtor (overloading)
  28. /// </summary>
  29. /// <param name="field">Objecto Campo</param>
  30. public Fields(Field field) { this.AddField(field); }
  31.  
  32. /// <summary>
  33. /// Adicionar Campo
  34. /// </summary>
  35. /// <param name="field"></param>
  36. public void AddField(Field field) {this._fields.Add(field);}
  37.  
  38. /// <summary>
  39. /// Implementar as interfaces: IEnumerator, IEnumerable
  40. /// Isto faz com que possamos usar o foreach
  41. /// </summary>
  42. #region IEnumerator Members
  43.  
  44. public object Current {get{return this._fields[this._pos];}}
  45.  
  46. public bool MoveNext() {
  47. this._pos++;
  48. return (this._pos < this._fields.Count);
  49. }
  50.  
  51. public void Reset() {this._pos =-1;}
  52.  
  53. #endregion
  54.  
  55. #region IEnumerable Members
  56.  
  57. public IEnumerator GetEnumerator() {
  58. return (IEnumerator)this;
  59. }
  60.  
  61. #endregion
  62. }




Record.cs

  1. using System;
  2. using System.Data;
  3. using System.Configuration;
  4. using System.Web;
  5. using System.Web.Security;
  6. using System.Web.UI;
  7. using System.Web.UI.HtmlControls;
  8. using System.Web.UI.WebControls;
  9. using System.Web.UI.WebControls.WebParts;
  10.  
  11. using System.Collections;
  12.  
  13. /// <summary>
  14. /// Class que contém os registos da DataTable
  15. /// </summary>
  16. public class Record{
  17. private Hashtable _arr = new Hashtable();
  18.  
  19. /// <summary>
  20. /// Método construtor
  21. /// </summary>
  22. public Record() { }
  23.  
  24. /// <summary>
  25. /// Adicionar informação a um campo
  26. /// </summary>
  27. /// <param name="col">Campo</param>
  28. /// <param name="value">Valor</param>
  29. public void AddFieldValue(string col, string value) {this._arr.Add(col, value);}
  30.  
  31. /// <summary>
  32. /// Obter valor de um determinado campo
  33. /// </summary>
  34. /// <param name="field">Campo</param>
  35. /// <returns>Valor do Campo</returns>
  36. public string GetFieldValue(string field) { return this._arr[field].ToString();}
  37.  
  38. /// <summary>
  39. /// Obter o número de colunas do registo
  40. /// </summary>
  41. /// <returns></returns>
  42. public int Count() { return this._arr.Count;}
  43. }



Settings.cs, possui unicamente informações para a aplicação, poderemos por ex. alterar aqui o nome das colunas/ campos e os seus respectivos tipos.

  1. using System;
  2. using System.Data;
  3. using System.Configuration;
  4. using System.Web;
  5. using System.Web.Security;
  6. using System.Web.UI;
  7. using System.Web.UI.HtmlControls;
  8. using System.Web.UI.WebControls;
  9. using System.Web.UI.WebControls.WebParts;
  10.  
  11. using System.Collections;
  12. /// <summary>
  13. /// Class que fornece informações,
  14. /// só possui métodos/atributos estáticos
  15. /// </summary>
  16. public class Settings {
  17.  
  18. static public string mySessionName = "MyPersistentDataTable";
  19. static public string[] column_titles = { "BI", "Nome", "Telefone" };
  20.  
  21. /// <summary>
  22. /// Obter os nomes das colunas que vão preencher
  23. /// a DataTable
  24. /// </summary>
  25. /// <returns></returns>
  26. public static Hashtable Columns() {
  27. Hashtable columns = new Hashtable();
  28.  
  29. columns.Add(column_titles[0], "System.Int32");
  30. columns.Add(column_titles[1], "System.String");
  31. columns.Add(column_titles[2], "System.String");
  32.  
  33. return columns;
  34. }
  35.  
  36. /// <summary>
  37. /// Método construtor
  38. /// </summary>
  39. public Settings() { }
  40. }








DynamicGridview.cs, este será o objecto armazenado na sessão, irá conter toda a informação necessária para construir a gridview quando assim fôr necessário. Neste objecto temos um public enum que é o AllowDuplicates, podendo tomar 2 valores, Yes ou No, como o nome já explicita, vai permitir ou não que a DataTable aceite objectos Records duplicados

  1. using System;
  2. using System.Data;
  3. using System.Configuration;
  4. using System.Web;
  5. using System.Web.Security;
  6. using System.Web.UI;
  7. using System.Web.UI.HtmlControls;
  8. using System.Web.UI.WebControls;
  9. using System.Web.UI.WebControls.WebParts;
  10.  
  11. using System.Collections;
  12.  
  13. /// <summary>
  14. /// Summary description for DynamicGridview
  15. /// </summary>
  16. public class DynamicGridview {
  17. private DataTable _dt;
  18. private string _tableName;
  19. private Int16 _allow_duplicates;
  20. public enum AllowDuplicates { Yes = 1, No = 0 }
  21.  
  22. /// <summary>
  23. /// Método construtor
  24. /// </summary>
  25. /// <param name="dataTableName">Nome da DataTable armazenada na sessão</param>
  26. public DynamicGridview(string dataTableName, AllowDuplicates allow) {
  27. this._dt = new DataTable(dataTableName);
  28. this._tableName = dataTableName;
  29. this._allow_duplicates = (Int16)allow;
  30. }
  31.  
  32. /// <summary>
  33. /// Adicionar colunas à DataTable
  34. /// </summary>
  35. /// <param name="col">Colunas</param>
  36. public void AddColumns(Fields fields) {
  37. foreach (Field field in fields) {
  38. this._dt.Columns.Add (new DataColumn(field.FieldName, field.FieldType));
  39. }
  40. this.SaveSession();
  41. }
  42.  
  43. /// <summary>
  44. /// Adicionar linha de dados à DataTable
  45. /// </summary>
  46. /// <param name="row">Registo</param>
  47. public void AddRecord(Record record) {
  48. if (this._allow_duplicates == 0) {
  49. if (RecordExists(record)) return;
  50. }
  51.  
  52. DataRow myNewRow;
  53. DataColumn auxCol;
  54.  
  55. myNewRow = this._dt.NewRow();
  56. int count = this._dt.Columns.Count;
  57. for (int i = 0; i < count; i++) {
  58. auxCol = this._dt.Columns[i];
  59. myNewRow[auxCol] = record.GetFieldValue(auxCol.ColumnName);
  60. }
  61.  
  62. this._dt.Rows.Add(myNewRow);
  63. this.SaveSession();
  64. }
  65.  
  66. /// <summary>
  67. /// Guardar objecto na sessão
  68. /// </summary>
  69. public void SaveSession() { HttpContext.Current.Session[this._tableName] = this; }
  70.  
  71. /// <summary>
  72. /// Verificar se o registos já se encontra na DataTable
  73. /// </summary>
  74. /// <param name="rec"></param>
  75. /// <returns></returns>
  76. private Boolean RecordExists(Record rec) {
  77. if (_dt.Rows.Count == 0) return false;
  78.  
  79. bool result = false;
  80. int count = _dt.Columns.Count, aux = 0;
  81.  
  82. foreach (DataRow row in _dt.Rows) {
  83. foreach (DataColumn col in _dt.Columns) {
  84. if (rec.GetFieldValue(col.ColumnName).Equals(row[col.ColumnName].ToString())) aux++;
  85. }
  86. if (aux == count) { result = true; break; }
  87. aux = 0;
  88. }
  89. return result;
  90. }
  91.  
  92. /// <summary>
  93. /// Obter DataTable
  94. /// </summary>
  95. public DataTable DTable { get { return this._dt; } }
  96. }




Default.aspx

  1. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" EnableViewState="false" %>
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml">
  4. <head runat="server">
  5. <title>myDynamicGridView</title>
  6. <link href="style.css" rel="stylesheet" type="text/css" />
  7. </head>
  8. <body>
  9. <form id="form1" runat="server">
  10. <div>
  11. <asp:GridView ID="gridView" runat="server" EnableViewState="false"></asp:GridView>
  12. <br />
  13. BI <asp:TextBox ID="txtBI" runat="server"></asp:TextBox>
  14. &nbsp;Nome <asp:TextBox ID="txtNome" runat="server"></asp:TextBox>
  15. &nbsp;Telefone <asp:TextBox ID="txtTelefone" runat="server"></asp:TextBox>
  16. <br /><br /><br />
  17. <asp:Button ID="btAddRecord" runat="server" OnClick="btAddRecord_Click" Text="Adicionar Registo" />
  18. <br /><br />
  19. <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/other_page.htm">Ir para outra página</asp:HyperLink>
  20. </div>
  21. </form>
  22. </body>
  23. </html>




E a code-bedind respectiva - Default.aspx.cs

  1. using System;
  2. using System.Configuration;
  3. using System.Data;
  4. using System.Web;
  5. using System.Web.Security;
  6. using System.Web.UI;
  7. using System.Web.UI.HtmlControls;
  8. using System.Web.UI.WebControls;
  9. using System.Web.UI.WebControls.WebParts;
  10.  
  11. using System.Collections;
  12.  
  13. public partial class _Default : System.Web.UI.Page {
  14.  
  15. DynamicGridview myDynamicGrid;
  16.  
  17. protected void Page_Load(object sender, EventArgs e) {
  18.  
  19. //se a DynamicGridview já estiver em sessão
  20. //construímos a gridview
  21. if (Session[Settings.mySessionName] != null) {
  22. myDynamicGrid = (DynamicGridview)Session[Settings.mySessionName];
  23. gridView.DataSource = this.myDynamicGrid.DTable;
  24. gridView.DataBind();
  25. }
  26.  
  27. //a DynamicGridview não se encontra na sessão nem estamos a fazer um postback
  28. //vamos então instanciar a DynamicGridview e adicionar-lhe as colunas/campos
  29. else if (!IsPostBack) {
  30. myDynamicGrid = new DynamicGridview(Settings.mySessionName, DynamicGridview.AllowDuplicates.No);
  31.  
  32. //<construir campos da gridview>
  33. Fields fields = new Fields();
  34. IDictionaryEnumerator columnsEnum = Settings.Columns().GetEnumerator();
  35. while (columnsEnum.MoveNext()) {
  36. fields.AddField(new Field(columnsEnum.Key.ToString(), columnsEnum.Value.ToString()));
  37. }
  38. //<construir campos da gridview />
  39.  
  40. //adicionar campos
  41. this.myDynamicGrid.AddColumns(fields);
  42. }
  43. }
  44.  
  45. protected void btAddRecord_Click(object sender, EventArgs e) {
  46. this.myDynamicGrid = (DynamicGridview)Session[Settings.mySessionName]; //obter gridview
  47.  
  48. //criar um registo
  49. Record rec = new Record();
  50. rec.AddFieldValue(Settings.column_titles[0], txtBI.Text);
  51. rec.AddFieldValue(Settings.column_titles[1], txtNome.Text);
  52. rec.AddFieldValue(Settings.column_titles[2], txtTelefone.Text);
  53.  
  54. //adicionar registo
  55. this.myDynamicGrid.AddRecord(rec);
  56.  
  57. //actualizar gridview
  58. gridView.DataSource = this.myDynamicGrid.DTable;
  59. gridView.DataBind();
  60. }
  61. }




Aqui fica uma breve demonstração:











Como podem observar não são inseridos registos que já existam, mesmo fazendo Refresh à página ou inserindo-os novamente.






Qualquer erro/ dúvida é só dizer!









clicks: 24054 24054 2008-03-22 2008-03-22 goto mySnippets mySnippets asp.net  Download  Bookmark This Bookmark This