jueves, 15 de marzo de 2007

- Paging and Sorting

Las tareas de Paging y Sorting de forma simultanea en páginas ASP.NET presenta un pequeño cuidado; se debe mantener el índice de la página en la que estamos cuando establecemos un nuevo criterio de ordenamiento. De la misma forma debemos mantener el criterio de ordenamiento al cambiar el índice de la página a la que queremos acceder.

El código que sigue es un ejemplo o una de las formas, siempre hay mas de una forma :), de como se puede lograr esta tarea.-

- SortingPaging.aspx (nuestra página aspx)
    <asp:GridView ID="gridSample" runat="server" AutoGenerateColumns="false"
AllowPaging = "true" OnPageIndexChanging = "gridView_PageIndexChanging"
PageSize = "5"
AllowSorting = "true" OnSorting = "gridView_Sorting">
<Columns>
<asp:BoundField HeaderText="Nombre"
DataField="Nombre"
SortExpression = "Nombre" />
<asp:BoundField HeaderText="Edad"
DataField="edad"
SortExpression = "edad" />
</Columns>
</asp:GridView>
- SortingPaging.aspx.cs (nuestro archivo de code behind)
using System;
using System.Data;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class SortingPaging : Page {
protected void Page_Load(object sender, EventArgs e) {
DataSet ds = new DataSet();
// Aqui llenamos nuestro DataSet
DataView dv = ds.Tables[0].DefaultView;
dv = sortDataView(dv, true);
gridSample.DataSource = dv;
gridSample.DataBind();
}

private string GridSampleSortDirection {
get { return ViewState["SortDirection"] as string ?? "ASC"; }
set { ViewState["SortDirection"] = value; }
}

private string GridSampleSortExpression {
get { return ViewState["SortExpression"] as string ?? "Nombre"; }
set { ViewState["SortExpression"] = value; }
}

private string getSortDirection() {
switch (GridSampleSortDirection) {
case "ASC":
GridSampleSortDirection = "DESC";
break;

case "DESC":
GridSampleSortDirection = "ASC";
break;
}
return GridSampleSortDirection;
}

protected void gridView_PageIndexChanging(object sender, GridViewPageEventArgs e) {
gridSample.DataSource = sortDataView(gridSample.DataSource as DataView, true);
gridSample.PageIndex = e.NewPageIndex;
gridSample.DataBind();
}

protected DataView sortDataView(DataView dataView, bool isPageIndexChanging) {
if (isPageIndexChanging) {
dataView.Sort = string.Format("{0} {1}",
GridSampleSortExpression,
GridSampleSortDirection);
} else {
dataView.Sort = string.Format("{0} {1}",
GridSampleSortExpression,
getSortDirection());
}
return dataView;
}

protected void gridView_Sorting(object sender, GridViewSortEventArgs e) {
GridSampleSortExpression = e.SortExpression;
int pageIndex = gridSample.PageIndex;
gridSample.DataSource = sortDataView(gridSample.DataSource as DataView, false);
gridSample.DataBind();
gridSample.PageIndex = pageIndex;
}
}

- Conversión de Ilist en DataTable

Nota: Este código ha sido probado con .NET Framework 2.0

Muchas veces retornamos de nuestra fuente de datos un set de datos de tipo Ilist, el cual enlazamos a nuestros GridViews sin ningún problema.

Como veremos en próximas entradas, hay una forma muy interesante de realizar sorting y paging al mismo tiempo, para ello necesitamos pasar como parámetro un DataTable en uno de los métodos que implementaremos , por lo cual vamos a escribir una utilidad que convierta un Ilist en un DataTable.

Como en casos anteriores, tomaremos la aplicación de ejemplo de este blog (virtualEducation) y agregaremos en el módulo de Utilidades la clase Converter.cs, la cual iremos completando en las próximas entradas. En esta oportunidad , solo definiremos el método public static DataTable IList2DataTable(IList iList) para tener solo el conversor que necesitamos.

Aquí tenemos el código:


