Showing posts with label aspnet. Show all posts
Showing posts with label aspnet. Show all posts

Monday, June 22, 2009

New transaction cannot enlist in the specified transaction coordinator

Cuando estas utilizando el soporte a transacciones, System.Transactions, para realizar operaciones contra la Database en el desarrollo de tu producto de software, probablemente te encuentres con este mensajito.

System.Runtime.InteropServices.COMException (0x8004D00A): New transaction cannot enlist in the specified transaction coordinator

Lo que sucede es que NO se estan dando todos los requisitos necesario para la comunicacion entre tu Biztalk Server y tu Database Server.

Este mensaje es muy generico, aun, es necesario validar a bajo nivel si las caracteristicas necesarias para el MSDTC estan dadas, entre las dos machines.

Para ello, es necesario que utilices el DTCPing de Microsoft.

Una vez que copies el ejecutable tanto en el Biztalk Server como en el Database Server, los ejecutaras al mismo tiempo.

Desde el Biztalk Server colocaras el nombre de tu Database Server en la consola del DTCPing, y este testeara sin los requisitos minimos de comunicacion entre ambas machines, estan dadas, y te informara de los inconvenientes, para que puedas solucionarlo.

Alli, tendras informacion especifica sobre el inconveniente y ya NO un simple mensaje generico.

Monday, April 13, 2009

Interfaces Web Forms herencia multiple

Interfaces en .NET son pocas veces usadas, por NO decir que jamas son mencionadas, en la implementacion de tu producto de software.

¿Que esta sucediendo? ¿Como te sentis al saber que NO estas utilizando todo el potencial de la tecnologia que tienes en tus manos, hablando estrictamente de C#, claro esta?

Pues VB.NET es un juguete para niños, mas nada :)

Lo que sucede es que NO le encuentras, muchas veces, razon de ser a la necesidad de definir una serie de metodos en una Interface que luego deberas implementar en cada Clase o Class que requiera implementarla.

Suena a realizar trabajo doble. A que NO ganas nada, en realidad, desde un limitado punto de vista.

Nada mas falso que la verdad :)

Las Interfaces tienen una valiosa utilidad. Aquella en la que ese comportamiento definido pueda ser utilizado por otras clases con la finalidad de brindar una valvula de escape al famoso, pero poco usado polimorfismo, en un lenguaje de programacion Orientado a Objetos, quimicamente NO puro, como el caso de los lenguajes de .NET ;)

Por ejemplo, la interfaz IEnumerable, que por cierto es la mas utilizada por el .NET Framework, radica su valiosa utilidad en brindarle la posibilidad a la clase que la implemente en que esta pueda ser iterable sobre el famoso foreach. Omnipresente estructura de control de flujo en el C#.

IEnumerable es de un nivel primario que casi forma parte de la esencia misma del lenguaje C#.NET.

Nosotros ya NO implementamos IEnumerable en nuestras propias clases, pues existen diferentes estructuras de datos, que la implementan y nos facilitan ya esa tarea. Solo nos limitamos a usarla, a sabiendas que existe una estructura de control que la recibe para hacer algo con ella, iterarla. Hablamos del curioso foreach.

He alli la clave para la existencia de Interfaces en el modelo de clases orientadas al producto de software que estamos diseñando.

La necesidad de la existencia de una Clase a la par que defines Interfaces para que las utilizen y hagan algo con ellas, el trabajo pesado. Es decir, para que estas sean parte de la implementacion de un comportamiento de orden superior por el cual fueron concebidos.

¿Se entiende? ¿Toy hablando piedras?

Un ejemplo por favor...!

Bueno, el mas trivial de todos se encuentra en la definicion de un Modelo de Clases e Interfaces para el manejo de Web Forms y Web User Controls en productos de software con conectividad a Base de Datos.

Los Web Forms asumen la gestion de las acciones realizadas por el usuario e implementan el comportamiento, como respuesta a dichas acciones.

Los Web User Controls realizan las tareas a un nivel especifico de detalle, hacen el trabajo laborioso: asignar valores a los TextBox, llenar un DropDownList, recolectar los valores seteados de su propia estructura en un Business Entity y entregarselos a quien lo solicita, and other stuffs :)

En ese sentido, son los Web User Controls candidatos ideales a implementar tus propias Interfaces, previamente definidas, con la intencion de centralizar ciertos comportamientos e implementarlos a nivel de Web Forms.

En concreto, tendras una clase UIPage que hereda de Page, facilitando cierta funcionalidad comunmente usada.

