Marc Rubiño

ASP.NET, C#, AJAX.NET, JavaScript, etc.

septiembre 2007 - Artículos

Custom Control III

 

Para acabar con esta serie de artículos sobre como crear un control compuesto, veremos como personalizar el renderizado y poder comprobar en tiempo de diseño como quedara realmente nuestro control en la Página.

En los artículos anteriores vimos:

  1. Custom Control I : Como crear los controles secundarios y sus eventos.
  2. Custom Control II: Las Propiedades necesarias para personalizar nuestro control.

Ya tenemos nuestro control terminado y funcionando, pero personalmente me da mucha rabia esos controles que los arrastramos a nuestro formulario y solo te muestran el nombre y no te puedes hacer a la idea de como quedará ralmente en la página.

 

Me parece que el control desluce mucho y no nos ayuda a la hora de diseñar nuestra página.

Como podemos solucionar esto???

Muy sencillo, controlando el renderizado del control y sabiendo en cada momento lo que tenemos que pintar en la pantalla.

protected override void Render(HtmlTextWriter writer)
{
    EnsureChildControls();

    #region Titulo

    writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Table);
    writer.RenderBeginTag(HtmlTextWriterTag.Tr);

    writer.AddAttribute(HtmlTextWriterAttribute.Align, "Left", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssAnterior, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);

    if (string.IsNullOrEmpty(this.ImgAnterior))
    lAnterior.RenderControl(writer);
    else
    ImAnterior.RenderControl(writer);


    writer.RenderEndTag();

    writer.AddAttribute(HtmlTextWriterAttribute.Align, "Center", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssTitulo, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);
    lTitulo.RenderControl(writer);
    writer.RenderEndTag();

    writer.AddAttribute(HtmlTextWriterAttribute.Align, "Right", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssSiguiente, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);

    if (string.IsNullOrEmpty(this.ImgAnterior))
    lSiguiente.RenderControl(writer);
    else
    ImSiguiente.RenderControl(writer);

    writer.RenderEndTag();
    writer.RenderEndTag();

    writer.RenderBeginTag(HtmlTextWriterTag.Tr);

    #endregion Titulo

    #region Calendario

    writer.AddAttribute(HtmlTextWriterAttribute.Colspan, "3", false);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);

    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssCeldaCalendario, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Table);
    int col = this.Calendarios / this.Filas;
    int mes = 0;
    int posCal = 0;

    for (int y = 0; y < this.Filas; y++)
    {
        writer.RenderBeginTag(HtmlTextWriterTag.Tr);
        for (int x = 0; x < col; x++, mes++, posCal++)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            LCalendarios[posCal].RenderControl(writer);
            writer.RenderEndTag();
        }
        writer.RenderEndTag();
    }

    writer.RenderEndTag();
    writer.RenderEndTag();

    #endregion Calendario

    #region Pie

    writer.RenderBeginTag(HtmlTextWriterTag.Tr);
    writer.AddAttribute(HtmlTextWriterAttribute.Colspan, "3", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssPie, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);
    writer.RenderEndTag();
    writer.RenderEndTag();

    writer.RenderEndTag();
    writer.RenderEndTag();

    #endregion Pie 

}

Estudiaremos es te código por partes.

  • Lo primero que tenemos que hacer es asegurarnos que los controles secundarios se han creado para poder utilizarlos.

 

    EnsureChildControls();
  • Utilizaremos el HtmlTextWriter para crear la estructura del control ( Una tabla, filas y celdas para poder colocar los calendarios en su sitio)

    writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Table);
    writer.RenderBeginTag(HtmlTextWriterTag.Tr);

    writer.AddAttribute(HtmlTextWriterAttribute.Align, "Left", false);
    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssAnterior, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);

  • Pintaremos los calendarios dependiendo de la cantidad de filas que tengamos configurado

    #region Calendario

    writer.AddAttribute(HtmlTextWriterAttribute.Colspan, "3", false);
    writer.RenderBeginTag(HtmlTextWriterTag.Td);

    writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssCeldaCalendario, false);
    writer.RenderBeginTag(HtmlTextWriterTag.Table);
    int col = this.Calendarios / this.Filas;
    int mes = 0;
    int posCal = 0;

    for (int y = 0; y < this.Filas; y++)
    {
        writer.RenderBeginTag(HtmlTextWriterTag.Tr);
        for (int x = 0; x < col; x++, mes++, posCal++)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            LCalendarios[posCal].RenderControl(writer);
            writer.RenderEndTag();
        }
        writer.RenderEndTag();
    }

    writer.RenderEndTag();
    writer.RenderEndTag();

    #endregion Calendario