using System;
using System.Collections;
using System.Data;
using System.Reflection;

namespace Utilidades {
public class Converter {
public static DataTable IList2DataTable(IList iList) {
DataTable oDataTableReturned = new DataTable();

if (iList.Count > 0) {
object _baseObj = iList[0];
Type objectType = _baseObj.GetType();
PropertyInfo[] properties = objectType.GetProperties();

foreach (PropertyInfo property in properties) {
DataColumn oColumna;
oColumna = new DataColumn();
oColumna.ColumnName = property.Name;
oColumna.DataType = property.PropertyType;
oDataTableReturned.Columns.Add(oColumna);
}

foreach (object objItem in iList) {
DataRow oFila;
oFila = oDataTableReturned.NewRow();
foreach (PropertyInfo property in properties) {
oFila[property.Name] = property.GetValue(objItem, null);
}
oDataTableReturned.Rows.Add(oFila);
}
}
return oDataTableReturned;
}
}
}

- Issue con nuevas clases en VS2005

El nuevo Visual Studio 2005 es una muy buena herramienta que nos ofrece muchas ventajas y nos ahorra muchos esfuerzos.

Sin embargo hay una característica que no me resulta del todo agradable. Cuando agregamos librerias de clases (class libraries) como en el ejemplo de virtualEducation y creamos una clase dentro de alguna de estas librerías como hemos hecho en una entrada anterior con el generador de GUIDs; notamos que las clases nuevas no tienen accesibilidad pública por lo que al querer utilizarlas no están disponibles.

Esto requiere que agreguemos el modificador de acceso a public y hagamos un rebuild de la librería de clases y a veces necesitamos hacer rebuild de toda la solución.-

Me gustaría saber si alguien conoce una forma de configurar el VS 2005 para que por defecto genere las clases como públicas.

lunes, 12 de marzo de 2007

- Subir archivos grandes con httpRuntime

En algunas situaciones necesitamos permitir que los usuarios de nuestra aplicación realicen upload de archivos de tamaño importante (digamos 6 a 10 Mb).
Hemos visto a través de una entrada anterior como subir archivos con ASP.NET y AJAX.
Por defecto, ASP.NET permite realizar uploads de hasta 4Mb. Hoy veremos como podemos configurar nuestros servidores o nuestra aplicación para que permita subir archivos mayores a este tamaño.

Esta configuración se puede realizar tanto en los archivos machine.config como en archivos web.config de cada aplicación. El elemento en cuestión que nos permitirá esta operación es <httpRuntime> a través de su atributo maxRequestLength.

Este atributo indica el tamaño máximo soportado por ASP.NET para un archivo que necesita ser subido a un servidor. El tamaño se debe especificar en kbytes. Como adelantamos antes el valor por defecto es 4096 kb.

El elemento <httpRuntime> debe ubicarse dentro de la sección configuration/system.web de nuestro archivo de configuración. Veamos un ejemplo simple para un archivo de 6Mb:
<configuration>
<system.web>
<httpRuntime maxRequestLength="6144"/>
</system.web>
</configuration>

- GUID Generator con C#

GUID es un acrónimo para Globally Unique Identifier o identificador único global, es un número de 128 bits que es producido por el sistema operativo Windows o por algunas aplicaciones Windows para identificar un componente particular, una aplicación, un archivo, un registro en una base de datos y/o un usuario. Por ejemplo, podemos tener un sitio web que genere un GUID y se lo asigne a un usuario para grabar las acciones de este usuario en la sesión (lo que se conoce como session tracking).

GUID se utiliza también en el registro de Windows para identificar dlls COM. Algunos DBAs incluso utilizan un GUID como claves primarias de sus bases de datos.
Un ejemplo de GUID generado es el siguiente: {ded53e2b-91e9-4682-b673-862ca6503b2e}