class UIPage : Page
{
protected override void OnLoad( EventArgs e )
{
if( ! this.IsPostBack )
{
this.FirstRequest();
}
}

protected virtual void FirstRequest() { }

protected override void OnInit( EventArgs e )
{
this.DefineHandlers();
this.DefineObjects();
}

protected virtual void DefineHandlers() { }
protected virtual void DefineObjects() { }

// Otras tantas funcionalidades
}

A la vez, tendras una clase UIPageForm que hereda de UIPage

class UIPageForm<TForm> : UIPage
where TForm : IForm
{
protected virtual TForm MyForm
{
get { throw new NotImplementedException(); }
}

// Otras tantas funcionalidades
}

Asi mismo, tendras una clase UIPageEdit que hereda de UIPageForm, centralizando el comportamiento para editar los elementos de ciertas entidades de negocio.

class UIPageEdit<TForm, TAtom> : UIPageForm<TForm>
where TForm : IFormEdit<TAtom>
where TAtom : ITransaction
{
protected virtual TAtom MyAtom
{
get { throw new NotImplementedException(); }
}

// Otras tantas funcionalidades
}

Ahora, imaginemos que se requiere un nuevo comportamiento, por el cual al editar un Business Entity sobre el Web Form, uno de los datos ingresados por el usuario, sobre la vista o View, deba ser unico. Permitiendo la posibilidad de validar aquello a traves de una opcion especial en el View. Asi como, tambien, estrictamente validable al elegir la opcion Save.

Entonces optaremos por una implementacion como esta:

class UIPageEditValidate<TForm, TAtom> :
UIPageEdit <TForm, TAtom>
where TForm : IFormEdit<TAtom>, IFormValidate
where TAtom : ITransaction
{
// Se varia el comportamiento base sobre este contexto
// para soportar el nuevo requerimiento.
}

Ahora, se requiere que ciertos Web Forms soporten el manejo de informacion historica por cada registro, permitiendo navegar sobre dicho historico a traves del mismo Web Form.

Este nuevo requerimiento podria aplicar tanto a Web Forms que soporten la funcionalidad de IFormValidate como a los que NO.

Aqui empieza nuestro problema, como consecuencia de la NO existencia de una herencia multiple de clases, como lo pregona aquella preciosa teoria de la POO, que tanto añoramos.

Si nuestro C# fuese un lenguaje Orientado a Objetos quimicamente puro, bastaria implementar la funcionalidad requerida sobre una clase llamada Navigator, llamada asi para efectos didacticos, y hacer que los Web Forms que requieran dicha funcionalidad la herenden, a la par que heredan su tradicional clase base, que en el mas alto nivel hereda de Page.

Pero NO es posible, entonces tendremos que buscar una forma de superar esta limitacion tecnica.

En esa busqueda, el uso correcto de Interfaces sera la solucion para este requerimiento, definiendo una interfaz INavigator y una clase llamada ConcreteNavigator.

Haciendo una simple analogia, con fines didacticos, INavigator seria como IEnumerable y a su vez ConcreteNavigator seria como la estructura de control foreach. Aquella que hace algo con la interfaz, que la utiliza en el contexto de la implementacion de un comportamiento de orden superior a la simple definicion de una Interfaz.

En ese sentido, tendriamos algo como esto para la interfaz INavigator:

public interface INavigator
{
event EventHandler PreviousClicked;
event EventHandler CurrentClicked;
event EventHandler NextClicked;

void StatusPrevious( Boolean enabled );
void StatusCurrent( Boolean enabled );
void StatusNext( Boolean enabled );
}

Por su parte la clase ConcreteNavigator tendria una implementacion como esta.

public class ConcreteNavigator<TForm, TAtom>
where TAtom : IHasHistory, IBuildHistory<TAtom>
where TForm : IFormEditHistory<TAtom>
{
StateBag _viewstate;
INavigator _navigator;
TForm _form;
TAtom _atom;

public ConcreteNavigator(
StateBag viewstate,
INavigator navigator,
TForm form,
TAtom atom )
{
this._viewstate = viewstate;
this._navigator = navigator;
this._form = form;
this._atom = atom;
}

public void DoPreviousClicked()
{
// Implementacion del comportamiento requerido
// usando los parametros entregados
// al instanciar ConcreteNavigator
// estando INavigator como parte de ellos.
}

public void DoNextClicked()
{
// Idem
}
public void DoCurrentClicked()
{
// Idem
}
}