Después de controlar el renderizado de nuestro control podremos disfrutar de todo su potencial en tiempo de diseño y todas las modificaciones que hagamos en sus propiedades se reflejarán automáticamente en la pantalla.

 

 

Bueno yo creo que ya tenemos el control bastante decente no?, se que podría tener más funcionalidades y muchísimo más vistoso, pero como ejemplo de como crear un control compuesto me parece suficiente ;-).

Si queréis Trastear el proyecto os lo podéis bajar gratuitamente y si tenéis dudas me lo podéis comentar sin problemas.

Espero veros por estos barrios.

  Proyecto CustomCalendar.

User Control I    -    User Control II  -  User Control III

 

Posted: sep 22 2007, 05:11 por Marc Rubiño | con 1 comment(s)
Archivado en:
Custom Control II

 

En el artículo anterior Custom Control I ya vimos como empezar a crear un control compuesto y de que clases teníamos que heredar para dotarle de toda la funcionalidad necesaria.

Ahora seguiremos creando las propiedades para que el usuario pueda personalizar la apariencia entre otras cosas.

La primera propiedad que veremos será la de poder elegir el Nº de calendarios que queremos ver en el control.

[Bindable(true)]
[Category("Datos")]
[DefaultValue(1)]
[Description(" Número de Calendarios a mostrar")]
 public int Calendarios
 {
      get
      {
          object s = ViewState["Calendarios"];
          return ((s == null) ? 1 : (int)s);
      }
      set
      {
          ViewState["Calendarios"] = value;
      }
  }

Como podéis comprobar es una propiedad muy simple que mantiene la información entre postbacks en el estado del control, por defecto siempre mostrará 1 calendario. Siguiendo esta técnica podremos crear otras propiedades como:

  • Nº de Filas int: Nº de filas en los que se repartirán los calendarios.
  • Fecha inicial DateTime: Fecha inicial para calcular el mes inicial del control.
  • Fechas Seleccionadas list<DateTime>: Lista de Datetimes con las fechas seleccionadas en los Calendarios.

Como se verán estas propiedades en tiempo de diseño.

Siguiendo con las propiedades crearemos otro grupo para el formato del control.