Si bien no está garantizado que un GUID generado sea único, el total de claves que se pueden generar (2 elevado a la 128 potencia
) es tan grande que la probabilidad de que se repita es realmente muy pequeña. Para dar una idea de esto podemos decir que una aplicación que genere 10 mil millones de GUID la probabilidad de que se repita una clave generada es de 1 en un quintillión (en la escala americana - 10 elevado a la 30 potencia en la escala tradicional).

Veamos entonces como se genera un GUID en una aplicación Windows con C#. Para mostrar un ejemplo vamos a utilizar la aplicación virtualEducation que generamos en una entrada anterior (ver entrada) y vamos a ubicar nuestra clase GUIDGenerator en el módulo de Utilidades.
Este es todo el código que necesitamos:



namespace Utilidades {
class GUIDGenerator {
public static string getGUIDString() {
string guidIdentifier = System.Guid.NewGuid().ToString();
guidIdentifier = guidIdentifier.Replace("-", string.Empty);
guidIdentifier.ToUpper();
return guidIdentifier;
}
}
}

viernes, 9 de marzo de 2007

- Tabs simples en ASP.NET

Algunas de nuestras aplicaciones necesitan mostrar tabs o elementos superpuestos. Vamos a ver como se logra este efecto utilizando dos elementos de ASP.NET; estos son el elemento asp:Menu y el elemento asp:MultiView. El código es realmente muy sencillo y nos ayudamos con un set de 4 imágenes. Dos imágenes para el primer y segundo tab (con sus estados activado y desactivado).


-SimpleTabulation.aspx (nuestra página aspx)
<body>
<form id="form1" runat="server">
<div>
<asp:Menu ID="MenuTab" Width="120px" runat="server"
Orientation="Horizontal" StaticEnableDefaultPopOutImage="False"
OnMenuItemClick="MenuTab_MenuItemClick">
<Items>
<asp:MenuItem ImageUrl="~/images/TabOneOn.jpg" Value="0"/>
<asp:MenuItem ImageUrl="~/images/TabTwoOff.jpg" Value="1"/>
</Items>
</asp:Menu>
<asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">
<asp:View ID="View1" runat="server">
Contenido del Tab 1!
</asp:View>
<asp:View ID="View2" runat="server">
Contenido del Tab 2!
</asp:View>
</asp:MultiView>
</div>
</form>
</body>

-SimpleTabulation.aspx.cs (nuestro archivo de code behind)

using System;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class SimpleTabulation : Page {
protected void MenuTab_MenuItemClick(object sender, MenuEventArgs e) {
MultiView1.ActiveViewIndex = Int32.Parse(e.Item.Value);
int selectedTab = Int32.Parse(e.Item.Value);

switch (selectedTab) {
case 0:
MenuTab.Items[0].ImageUrl = "~/images/TabOneOn.jpg";
MenuTab.Items[1].ImageUrl = "~/images/TabTwoOff.jpg";
break;

case 1:
MenuTab.Items[0].ImageUrl = "~/images/TabOneOff.jpg";
MenuTab.Items[1].ImageUrl = "~/images/TabTwoOn.jpg";
break;
}
}
}

- Custom Exceptions en ASP.NET

En esta entrada veremos como utilizar excepciones personalizadas o custom exceptions. La idea básica es muy simple. Se trata crear una clase que herede de la clase Exception de ASP.NET y pasarle argumentos básicos como puede ser el mensaje de la excepción e incluso parámetros personalizados. Vamos a ver un ejemplo muy simple en el cual tenemos un label en el cual mostraremos el mensaje de la excepción arrojada desde nuestra aplicación.

- CustomExceptions.aspx (nuestra página aspx)
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblException"
runat="server" Text="" />
</div>
</form>
</body>
- CustomExceptions.aspx.cs (nuestro archivo de code behind)
using System;
using System.Web.UI;

public class MyCustomException : Exception {
public MyCustomException(string message) : base(message) { }
}

public partial class TestCustomExceptions : Page {

public void Page_Load(object sender, EventArgs e) {
try {
if (1 == 1) {
throw new MyCustomException("Testing Custom Exceptions.-");
}
} catch (MyCustomException exc) {
Trace.Write(exc.Message);
lblException.Text = exc.Message;
}
}
}