Entonces se definiran dos nuevas clases para aquellos Web Forms que requieran utilizar el comportamiento plasmado en ConcreteNavigator.

Asi, tendremos las clase UIPageEditNavigator que hereda de UIPageEdit utilizando una instancia de ConcreteNavigator.

public class UIPageEditNavigator<TForm, TAtom> :
UIPageEdit<TForm, TAtom>
where TAtom : ITransaction, IHasHistory, IBuildHistory<TAtom>
where TForm : IFormEditHistory<TAtom>
{
private ConcreteNavigator<TForm, TAtom> _concrete;

protected virtual INavigator MyNatigator
{
get { throw new NotImplementedException(); }
}

protected override void DefineObjects()
{
this._concrete = new ConcreteNavigator<TForm, TAtom>(
this.ViewState,
this.MyNavigator,
this.MyForm,
this.MyAtom );
}

private void MyNavigator_PreviousClicked( Object sender, EventArgs e )
{
this._concrete.DoPreviousClicked();
}

// Otras tanta funcionalidades que se ajustan con
// lo soportado por ConcreteNavigator
}

De la misma forma, definimos la clase UIPageEditValidateNavigator que hereda de la clase UIPageEditValidate utilizando una instancia de ConcreteNavigator

public class UIPageEditValidateNavigator<TForm, TAtom> :
UIPageEditValidate<TForm, TAtom>
where TAtom : ITransaction, IHasHistory, IBuildHistory<TAtom>
where TForm : IFormEditHistory<TAtom>, IFormValidate
{
private ConcreteNavigator<TForm, TAtom> _concrete;

protected virtual INavigator MyNatigator
{
get { throw new NotImplementedException(); }
}

protected override void DefineObjects()
{
this._concrete = new ConcreteNavigator<TForm, TAtom>(
this.ViewState,
this.MyNavigator,
this.MyForm,
this.MyAtom );
}

private void MyNavigator_PreviousClicked( Object sender, EventArgs e )
{
this._concrete.DoPreviousClicked();
}

// Otras tanta funcionalidades que se ajustan con
// lo soportado por ConcreteNavigator
}


De esta manera, se supera la limitacion tecnica de la NO herencia multiple de clases inexistente en los lenguajes de .NET, como C#.

La clase ConcreteNavigator hace el trabajo pesado de una funcionalidad que luego es posible reflejar en diferentes clases, evitando el COPY & PASTE.

Al NO lograr este nivel de abstraccion, estaremos con seguridad, dejando de utilizar todas las bondades que la tecnologia nos brinda, hablamos de la POO.

Nuestro producto de software muy probablemente este disgregado de codigo, como consecuencia irresponsable del COPY & PASTE por everywhere.

Haciendo de este dolorasemente mantenible y vergonzosamente presentable :)

Monday, March 23, 2009

HTTP Error 404.3 Vista WCF

Cuando intentas hacer el deploy de tu WCF sobre IIS 7 del Windows Vista puede que te encuentres con este mensaje de error:

HTTP Error 404.3 - Not Found
Description: The page you are requesting cannot be served because of the Multipurpose Internet Mail Extensions (MIME) map policy that is configured on the Web server. The page you requested has a file name extension that is not recognized, and is not allowed.


Lo que sucede que es las extensiones .SVC de los EndPoint tu de WCF pues NO se encuentran activados aun para ser reconocidos como peticiones correctas en tu servicio web.

Lo unico que debes hacer es ejecutar la sentencia desde el Command Line: ServiceModelReg -i

Esta permitira que ls SVC puedan ser interpretados y resueltos como esperabas.

El ServiceModelReg se encuentra en el %Windows%\Microsoft.Net\Framework\v3.0\Windows Communication Foundation\

Monday, November 03, 2008

Application Error Global asax

Un mecanismo para gestionar los errores por el lado del servidor en las aplicaciones web ASP.NET, es a traves del:

Application_Error( Object sender, EventArgs e )

Alli dentro debes implementar el codigo que permita capturar la excepcion NO controlada que se produjo dentro de tu aplicativo ASP.NET.

HttpContext.Current.Server.GetLastError().GetBaseException();

Esta linea de codigo, te permitira obtener la excepcion de menor nivel, la que realmente origino la excepcion.

Es una forma de obtener el InnerException por cada una de las excepciones que fueron desencadenadas.

¿Para que usar HttpContext.Current? ¿NO bastaria con utilizar this.Server.GetLastError()?

