El control DateTimePicker permite al usuario seleccionar una fecha y hora y mostrar ese valor con un formato especificado. Para cambiar el color seleccionado, el método depende de la tecnología utilizada: en Windows Forms es posible personalizar el control mediante propiedades del calendario o mediante un control heredado, mientras que en Xamarin.Forms para Android se debe aplicar un estilo al diálogo o crear un renderer personalizado cuando se utiliza Visual Material.
El color de fondo del calendario desplegable se controla con CalendarMonthBackground, y los colores de sus elementos principales se pueden establecer con CalendarForeColor, CalendarTitleBackColor, CalendarTitleForeColor y CalendarTrailingForeColor. Sin embargo, establecer BackColor no tiene ningún efecto en la apariencia de DateTimePicker.
Qué representa el color seleccionado
Cuando se usa para representar una fecha, el DateTimePicker aparece en dos partes: una lista desplegable con una fecha representada en texto y un calendario que aparece al hacer clic en la flecha abajo situada junto a la lista. El calendario tiene el aspecto del MonthCalendar, que se puede usar para seleccionar varias fechas.
El DateTimePicker se usa para permitir al usuario seleccionar una fecha y hora, y mostrar esa fecha y hora en el formato especificado. El control facilita el trabajo con fechas y horas, ya que controla una gran cantidad de la validación de datos automáticamente.
Para usar un control de botón de número, también conocido como control ascendente, para ajustar el valor de fecha y hora, establezca la propiedad ShowUpDown en true. El calendario no se mostrará cuando se seleccione el control.
La fecha y hora se pueden ajustar seleccionando cada elemento individualmente y usando los botones arriba y abajo para cambiar el valor.
Colores disponibles en Windows Forms
El control proporciona propiedades específicas para modificar la apariencia del calendario desplegable. Estas propiedades permiten diferenciar el fondo del mes, el título, el texto del calendario y las fechas que pertenecen al mes anterior o posterior.
| Propiedad | Función |
|---|---|
| CalendarMonthBackground | Obtiene o establece el color de fondo del mes natural. |
| CalendarForeColor | Obtiene o establece el color de primer plano del calendario. |
| CalendarTitleBackColor | Obtiene o establece el color de fondo del título del calendario. |
| CalendarTitleForeColor | Obtiene o establece el color de primer plano del título del calendario. |
| CalendarTrailingForeColor | Obtiene o establece el color de primer plano de las fechas finales del calendario. |
| CalendarFont | Obtiene o establece el estilo de fuente aplicado al calendario. |
| ForeColor | Obtiene o establece el color de primer plano del control. |
El control también dispone de la propiedad CustomFormat, que permite establecer una cadena de formato de fecha y hora personalizada, y de la propiedad Format, que obtiene o establece el formato de la fecha y hora que se muestra en el control.
Ejemplo de configuración del calendario
En este ejemplo se establecen los colores del calendario desplegable y se configura el formato mostrado por el control:
dateTimePicker1.CalendarMonthBackground = Color.White;
dateTimePicker1.CalendarForeColor = Color.Black;
dateTimePicker1.CalendarTitleBackColor = Color.MediumSlateBlue;
dateTimePicker1.CalendarTitleForeColor = Color.White;
dateTimePicker1.CalendarTrailingForeColor = Color.Gray;
dateTimePicker1.CustomFormat = "dd/MM/yyyy";
El calendario desplegable es la parte cuya apariencia puede modificarse directamente mediante las propiedades específicas del control. En cambio, el campo principal que muestra la fecha seleccionada utiliza la implementación nativa de DateTimePicker.
Por qué BackColor no cambia el control
La propiedad BackColor obtiene o establece un valor que indica el color de fondo del control DateTimePicker. Sin embargo, establecer el objeto BackColor no tiene ningún efecto en la apariencia de DateTimePicker.
Para establecer el color de fondo del calendario desplegable de DateTimePicker, se debe utilizar la propiedad CalendarMonthBackground.
La propiedad BackColor aparece como una propiedad sobrescrita, pero está marcada para no mostrarse normalmente en el diseñador:
public override System.Drawing.Color BackColor { get; set; }
El comportamiento visual del control depende de su implementación nativa, por lo que asignar un color al objeto base no garantiza que el área visible se vuelva a pintar con ese color.
Personalizar el DateTimePicker mediante herencia
Si se necesita cambiar el color del área principal que muestra la fecha seleccionada, una solución consiste en crear una clase que herede de DateTimePicker y controlar el repintado del fondo.
El siguiente ejemplo crea un control derivado llamado BaseDateTimePicker y sustituye el tratamiento del mensaje WM_ERASEBKGND para dibujar manualmente el color de fondo:
using System;
using System.Drawing;
using System.Windows.Forms;
public class BaseDateTimePicker : DateTimePicker
{
private Color _BackColor = SystemColors.Window;
public override Color BackColor
{
get { return _BackColor; }
set
{
_BackColor = value;
Invalidate();
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg == CInt(&H14))
{
Graphics g = Graphics.FromHdc(m.WParam);
g.FillRectangle(New SolidBrush(_BackColor), ClientRectangle);
g.Dispose();
return;
}
base.WndProc(ref m);
}
}
En C#, la comprobación equivalente del mensaje utiliza el valor hexadecimal 0x0014, correspondiente a WM_ERASEBKGND:
if (m.Msg == (int) 0x0014 /* WM_ERASEBKGND */)
{
Graphics g = Graphics.FromHdc(m.WParam);
g.FillRectangle(new SolidBrush(_BackColor), ClientRectangle);
g.Dispose();
return;
}
La propiedad personalizada almacena el color, lo devuelve cuando se consulta y llama a Invalidate() cuando se modifica para provocar el repintado del control.
Versión en VB.NET
La misma técnica puede implementarse en VB.NET mediante una clase que hereda de DateTimePicker:
Public Class BaseDateTimePicker
Inherits DateTimePicker
Private _BackColor As Color = SystemColors.Window
Public Sub New()
MyBase.New()
Me.Format = DateTimePickerFormat.Short
Me.Width = 88
Me.FontHeight = 25
Me.BackColor = Color.LightCyan
End Sub
Public Overrides Property BackColor() As Color
Get
Return _BackColor
End Get
Set(ByVal Value As Color)
_BackColor = Value
Invalidate()
End Set
End Property
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = CInt(&H14) Then
Dim g As Graphics = Graphics.FromHdc(m.WParam)
g.FillRectangle(New SolidBrush(_BackColor), ClientRectangle)
g.Dispose()
Return
End If
MyBase.WndProc(m)
End Sub
End Class
En este ejemplo, el constructor establece el formato corto, el ancho, la altura de la fuente y el color inicial LightCyan. Después de compilar el proyecto, el control personalizado puede utilizarse en el formulario como cualquier otro control de Windows Forms.
Crear un DateTimePicker con apariencia personalizada
Otra alternativa consiste en heredar del control tradicional y redibujar completamente su superficie. Esta estrategia permite controlar el fondo, el texto, el borde, el icono del calendario y la indicación visual de que el calendario está abierto.
Puedes heredar un UserControl, pero en este caso simplemente heredaremos del control DateTimePicker de la librería Windows Forms y así ampliaremos su funcionalidad y modificaremos la apariencia.
Además de esta manera el control es mucho más ligero y barato en tiempo de ejecución, y también fácil y rápido de personalizar mediante el evento Paint.
Importar las librerías
Para hacer cualquier control personalizado, es necesario importar la librería Windows Forms y la librería de dibujos:
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
Heredar del control original
La clase personalizada puede comenzar con una declaración sencilla:
public class RJDatePicker : DateTimePicker
{
}
Definir los campos de apariencia
En la clase se pueden declarar campos para la apariencia del botón y asignar sus valores predeterminados, como el color de fondo, el color de texto, el color de borde y el tamaño de borde.
También se necesitan otros campos para almacenar valores del control, como un campo para obtener o establecer si el calendario está desplegado:
private Color skinColor = Color.MediumSlateBlue;
private Color textColor = Color.White;
private Color borderColor = Color.PaleVioletRed;
private int borderSize = 0;
private bool droppedDown = false;
private Image calendarIcon = Properties.Resources.calendarWhite;
private RectangleF iconButtonArea;
private const int calendarIconWidth = 34;
private const int arrowIconWidth = 17;
Agregar las propiedades públicas
Las propiedades permiten exponer los campos de apariencia y actualizar el dibujo cada vez que se modifica un color:
public Color SkinColor
{
get { return skinColor; }
set
{
skinColor = value;
if (skinColor.GetBrightness() >= 0.8F)
calendarIcon = Properties.Resources.calendarDark;
else
calendarIcon = Properties.Resources.calendarWhite;
this.Invalidate();
}
}
public Color TextColor
{
get { return textColor; }
set { textColor = value; this.Invalidate(); }
}
public Color BorderColor
{
get { return borderColor; }
set { borderColor = value; this.Invalidate(); }
}
public int BorderSize
{
get { return borderSize; }
set { borderSize = value; this.Invalidate(); }
}
Cuando el fondo es claro, la propiedad SkinColor cambia el icono del calendario por calendarDark; cuando el fondo es oscuro, utiliza calendarWhite.
Configurar el constructor
En el constructor se especifica el estilo y el comportamiento del control. En este caso, el control será pintado por el usuario y no por el sistema operativo.
También se establece el tamaño mínimo del control, de esta manera se puede cambiar el alto del DateTimePicker:
public RJDatePicker()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.MinimumSize = new Size(0, 35);
this.Font = new Font(this.Font.Name, 9.5F);
}
Controlar la apertura y el cierre del calendario
Es necesario anular los métodos de evento DropDown y CloseUp para establecer el estado del calendario desplegable.
También se puede anular el método de evento KeyPress para indicar que el evento fue controlado y evitar cambiar el valor del DateTimePicker con las teclas numéricas:
protected override void OnDropDown(EventArgs eventargs)
{
base.OnDropDown(eventargs);
droppedDown = true;
}
protected override void OnCloseUp(EventArgs eventargs)
{
base.OnCloseUp(eventargs);
droppedDown = false;
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
e.Handled = true;
}
Redibujar el control
El método OnPaint permite dibujar la superficie, el texto, el resaltado del icono desplegable, el borde y el icono del calendario:
protected override void OnPaint(PaintEventArgs e)
{
using (Graphics graphics = this.CreateGraphics())
using (Pen penBorder = new Pen(borderColor, borderSize))
using (SolidBrush skinBrush = new SolidBrush(skinColor))
using (SolidBrush openIconBrush = new SolidBrush(Color.FromArgb(50, 64, 64, 64)))
using (SolidBrush textBrush = new SolidBrush(textColor))
using (StringFormat textFormat = new StringFormat())
{
RectangleF clientArea = new RectangleF(0, 0, this.Width - 0.5F, this.Height - 0.5F);
RectangleF iconArea = new RectangleF(clientArea.Width - calendarIconWidth, 0, calendarIconWidth, clientArea.Height);
penBorder.Alignment = PenAlignment.Inset;
textFormat.LineAlignment = StringAlignment.Center;
graphics.FillRectangle(skinBrush, clientArea);
graphics.DrawString(" " + this.Text, this.Font, textBrush, clientArea, textFormat);
if (droppedDown == true)
graphics.FillRectangle(openIconBrush, iconArea);
if (borderSize >= 1)
graphics.DrawRectangle(penBorder, clientArea.X, clientArea.Y, clientArea.Width, clientArea.Height);
graphics.DrawImage(calendarIcon, this.Width - calendarIcon.Width - 9, (this.Height - calendarIcon.Height) / 2);
}
}
El fondo utiliza skinColor, el texto utiliza textColor y el borde utiliza borderColor. Cuando el calendario está abierto, el área del icono se dibuja con un color semitransparente.
Cambiar el cursor sobre el icono
También se pueden anular los métodos HandleCreated y MouseMove para cambiar el cursor cuando el puntero del mouse esté sobre el botón del icono del DateTimePicker.
Esto es necesario porque no siempre es posible saber si el puntero está sobre el icono desplegable, ya que el tamaño del botón puede variar según el ancho del control:
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
int iconWidth = GetIconButtonWidth();
iconButtonArea = new RectangleF(this.Width - iconWidth, 0, iconWidth, this.Height);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (iconButtonArea.Contains(e.Location))
this.Cursor = Cursors.Hand;
else
this.Cursor = Cursors.Default;
}
Si no se desea anular los métodos de evento, los eventos pueden suscribirse desde el constructor.
Obtener el ancho del botón
El ancho del botón del icono se puede calcular midiendo el texto y comparando su anchura con el espacio disponible:
private int GetIconButtonWidth()
{
int textWidh = TextRenderer.MeasureText(this.Text, this.Font).Width;
if (textWidh <= this.Width - (calendarIconWidth + 20))
return calendarIconWidth;
else
return arrowIconWidth;
}
Recursos gráficos del control personalizado
Para mostrar un icono propio, primero se agregan los iconos CalendarIconDark y CalendarIconWhite a los recursos del proyecto. Los iconos pueden utilizarse para mantener el contraste entre el fondo y el botón del calendario.
La clase personalizada utiliza inicialmente el recurso Properties.Resources.calendarWhite y cambia a Properties.Resources.calendarDark cuando el brillo del fondo alcanza el valor establecido.
Limitaciones del control redibujado
El control personalizado permite cambiar el color de fondo, el color del texto, el color del borde, el tamaño del borde y el icono del calendario. También permite mostrar un resaltado cuando el calendario está desplegado.
Sin embargo, todavía puede ser necesario optimizar algunos aspectos. Por ejemplo, el control no tiene soporte para cambiar la hora; con más trabajo se puede agregar esta funcionalidad o crear otro control selector de hora.
Color del diálogo DatePicker en Xamarin.Forms para Android
En Xamarin.Forms, Xamarin.Android y Android Studio es sencillo cambiar los colores del diálogo del DatePicker agregando líneas al archivo styles.xml del proyecto Android.
El siguiente estilo indica al tema que utilice AppCompatDialogStyle para el diálogo del selector de fecha y establece el color de resaltado en #2196F3:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<style name="MyTheme" parent="MyTheme.Base">
</style>
<style name="MyTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
</style>
<style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">#2196F3</item>
</style>
</resources>
Conflicto entre DatePicker y Visual Material
La propiedad Visual de los elementos visuales permite cambiar su apariencia para que los controles de formulario tengan un estilo Material Design u otro definido por el desarrollador o por un tercero.
El problema aparece cuando los controles utilizan Visual Material y el diálogo del DatePicker necesita un color personalizado. En ese caso, los estilos definidos en styles.xml no siempre pueden cambiar los controles que no tienen Visual="Default".
Una solución consiste en implementar un renderer propio en el proyecto Android. No es necesario crear un renderer desde cero únicamente para cambiar el color del diálogo; se puede heredar el renderer de Xamarin.Forms y crear el diálogo con el estilo deseado.
Renderer personalizado para Visual Material
El siguiente código registra un renderer para los controles DatePicker que utilizan VisualMarker.MaterialVisual y hereda de MaterialDatePickerRenderer:
using Android.App;
using Android.Content;
using Xamarin.Forms;
using Xamarin.Forms.Material.Android;
[assembly: ExportRenderer(typeof(DatePicker), typeof(MiProyecto.Droid.Renderer.DatePickerRenderer), new[] { typeof(VisualMarker.MaterialVisual) })]
namespace MiProyecto.Droid.Renderer
{
class DatePickerRenderer : MaterialDatePickerRenderer
{
public DatePickerRenderer(Context context) : base(context) { }
protected override DatePickerDialog CreateDatePickerDialog(int year, int month, int day)
{
DatePicker view = Element;
var dialog = new DatePickerDialog(Context, Resource.Style.AppCompatDialogStyle, (o, e) =>
{
view.Date = e.Date;
((IElementController)view).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
}, year, month, day);
return dialog;
}
}
}
El atributo assembly especifica que el renderer se utilizará en los controles DatePicker y que se aplicará cuando el control utilice Visual Material.
La clase hereda de MaterialDatePickerRenderer, por lo que mantiene el comportamiento del renderer de Xamarin.Forms y puede recibir los cambios que surjan en la librería.
El método CreateDatePickerDialog se sobrescribe para crear el diálogo utilizando Resource.Style.AppCompatDialogStyle.
Definir el estilo del diálogo
El estilo del diálogo puede definirse en styles.xml con los colores deseados:
<style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="android:datePickerStyle">#7cd2de</item>
<item name="android:textColor">#7cd2de</item>
<item name="colorControlHighlight">#15daf8</item>
<item name="colorControlActivated">#7cd2de</item>
<item name="colorPrimary">#7cd2de</item>
<item name="colorPrimaryDark">#7cd2de</item>
<item name="android:yearListSelectorColor">#7cd2de</item>
</style>
<style name="DatePickerDialogStyle" parent="@android:style/Widget.Material.Light.DatePicker">
<item name="android:headerBackground">#7cd2de</item>
</style>
Con esto se define un estilo para los diálogos llamado AppCompatDialogStyle, utilizando los colores elegidos para el texto, el encabezado, los controles activos y los elementos resaltados.
Después de agregar el estilo y compilar el proyecto, el diálogo del DatePicker puede mostrar los colores configurados incluso cuando el control utiliza Visual Material.
Elección del método adecuado
- Para cambiar los colores del calendario desplegable en Windows Forms, utiliza CalendarMonthBackground, CalendarForeColor, CalendarTitleBackColor, CalendarTitleForeColor y CalendarTrailingForeColor.
- Para cambiar el fondo del campo principal que muestra la fecha, crea una clase que herede de DateTimePicker y controla el repintado de WM_ERASEBKGND.
- Para personalizar completamente el control, habilita ControlStyles.UserPaint y redibuja la superficie mediante OnPaint.
- Para Xamarin.Forms en Android sin Visual Material, define el estilo del diálogo en styles.xml.
- Para Xamarin.Forms con Visual Material, hereda de MaterialDatePickerRenderer, sobrescribe CreateDatePickerDialog y aplica el estilo personalizado al diálogo.

tags: #color #seleccionado #datetimepicker