- File Upload con AJAX

Vamos a ver en esta entrada el código necesario para realizar upload de un archivo y que la experiencia de usuario sea cómoda a través del uso de AJAX. El concepto es muy simple; utilizamos dos divs, uno para mostrar el componente FileUpload de ASP.NET y otro para mostrar un mensaje o una imagen animada al usuario mientras la tarea de subir el archivo se ejecuta en segundo plano y no se muestra al usuario ninguna acción de Postback o roundtrip al servidor. A través de AJAX utilizamos una serie de funciones JavaScript para mostrar/ocultar los divs.

He aquí el codigo necesario:
- FileUpload.aspx (Nuestra página aspx)

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>File Upload Test</title>
<script language="javascript" type="text/javascript">

function divFileHide() {
var divFile = document.getElementById('fileUploadDiv');
divFile.style.display = 'none';

var divLoading = document.getElementById('loadingFileDiv');
divLoading.style.display = 'block';
}

function divFileShow() {
var divFile = document.getElementById('fileUploadDiv');
iframe.style.display = 'block';

var divLoading = document.getElementById('loadingFileDiv');
divLoading.style.display = 'none';
}

function upload(){
divFileHide();
}

function onComplete( result ) {
divFileShow();
}
</script>

</head>
<body>
<form id="form1" runat="server">
<div id="fileUploadDiv">
<asp:FileUpload ID="FileUpload1" runat="server" Visible="true" />
<asp:Button ID="btnUpload" runat="server" Text="Subir Archivo"
OnClick="btnUpload_Click"
Visible="true" />
<asp:Label ID="lblError" runat="server" Visible="false"
Font-Bold="true" ForeColor="red" />
</div>
<div id="loadingFileDiv" style="display: none;">
El archivo está subiendo...
</div>
</form>
</body>
- FileUpload.aspx.cs (Nuestro archivo de code behind)
using System;
using System.Web.UI;

public partial class FileUpload : Page {
protected void Page_Load(object sender, EventArgs e) {
btnUpload.Attributes.Add("onclick", "return upload();");
}
protected void btnUpload_Click(object sender, EventArgs e) {
if (FileUpload1.HasFile) {
try {
String uploadFolder = "D:\\Uploads\\";
String file = FileUpload1.FileName;

FileUpload1.SaveAs(uploadFolder + file);
} catch (Exception ex) {
Trace.Write(ex.Message);
}
} else {
lblError.Text = "
El archivo no ha sido especificado";
lblError.Visible = true;
}
}
}

jueves, 1 de marzo de 2007

- Esqueleto de una aplicación Web en ASP.NET

En esta oportunidad quiero compartir con ustedes una de las formas básicas de crear una aplicación web que utilice componentes reutilizables, que tenga módulos independientes y que pueda integrar algún framework de persistencia como NHibernate o Ibatis.
Esta tarea la llevaremos a cabo utilizando el IDE de desarrollo Visual Studio 2005. Vamos a suponer que estamos desarrollando una aplicación orientada a e-learning. Lo primero que debemos tener presente es que todos estos módulos los tendremos en una misma carpeta. En mi caso la carpeta en cuestión es D:\NETProjects\E-Learning.

* Módulos de nuestra aplicación:
- Sitio Web: En éste módulo tendremos nuestras páginas .aspx con sus correspondientes archivos de code behind (.aspx.cs), las cuales utilizarán las clases y objetos (no se trata de otra cosa) del resto de los módulos involucrados en la aplicación.

- Módulo de Model: En éste módulo almacenaremos todas las clases de objetos que nuestra aplicación requiera. En nuestro caso, al tratarse de una aplicación de e-learning, ejemplos de clases que pondremos en este módulo son: curso, asignatura, profesor, etc.