Pues la respuesta es contundente, un reverendo NO.

En un entorno de desarrollo, en tu PC local donde tu eres amo, dueño y señor de tu develop machine, pues probablemente NO se manifiesten ciertas excepciones, que en un entorno de produccion, con mucha seguridad si se manifiesten, es por ello que es necesario una gestion de errores para tu aplicativo ASP.NET

Pero ademas, debido al mecanismo con el que funciona ASP.NET y el ciclo de manejo propio de este, en un entorno de produccion el this.Server.GetLastError() en ocasiones puede retornar un valor NULL, a pesar que se haya generado la excepcion por el lado del servidor.

HttpContext.Current.Server.GetLastError() si devuelve la informacion solicitada, es decir, la excepcion desencadenada.

Luego de capturar el Exception, puedes hacer lo que quieras con el, enviarlo por email al equipo de soporte a traves del SmtpClient del System.Net.Mail, o guardar el contenido de la excepcion en un archivo de texto a modo de log, o utlizar el EventLog del System.Diagnostics para preservar la excepcion el Visor de Eventos de las Herramientas Administrativas de tu servidor web.

En fin, muchas formas, para todos los gustos y de todos los colores.

Thursday, October 23, 2008

Group box en HTML Web Form

Muchas veces queremos que ciertos controles de WinForms tengan su respectivo equivalente en el mundo de las aplicaciones Web.

Group box es un control que agrupa un conjunto de controles en un formulario windows.

Bien, su correspondiente para el mundo WebForm es el menos conocido elemento HTML: fieldset.

Este elemento tiene un elemento hijo, llamado: legend, para establecer la etiqueta del Group box para HTML en la parte superior izquierda.

En fin, tu codigo HTML para un formulario de ingreso de datos deberia quedar, mas o menos asi:


<fieldset>
<legend>La descripcion de mi Group box</legend>
<table>
<tr>
<td>Campo 1:</td>
<td><asp:textbox /></td>
</tr>
<tr>
<td>Campo 3:</td>
<td><asp:dropdownlist /></td>
</tr>
</table>
</fieldset>


Ya sabes, luego aplica correctamente los estilos CSS para formatear los elementos de formulario en el codigo HTML.

Friday, October 17, 2008

Error connecting to undo manager

Error connecting to undo manager of source file namefile.aspx.designer.cs

Cuando te encuentras desarrollando con el Visual Studio 2008 es muy probable que recibas este mensaje al debuggear tu ASP.NET web application.

Algunas veces al editar -agregar o cambiar la definicion- de algunos elementos de servidor: textbox, dropdownlist, radiobuttonlist, etc. mientras te encuentras trabajando con la interfaz de usuario, este mensaje aparece y NO te dejara realizar tranquilamente tus proximas debuggeos.

Por algun motivo los archivos temporales NO son cambiados con el proximo resultado del rebuild de tu ASP.NET web application.

Una solucion, sin usar muchas neuronas, es simplemente editar los archivos aspx y luego el correspondiente aspx.cs para que la proxima vez dicho mensaje ya NO aparezca.

Lo unico que debes hacer es agregar un simple caracter en blanco y eliminarlo seguidamente, o alguna otra accion relativamente similar.

Al estar los dos archivos editados, el debug actualizar los archivos que antes se encontraban errados y NO se muestra el bendito mensaje.

Lo curioso es que el rebuild de la aplicacion NO da como resultado ningun warning, ni mucho menos un error.

Al parecer seria un bug del Visual Studio 2008 que con el Service Pack 1 sea superado.

Monday, October 13, 2008

Server error in asp.net application

Server error in asp.net application. Configuration error.

<authentication mode="Windows" />

Este mensaje puede ser visto cada vez que haces el deploy en el servidor web y el folder donde has colocado los archivos de tu aplicacion ASP.NET aun NO han sido configurados como un Directorio Virtual con la caracteristica de resolver las paginas aspx.

Para ello debes ir al IIS Management Console, desplegar Default Web Site, hacer clic derecho y seleccionar la opcion Add Application.

Alli indicaras el Alias para tu futura aplicacion y el folder fisico donde se encuentran los archivos que haz desarrollado.

MSDTC on server myserver is unavailable

MSDTC on server myserver is unavailable.

Este mensaje es recibido cuando estas intentando usar las bondades del System.Transactions para gestionar las transacciones de tu aplicativo con acceso a base de datos.

