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 Titulo: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.