[Bindable(true)]
[Category("Formato")]
[DefaultValue(typeof(System.Drawing.Color), "Green")]
[Localizable(true)]
[Description("Color de Fondo de la fecha seleccionada")]
public System.Drawing.Color ColorFondo
{
    get
    {
        object s = ViewState["ColorFondo"];
        return ((s == null) ? System.Drawing.Color.Green : (System.Drawing.Color)s);
    }

    set
    {
        ViewState["ColorFondo"] = value;
    }
}
  • Titulo string: Titulo que se mostrará en la cabecera del control.
  • TxTSiguiente string: Texto para mostrar el link Siguiente.
  • ImgSiguiente string: Si preferimos podemos mostrar una imagen como botón Siguiente.
  • TxtAnterior string: Texto para mostrar el link Siguiente.
  • ImgAnterior string: Si preferimos podemos mostrar una imagen como botón Siguiente.
  • ColorFondo Color: Color de fondo de la fecha seleccionada por el usuario.
  • ColorTexto Color: Color del texto de la fecha seleccionada por el usuario.
  •  

    Como podéis comprobar es muy fácil crear nuestras propiedades que son soportadas en tiempo de diseño, el entorno nos permite trabajar con algunos tipos como Colors y enumeraciones.

    Para terminar con las propiedades de nuestro control porque no le damos la posibilidad de personalizar los estilos!!.

    private TableItemStyle selectedDayStyle;
    
    [Category("Css")]
    [DefaultValue("")]
    [Description("Clase CSS día seleccionado.")]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public TableItemStyle SelectedDayStyle
    {
        get
        {
            if (selectedDayStyle == null)
            selectedDayStyle = new TableItemStyle();
    
            if (IsTrackingViewState)
            ((IStateManager)selectedDayStyle).TrackViewState();
    
            return selectedDayStyle;
        }
    }

    Para esta propiedad utilizamos la Clase TableItemStyle que representa las propiedades de estilo de un elemento de un control que se representa como un TableRow o un TableCell.

     

    Para dotar de todos los estilos necesarios a nuestro control crearemos estas propiedades:

  • OtherMonthDayStyleEstilo de los días que no pertenecen al mes actual.
  • SelectedDayStyle: Estilo de los días seleccionados.
  • WeekendDayStyle: Estilo para los días de fin de semana.
  • TodayDayStyle: Estilo para el día de hoy.
  • DayHeaderStyle: Estilo para los nombres de los días de la semana.
  • SelectorStyle: Estilo para los selectores de semana y de mes.
  • DayStyle: Estilo para los días del calendario.
  • TitleStyle: Estilo para la cabecera del control. 
  • Bueno, ya tenemos todos los controles secundarios que necesitamos y las propiedades para el buen funcionamiento del control, pero aún nos quedan un par de cosas que podemos hacer para que nuestro calendario funcione al 100%.

    Por eso lo dejaremos para el siguiente artículo, espero que os haya gustado.

    User Control I    -    User Control II  -  User Control III

     

    Posted: sep 22 2007, 12:14 por Marc Rubiño | con no comments
    Archivado en:
    Custom Control I

     

    Me da la sensación que estamos muy acostumbrados a buscar recursos en Internet  y no pararnos a pensar que a veces no están complicado adaptar los controles existentes a nuestras necesidades.

    Basándome en esta suposición publicare un par de artículos para explicar como crear un control personalizado  que he necesitado en un proyecto.

     

    Al final publicare el proyecto para que podáis probar.

    El control.

    Para una aplicación de reservas por Internet necesitábamos tener un calendario lo suficientemente adaptable para su reutilización.
    Por ejemplo el uso de dos calendarios “el típico desde hasta” o el uso de n calendarios para entrar un rango de Temporadas.

    Primero crearé un proyecto Web para probar el control en su salsa y después crearé un proyecto de clases para el control personalizado.

     

    Crearemos una nueva clase llamada Acicalendar que nos servirá para nuestro nuevo control.

     

    [ToolboxData("<{0}:AciCalendar runat=server></{0}:AciCalendar>")]

     public class AciCalendar : CompositeControl

     

    Como crearemos un control compuesto por más de un control y no unos basaremos en un control en concreto heredaremos de CompositeControl que implementa la configuración básica requerida por los controles Web que contienen controles secundarios.

     

     public abstract class CompositeControl : WebControl, INamingContainer, ICompositeControlDesignerAccessor

    •  
      • WebControl: proporciona las propiedades, métodos y eventos que comparten todos los controles de servidor Web.
      • INamingContainer: Cualquier control que implemente esta interfaz, crea un nuevo espacio de nombres en el cual se garantiza que todos los atributos del Id. de controles secundarios son únicos dentro de una aplicación.
      • ICompositeControlDesignerAccessor: Proporciona una interfaz para permitir a un diseñador de controles compuestos volver a crear los controles secundarios de su control asociado en tiempo de diseño llamando al método RecreateChildControls del control en tiempo de diseño. El diseñador asociado debe derivar de CompositeControlDesigner.

    Nuestro control estará formado por: 

    • List<Calendar> LCalendarios: Una lista de Calendarios para tener un número ilimitado de calendarios contenidos en el control.
    • LinkButton lAnterior: Link para mover los calendarios para los meses anteriores.
    • ImageButton ImAnterior: Podremos elegir entre el link o una imagen para mover los calendarios a los meses anteriores.
    • Label lTitulo: Título que mostrará el control.
    • LinkButton lSiguiente: Link para mover los calendarios para los meses siguientes.
    • ImageButton ImSiguiente: como antes podemos elegir entre un link o una imagen para navegar entre los calendarios.

    Una vez que tenemos la clase preparada podemos empezar a crear nuestro control, para eso sobrescribimos el método CreateChildControls que es el encargado de crear los controles contenidos por el nuestro.

     

        this.Controls.Clear();

     

        #region Variables

     

          lTitulo = new Label();

          lSiguiente = new LinkButton();

          ImSiguiente = new ImageButton();

          lAnterior = new LinkButton();

          ImAnterior = new ImageButton();

          LCalendarios = new List<Calendar>();

     

        #endregion Variables

     

    Como podéis comprobar lo primero que hacemos es limpiar la colección de controles secundarios e instanciar los nuevos.

     

    Seguidamente comenzamos con la navegación  y el título del control.

     

    #region Título

     

    lTitulo.Text = this.Titulo;

    lAnterior.Text = this.TxtAnterior;

    lAnterior.Click += new EventHandler(lAnterior_Click);

    ImAnterior.ImageUrl = this.ImgAnterior;

    ImAnterior.ImageAlign = ImageAlign.Baseline;

    ImAnterior.Click += new ImageClickEventHandler(ImgAnterior_Click);

     

    lSiguiente.Text = this.TxtSiguiente;

    lSiguiente.Click += new EventHandler(lSiguiente_Click);

    ImSiguiente.ImageUrl = this.ImgSiguiente;

    ImSiguiente.ImageAlign = ImageAlign.Baseline;

    ImSiguiente.Click += new ImageClickEventHandler(ImgSiguiente_Click);

     

    #endregion Título

     

    Dos cosas destacan de este código, la primera es que utilizaremos propiedades que veremos más adelante para formatear nuestros controles secundarios.

     

    lTitulo.Text = this.Titulo;

     

    Y la segunda es que enlazamos el evento Click del control secundario a un evento del control.

     

    lAnterior.Click += new EventHandler(lAnterior_Click);

     

    Porque tenemos que hacer esto???

     

    [Category("AciCalendar")]

    [Description("Evento de anterior posterior mes.")]

    public event EventHandler MonthChanged;

     

    protected void OnMonthChanged(EventArgs e)

    {

        if (MonthChanged != null)

        {

            MonthChanged(this, e);

        }

    }

     

    private void lSiguiente_Click(object sender, EventArgs e)

    {

       this.FechaInicial = this.FechaInicial.AddMonths(this.Calendarios);

       CargarCalendarios();

      

       //lanzamos el evento del control

       OnMonthChanged(EventArgs.Empty);

    }

     

     

    Como NO tenemos acceso directo a los eventos generados por los controles secundarios, tenemos dos opciones o hacer que estos eventos se propaguen a nuestro control o crear un evento nuevo en el control que sea lanzado por el evento de un control secundario. Uff!!

     

    En este caso he creado un evento nuevo MonthChanged que es lanzado cuando se hace Click en el link Siguiente. De esta forma podemos controlar cuando el usuario hace click en nuestro control para avanzar los meses necesarios en nuestro control.

     

     

    private void lSiguiente_Click(object sender, EventArgs e)

    {

       ...

       //lanzamos el evento del control

       OnMonthChanged(EventArgs.Empty);

    }

     

    Ahora seguimos con la creación de los controles secundarios ;)

     

    #region Calendarios

     

    int col = this.Calendarios / this.Filas;

    int mes = 0;

     

    for (int y = 0; y < this.Filas; y++)

    {

        for (int x = 0; x < col; x++, mes++)

        {

           #region calendario

     

           Calendar cal = new Calendar();

           cal.ShowNextPrevMonth = false;

           cal.VisibleDate = FechaInicial.AddMonths(mes);

           cal.SelectionChanged +=

           new EventHandler(this.cal_SelectionChanged);

           cal.DayRender += new DayRenderEventHandler(cal_DayRender);

           CargarFechas(cal);

     

           //le aplicamos los estilos.

           cal.CssClass = this.CssCalendario;

           cal.SelectorStyle.CopyFrom(this.SelectorStyle);

           cal.DayHeaderStyle.CopyFrom(this.DayHeaderStyle);

           cal.WeekendDayStyle.CopyFrom(this.WeekendDayStyle);

           cal.TodayDayStyle.CopyFrom(this.TodayDayStyle);

           cal.SelectedDayStyle.CopyFrom(this.SelectedDayStyle);

           cal.TitleStyle.CopyFrom(this.TitleStyle);

           cal.DayStyle.CopyFrom(this.DayStyle);

           cal.OtherMonthDayStyle.CopyFrom(this.OtherMonthDayStyle);

           cal.SelectionMode = CalendarSelectionMode.DayWeekMonth;

           cal.SelectedDayStyle.BackColor = this.ColorFondo;

           cal.SelectedDayStyle.ForeColor = this.ColorTexto;

     

           LCalendarios.Add(cal);

           this.Controls.Add(cal);

     

           #endregion calendario

     

    #endregion calendarios

     

    this.Controls.Add(lAnterior);

    this.Controls.Add(ImAnterior);

    this.Controls.Add(lTitulo);

    this.Controls.Add(lSiguiente);

    this.Controls.Add(ImSiguiente);

     

     

     

     

    Como podéis comprobar podemos elegir el Nº de columnas y el Nº de filas que queramos que utilice el control y el Nº total de calendarios a repartir.

     

    Entonces creará los calendarios necesarios, le enlaza los eventos SelectionChanged, DayRender y establecerá propiedades y estilos para los controles.

     

    Finalmente agregará el calendario a la lista de calendarios y también a la colección de controles secundarios de nuestro control.

     

    Bueno ya tenemos la base de nuestro control creado con esto, espero que os esté interesando el tema y no os haya aburrido demasiado.

     

    Dejaremos la parte final del proyecto para el siguiente artículo espero veros.

     

    Posted: sep 19 2007, 12:16 por Marc Rubiño | con no comments
    Archivado en:
    Nuevo vecino en la Comunidad !!!

     

    En realidad no soy tan nuevo [;)] pero hasta ahora no me había planteado la posibilidad de tener mi propio rinconcito.

     Pero bueno, todas las aportaciones al conocimiento común espero que sean bien acogidas por todos.

    Para empezar y para que podáis conocerme un poquito mejor os dejo un par de links a unos artículos que publiqué en elguille y que espero que aún sean útiles para todos:

    • Código de Barras

      Creación e Impresión de Código de Barras utilizando Fuentes Personalizadas ( True Type )

    Bueno espero veros pronto a todos por este sitio.