Lo que sucede es que el Microsoft Distributed Transaction Coordinator se encuentra desahiblitado, por lo que cuando utilizas el TransacionScope del System.Transactions este NO te permitira manejar las transacciones como tu esperas.

Para ello debes ingresar al Component Services, expandir el Distributed Transaction Coordinator, hace clic derecho sobre Local DTC, seleccionar la opcion Properties y mostrar la pestaña Security para habilitar el Netwotk DTC Access.

Del mismo modo, habilitar el Allow Inbound y Allow Outbound.

Thursday, October 09, 2008

This configuration section cannot be used

This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault="Deny"), or set explicitly by a location tag with overrideMode="Deny" or the legacy allowOverride="false".

Este mensaje nos muestra el IIS 7 del Windows Vista debido a que NO se han instalado todos los componentes del lado del servidor necesario para resolver ASP.NET.

Windows Vista no activa estos componentes en la instalacion por defecto del IIS.

Para poder visualizar aplicativos ASP.NET sobre una PC con Windows Vista, es importante que tus componentes activados se parezcan a los que se muestran en la imagen.



Windows Vista esta orientado a colocar mas trabas para realizar ciertas tareas que en las versiones anteriores de Windows NO se mostraban.

Una caracteristica de seguridad.

Sunday, December 16, 2007

OnInit ViewState Page

Cuando estas construyendo estructuras complejas sobre el formulario de tu aplicacion ASP.NET tendras algunas dificultades que superar.

Los formularios de facturación u otros relativamente similares traen consigo ciertos retos tecnicos. Pues sobre este convergen una serie de lookups, que han sido ingresados a traves de otros modulos de la aplicacion, para que estos sean seleccionados por el usuario dentro del formulario de la factura.

Muchas veces el formulario muestra controles cuyos elementos estan en función de algunas lookups cuyo numero de items depende del cliente seleccionado u otra variable.

En terminos sencillos no esta determinado en tiempo de diseño la estructura final del formulario.

En ese sentido, se tiene la necesidad de crear elementos en tiempo de ejecución sobre el formulario web, cuya informacion es obtenidad de la base de datos.

Por definicion la construcción de elementos debe estar implementada sobre el OnInit de la pagina. Pero sucede un pequeño detalle, el ViewState se encuentra vacio en ese estado del ciclo de la pagina web. ¿Que hacer?

El ViewState se llena despues del OnInit y antes OnLoad de cada pagina ASPx dentro del mundo ASP.NET.

Cuando te encuentras programando dentro del OnLoad todos y cada uno de los controles que este definidos allí, se encuentran ya con el valor que hayan tenido previamente, de forma automatica. Es la naturaleza de ASP.NET.

Un solucion inmediata, para superar la dificultad del ViewState en el OnInit, pasa por recordar la forma tradicional de como obtener informacion del post de la pagina.

String something = this.Request.Form( "_something" );

Vale recalcar que _something es un elemento dentro del formulario de mi pagina.

De esta manera podras recuperar el valor de algun control, que dentro de la implementacion que estes realizando, te implique algun grado de sensibilidad con respecto a los demas elementos dentro del formulario.

Tuesday, August 24, 2004

Handling Events and Delegates

Una de las cosas mas interesantes que trajo consigo la llegada de ASP.NET, aya por el año 2002, fue la posibilidad de encapsular logica e interfaz de usuario a traves de los ahora muy conocidos Web User Controls.

Esto nos ha permitido reutilizar dichas funcionalidades a lo largo de nuestro projecto web, permitiendo reducir algunas lineas de codigo, haciendo mas sencillo y flexible los posteriores mantenimientos.

Sin embargo, la manera profesional de trabajar con los Web User Controls consiste en definir eventos, a partir de los cuales se envian mensajes hacia la entidad que los contenga, que generalmente son Pages u otros Web User Controls.

La intencion esta en que las paginas ASP.NET, asuman el rol de gestores de los eventos de los diferentes Web User Controls que contengan dentro de el.

Les alcanzo un pequeño ejemplo que podria mostrar lo sencillo que es definir un evento para un Web User Control, haciendo uso de un Delegate que define cierta firma a la que se ha de ajustar el evento que deseamos publicar.


Archivo .ASCX


<%@ Control Language="c#" AutoEventWireup="false" Codebehind="SearchEngine.ascx.cs" Inherits="DemoWebAppCSharp.Event.SearchEngine"%>
<asp:TextBox id="_text" runat="server"></asp:TextBox>
<asp:Button id="_go" runat="server" Text="Go"></asp:Button>