- Módulo de Persistencia: En éste módulo crearemos dos carpetas. La primera será nuestra carpeta de Interfaces en la cual tendremos las interfaces de las clases que necesitamos persistir. El uso de interfaces nos permite además migrar de un framework a otro teniendo que modificar solo las implementaciones del mismo. Entonces necesitaremos una segunda carpeta que puede llamarse Implementaciones. En ella almacenaremos los archivos necesarios que dependerán del framework de persistencia que elijamos.

- Módulo de Servicio: En éste módulo se almacenarán las clases necesarias para ejecutar llamadas a las clases en el módulo de persistencia y eventualmente procesar reglas de negocio antes de realizar la persistencia.

- Módulo de Utilidades: En éste módulo se almacenarán aquellas clases que pueden ser reutilizables en más de un proyecto como pueden ser utilidades de conversion para listas, arrays, etc.-

Veamos como es el proceso en Visual Studio 2005 para lograr este esqueleto:

1- Sitio Web:
Creamos el sitio Web a través de la opción File... New Web Site... elegimos el lenguaje (C# en nuestro caso) , elegimos la ruta y el nombre de nuestro sitio o aplicación Web, para nuestro ejemplo será D:\NETProjects\E-Learning\virtualEducation donde E-Learning es la carpeta del proyecto conteniendo todos los módulos y virtualEducation es nuestro sitio web.

2- Módulos: Agregamos los móulos que vamos a necesitar. Abrimos la opción File... New Project y aquí tenemos varias opciones a tener en cuenta. Lo primero es elegir el tipo de proyecto. Elegimos Visual C# - Windows en Project types. Elegimos Class Library como template. Especificamos el nombre del poyecto, en nuestro caso Model, especificamos la ubicación: D:\NETProjects\E-Learning y por último seleccionamos el drop down list a Add to Solution.



Realizamos el mismo procedimiento para nuestros otros módulos teniendo en cuenta la creación de las dos carpetas en el módulo de Persistencia (Interfaces e Implementaciones)



3- Referencias: Ahora necesitamos agregar a nuestro sitio web las referencias a estos módulos para que al realizar la compilación nos genere dlls para estos proyectos. Para ello hacemos click derecho en nuestro sitio web y seleccionamos Add Reference... vamos al tab de Projects y seleccionamos los proyectos creados en el paso anterior.

4- Construir Solución: Por último, hacemos click derecho a nuestra solución Solution \virtualEducation (5 projects) y elegimos la opción Rebuild Solution. Vemos como se generan las dlls de nuestros proyectos en la carpeta Bin de nuestro sitio web.-



Con estos sencillos 4 pasos, tenemos listo nuestro esqueleto para una aplicación Web modularizada, escalable, lista para utilizar un framework de persistencia y componentes reutilizables.-

- Tooltips en columnas de GridView

Muchas veces por razones de espacio necesitamos poner nombres abreviados o acrónimos en las cabeceras de nuestros GridViews. Para enriquecer la experiencia de usuario podemos agregar tooltips a estas cabeceras de una forma muy sencilla de modo de proveer al usuario el nombre completo o mas descriptivo para determinado campo mostrado.

Pre-Requisitos:
* Tenemos establecida la conexión a la base de datos.
* Tenemos un DataSet con los datos para realizar el Binding al Dataview.
* Tenemos los datos en el gridview a traves de Databind().

Elementos Clave:
* Habilitar la propiedad HtmlEncode a false en cada campo enlazado.
* Utilizar elementos HTML en la propiedad HeaderText.

He aquí nuesto código:
- products.aspx (Nuestra página aspx)

<asp:gridview id="gvProducts" runat="server" autogeneratecolumns="false">
<Columns>
<asp:BoundField
HeaderText="Producto"
DataField="name"/>
<asp:BoundField
HtmlEncode="false"
HeaderText="&lt;div title='Cantidad'> Cant. </div>"
DataField="qty"/>
<asp:BoundField
HtmlEncode="false"
HeaderText="&lt;div title='Precio'> $ </div>"
DataField="price"/>
</Columns>
</asp:gridview>
Esta es solo una de las formas de realizarlo, otra forma es utilizando el tag <acronym> de HTML.