--- End File ---

Este archivo define como se ha de estructurar la interfaz del usuario para este Web User Control en particular.

Algo tan simple como un TextBox y un Button para un sencillo buscador.


Archivo .ASCX.CS



namespace DemoWebAppCSharp.Event
{
using System;
using UserInterface;

/// <summary>
/// Definimos el delegate que define la firma con la que publicaremos el evento
/// </summary>
public delegate void ClickedEventHandler( Object sender, ClickedEventArgs e );

/// <summary>
/// Este Web User Control hereda de un clase que permite encapsular
/// cierta funcionalidad similar para todos los controles de
/// nuestro projecto web
/// </summary>
public class SearchEngine : CustomControl
{
protected System.Web.UI.WebControls.TextBox _text;
protected System.Web.UI.WebControls.Button _go;

/// <summary>
/// Publicamos el evento Clicked del tipo ClickedEventHandler
/// el delegate que habiamos definido lineas arriba
/// </summary>
public event ClickedEventHandler Clicked;

/// <summary>
/// Es requisito sobre-escribir este metodo para todo Web User Control
/// que diga decente :)
/// </summary>
/// <param name="e">Uhmmm... obvio!!!</param>
protected override void OnInit( EventArgs e )
{
base.OnInit( e );

// Enganchamos el metodo SearchEngine_Load al evento Load del control
this.Load += new EventHandler(SearchEngine_Load);

// Enganchamos el metodo Go_Click al evento Click del Button
this._go.Click += new EventHandler( this.Go_Click );
}

private void SearchEngine_Load(object sender, System.EventArgs e)
{
}

/// <summary>
/// Este metodo estara enganchado al evento Click del Button
/// que se muestra en la interfaz del usuario del Web User Control
/// y soltara el evento propio de este control para dicha accion Clicked.
/// </summary>
/// <param name="sender">Una instancia de quien ocasiona el evento</param>
/// <param name="e">Informacion del evento</param>
private void Go_Click(
Object sender,
EventArgs e )
{
// Lanzamos el evento Clicked. Alguien lo gestionara y sino no hay problema
if( this.Clicked != null )
this.Clicked( this, new ClickedEventArgs( this._text.Text ) );
}
}

/// <summary>
/// Esta clase encapsula cierta informacion particular para el evento Clicked
/// del Web User Control que estamos implementando SearchEngine.
/// Lo unico que enviara es el texto que fue ingresado en el TextBox del
/// control para que se realize la accion esperada del usuario.
/// </summary>
public class ClickedEventArgs : EventArgs
{
private String _text;

public ClickedEventArgs( String Text )
{
this._text = Text;
}

public String Text
{
get { return this._text; }
set { this._text = value; }
}
}
}

--- End File ---

Aqui implementamos la manera en como nuestro Web User Control ha de interactuar con el usuario. En funcion a las opciones que permitamos, realizaremos ciertas acciones a traves de Eventos. En este caso particular, al hacer 'click' sobre el Button lanzaremos el evento Clicked propio de este Web User Control.

Para ello tenemos que engancharnos al evento Click del Button y dentro de su implementacion disparar nuestro propio evento, Clicked.

Debido a que tenemos la intencion de enviar cierta informacion a traves del evento. La manera de llevarlo a cabo es usando el definiendo una clases que herede de EventArgs. En nuestro caso particular ClickedEventArgs. El cual solo cuenta con una propiedad llamada Text, donde colocaremos el dato que haya ingresado el usuario sobre el TextBox del Web User Control.

El evento que publicaremos debe ajustarse al Delegate que hemos definido, ClickedEventHandler. Este delegate se ajusta a cierta firma, propia para este evento.

No hubiera sido necesario definir este Delegate si no hubieramos visto conveniente una firma particular para el evento o enviar informacion a traves de el.

En ese caso, hubiera sido suficiente definir el evento Clicked del tipo EventHandler, el delegate por default. De ese modo, ya no seria necesario enviar informacion adicional como parte del evento. Entonces tendrias que usar EventArgs.Empty en el momento de lanzar el evento Clicked.



Archivo .ASPX



<%@ Register TagPrefix="Custom" TagName="SearchEngine" Src="SearchEngine.ascx" %>
<%@ Page language="c#" Codebehind="Holder.aspx.cs" AutoEventWireup="false" Inherits="DemoWebAppCSharp.Event.Holder" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>Holder</title>
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<table>
<tr>
<td>
<Custom:SearchEngine id="_engine" runat="server"></Custom:SearchEngine></td>
</tr>
<tr>
<td>
<asp:TextBox id="_show" runat="server"></asp:TextBox>
</td>
</tr>
</table>
</form>
</body>
</HTML>

--- End File ---

Este archivo contiene el Web User Control y un TextBox.

La intencion con propositos de testeo, es la de pintar el texto ingresado por el TextBox del Web User Control sobre el TextBox de la Page.


Archivo .ASPX.CS



namespace DemoWebAppCSharp.Event
{
using System;
using UserInterface;

/// <summary>
/// Esta es la pagina que contendra el Web User Control que hemos implementado
/// Por cierto, hereda de una pagina que define cierta funcionalidad similar
/// para todas las paginas de nuestro proyecto web.
/// </summary>
public class Holder : CustomPage
{
protected System.Web.UI.WebControls.TextBox _show;
protected SearchEngine _engine;

/// <summary>
/// Del mismo modo, que para el Web User Control es requisito para
/// toda Page que se diga decente.
/// </summary>
/// <param name="e"></param>
protected override void OnInit( EventArgs e )
{
base.OnInit( e );

// Enganchamos el metodo Holder_Load al evento Load de la page
this.Load += new EventHandler(Holder_Load);

// Enganchamos el metodo Engine_Clicked al evento Clicked de Web User Control
this._engine.Clicked += new ClickedEventHandler( this.Engine_Clicked );
}

private void Holder_Load(object sender, System.EventArgs e)
{
}

/// <summary>
/// Este es el metodo que se enganchara al evento Clicked del Web User Control
/// y que dentro de su implementacion realizara la tarea que creemos
/// conveniente. Pero dicha accion estara dentro de la jurisdiccion de la Page.
/// </summary>
/// <param name="sender">La instancia de quien genera el evento</param>
/// <param name="e">Informacion propia de este evento</param>
private void Engine_Clicked(
Object sender,
ClickedEventArgs e )
{
// Algo tan sencillo como pintar el texto ingresado por el control sobre otro TextBox
this._show.Text = e.Text;
}
}
}

--- End File ---

Para llevar a cabo dicho testeo, tenemos que subscribirmos al evento Clicked del Web User Control. De esa manera, coger la informacion que trae consigo el evento para poder realizar una accion. En este caso, ClickedEventArgs nos brinda el dato que el usuario hubiera ingresado sobre el TextBox del Web User Control. El cual nosotros cogeremos para poder colocarlo sobre el TextBox de la Page.

Thursday, July 01, 2004

Dynamic WebForms in ASP.NET

Muchas veces, segun los requerimientos del cliente, se da la necesidad de poder construir -en ciertos casos- formularios webs y grillas de datos de manera dinamica, a partir de cierta logica para definir la estrucura que se ha de presentar a traves de la interfaz del usuario.

Les muestro un ejemplo que puede darles las primeras pautas de como poder llevar a cabo estas tareas.

Archivo .ASPX

<form id="SimpleDynamic" method="post" runat="server">
<asp:PlaceHolder id="_holder" runat="server"></asp:PlaceHolder><br/>
<asp:Button id="_submit" runat="server" Text="Submit"></asp:Button><br/>
<asp:Label id="_post" runat="server"></asp:Label><br/>
<asp:Label id="_message" runat="server"></asp:Label><br/>
</form>

--- End File ---


Notese el uso del control PlaceHolder, que permitira alojar en el diferentes tipos de controles en tiempo de ejecucion, para su posterior presentacion e interaccion.


Archivo .CS

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace DemoWebAppCSharp.Custom
{
/// <summary>
/// Descripción breve de Dynamic.
/// </summary>
public class SimpleDynamic : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Button _submit;
protected System.Web.UI.WebControls.Label _message;
protected System.Web.UI.WebControls.Label _post;
protected System.Web.UI.WebControls.PlaceHolder _holder;

private void Page_Load(object sender, System.EventArgs e)
{
if( ! this.IsPostBack )
{
// Llenando el DropDownList de datos
this.FillList();
}
}

private void DefineForm()
{
TextBox Name = new TextBox();
Name.ID = "_name";

DropDownList List = new DropDownList();
List.ID = "_list";
List.AutoPostBack = true;

// Enganchando una funcionalidad al evento del DropDownList
List.SelectedIndexChanged += new EventHandler( this.List_SelectedIndexChanged );

// Colocando los controles en el PlaceHolder
this._holder.Controls.Add( Name );
this._holder.Controls.Add( List );
}

private void FillList()
{
// Solicitando el DropDownList al PlaceHolder
DropDownList List = (DropDownList)this._holder.FindControl( "_list" );

List.Items.Add( "-- Elija una opción --" );
List.Items.Add( "Simple Geek");
List.Items.Add( "Spoutlet" );
List.Items.Add( "Frabriq" );
}

// Funcion que se enganchara al evento correspondiente del DropDownList
private void List_SelectedIndexChanged( Object sender, EventArgs e )
{
DropDownList List = (DropDownList)sender;
this._post.Text = "Item selected: " + List.SelectedValue;
}

#region Código generado por el Diseñador de Web Forms
override protected void OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// Método necesario para admitir el Diseñador. No se puede modificar
/// el contenido del método con el editor de código.
/// </summary>
private void InitializeComponent()
{
this._submit.Click += new System.EventHandler(this.Submit_Click);
this.Load += new System.EventHandler(this.Page_Load);

this.DefineForm();
}
#endregion

// Posteando la informacion
private void Submit_Click(object sender, System.EventArgs e)
{
TextBox Name = (TextBox)this._holder.FindControl( "_name" );
DropDownList List = (DropDownList)this._holder.FindControl( "_list" );

this._message.Text = Name.Text + " : " + List.SelectedValue;
}
}
}

--- End File ---

Explicando, se contruira un TextBox y un DropDownList que esta seteado como AutoPostBack. De esta manera, cada vez que el usuario seleccione un item en la lista la pagina se posteara inmediatamente.

Revisando el metodo DefineForm. En este punto se muestra algo interesante, que es el enganche de un metodo hacia el evento de un control que se crea on-fly, es decir en tiempo de ejecucion.

Este metodo -el DefineForm- es llamado desde el InitializeComponent, que permite ejecutar operaciones sobre el metodo sobre-escrito OnInit. Que por cierto, es el lugar adecuado para poder colocar elementos dinamicos en la pagina.

Vale decir, que por el hecho de ser llamado desde dicho evento -el Init- la construccion dinamica de los elementos se realiza, por cada peticion de la pagina. Pues este evento siempre es solicitado por cada PostBack, al igual que el evento Load.

Es trabajo del modelo de objetos de ASP.NET, mantener el estado de los controles en la pagina por cada posterior PostBack.

Se remarca tambien, que los itemes adheridos al DropDownList, solo se realiza una sola vez, y esta operacion se lleva a cabo dentro del evento Load, validando el IsPostBack.

Se quiere dar a entender de esta manera, que la estructura del formulario es independiente de los datos que se plasmaran en dicha estructura. El evento Init es el lugar correcto para construir estructuras dinamicas y el Load para llenar datos en esas estructuras.

El metodo List_SelectedIndexChanged, solo hace un casting del objeto sender. Pues es justamente en esta variable que se encuentra una referencia al control que provoco el posting de la pagina, es decir el DropDownList.

Finalmente, el metodo Submit_Click, solicita al PlaceHolder, los controles que se adherieron al el, para presentar los valores que el usuario haya ingresado o seleccionado.

En conclusion, la intencion es poder construir cualquier estructura en el webform, a partir de una metadata, que brinde toda la informacion necesaria para la presentacion de dicha estructura y los datos que han de colocarse en el.

Les recomiendo descargar una interesante herramienta que he constuido, y que justamente parte de lo aqui mostrado es el 'core' de dicho producto.

http://groups.msn.com/mugperu/general.msnw?action=get_message&mview=1&ID_Message=11466

En dicho lugar, podran encontrar mayor informacion de como instalar el software y como trabajar con el.

Comparto tambien estos lugares de donde podran encontran mayor informacion sobre los eventos asociados a una pagina, el orden en el que son llamados y las actividades que se realizan en cada uno de ellos.

The ASP.NET Page Object Model
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/aspnet-pageobjectmodel.asp

The ASP.NET Page Life Cycle
http://www.15seconds.com/issue/020102.htm

Al igual que esto, la construccion de un DataGrid dinamico en funcion a las columnas y al contenido en cada DataGridItem, sean estos datos u otros controles, tambien es posible realizarlo, de esta forma.

Javier Luna
-- Software Architect and blogger too ;)
-- Movil: (51-1) 9-731-7187

"Indigo: Ports, Messages, Channels and more..."