¿Mostrar el cursor en un TextBox?

Mucho tiempo sin poner nada en el blog. Demasiado.

Probablemente siga durante una temporada sin poder contribuir de forma regular, pero vamos a intentar de que sea información útil (subrayemos intentar).

Durante los últimos meses he estado trabajando en un proyecto personal con un grupo de amigos. Espero que pronto pueda subir una entrada en este blog, de momento diré que estoy un poco aTareado.

Al grano. ¿Qué pasa con los cursores (caret)?

Dejadme que os sitúe: Windows 8 App Store y sus pequeñas cosillas (con el tiempo estoy descubriendo que no le faltan pequeñas cosillas)

Estamos en fase de pruebas y, repentinamente me llega una pequeña incidencia (pequeña por ser trivial en apariencia): “Oye, Juan, que el cursor no se ve cuando el campo de texto coge el foco y empiezo a escribir en él”

image

Vaya, claro. ¡Cómo va a verse si tiene un feo color negro sobre un fondo azul oscuro! Nada, no nos preocupemos. Bastará con dar con la propiedad o estilo que afecte al cursor (caret), y de paso al aspa de borrado, y la cambiamos de negro a blanco. ¿Verdad?

Pues no.

Dicho sea de paso que el aspa de borrado sí puede tocarse, pero no ocurre lo mismo con el cursor, tal y como puede verse aquí.

La referencia tiene casi medio año en el momento en que esta entrada se escribe y, sigo sin encontrar nada que lo contradiga en la procelosa web.

Así que. ¿Qué hacer?

Pues bien habrá que conformarse (de momento) con una solución un tanto desagradable: Cambiemos el fondo del cuadro de texto cuando el mismo tenga el foco.

Dicho y hecho.

Vamos a hacerlo de dos formas. Una utilizando únicamente XAML y la otra con unas pocas líneas de código que escribiremos por detrás.

Con la ayuda de Blend

Esta forma es muy sencilla y rápida. Primero, con la ayuda de Visual Studio (más porque lo tengo ya abierto que porque sea la única forma). Pulsaré con el botón derecho sobre el control y crearé una copia de la plantilla del mismo.

image

Una vez creada la copia podemos seguir en Visual Studio, pero os recomiendo que paséis a Blend donde podremos editar las propiedades según el estado visual de manera más sencilla (el paso descrito en el párrafo anterior también puede realizarse en Blend, por supuesto).

image

Pues bien, ya tenemos Blend mostrándonos la plantilla del control.

image

Centrémonos en la esquina superior izquierda, donde se enumeran los estados visuales del control. Concretamente, nos fijaremos en el estado visual “Focused”.

Una vez seleccionamos dicho estado, Blend se pone a “grabar” los cambios que realicemos sobre las propiedades del control. Concretamente nos centraremos en dos elementos del control de texto: “BackgroundElement” y “ContentElement”

image

En mi caso he creado un color blanco con transparencia, he creado un recurso para dicho color (luego veremos por qué) y se lo he asignado al fondo “background”.

Lo mismo para el color del texto (originalmente blanco). He creado un color negro, he creado un recurso para dicho color (de nuevo, un poco más adelante veremos por qué) y se lo he asignado al color de la fuente “foreground”.

image

Como veis, con muy poco esfuerzo hemos conseguido el objetivo secundario (no he cambiado el color del cursor, pero al menos consigo que se vea):

image

image

Con la ayuda de unas líneas de código

Sin embargo, no todos los controles de texto en la aplicación son TextBox, en algún punto del desarrollo decidimos introducir unos controles de terceros sobre los que la creación de la plantilla con Blend no nos proporciona el resultado adecuado. ¿Qué hacer entonces?

Escribamos un poquito de código, que no se nos caerán los anillos.

Lo primero es crear una clase que herede de la clase que representa el control de texto.

public sealed class MiTextBoxExt : TextBoxExtDeOtros
    {
        #region Constructor

        static MiTextBoxExt()
        {
            ResourceDictionary dict = new ResourceDictionary();
            dict.Source = new Uri("ms-appx:///Themes/TextBoxTemplate.xaml");

            FOCUSED_BACKGROUND_COLOR = dict["FocusedTextBoxBackground"] as SolidColorBrush;
            FOCUSED_FOREGROUND_COLOR = dict["FocusedTextBoxForeground"] as SolidColorBrush;
        }

        /// <summary>
        /// Constructor por defecto de la clase
        /// </summary>
        public MiTextBoxExt(): base()
        {
        }
        #endregion

        #region Miembros Protegidos

        protected override void OnGotFocus(Windows.UI.Xaml.RoutedEventArgs e)
        {
            base.OnGotFocus(e);

            _previousBackground = this.Background as SolidColorBrush;
            _previousForeground = this.Foreground as SolidColorBrush;

            this.Background = FOCUSED_BACKGROUND_COLOR;
            this.Foreground = FOCUSED_FOREGROUND_COLOR;
        }

        protected override void OnLostFocus(Windows.UI.Xaml.RoutedEventArgs e)
        {
            base.OnLostFocus(e);

            this.Background = _previousBackground;
            this.Foreground = _previousForeground;
        }

        #endregion

        #region Miembros Privados

        //Brush 
        private static readonly SolidColorBrush FOCUSED_BACKGROUND_COLOR;
        private static readonly SolidColorBrush FOCUSED_FOREGROUND_COLOR;

        private SolidColorBrush _previousForeground;
        private SolidColorBrush _previousBackground;
        #endregion
    }

Como se puede ver la solución es muy sencilla.

  1. Recuperamos del diccionario de recursos los colores definidos en el método anterior (así, si cambiamos el estilo no será necesario cambiar el código)
  2. Cuando el control toma el foco se almacenan los colores de fondo y fuente originales para luego poderlos restaurar con comodidad.
  3. Cuando el control pierde el foco, simplemente restaura los colores previamente almacenados.

Conclusión

Pues que no hemos solucionado el problema, pero hemos encontrado una forma de evitarlo.

¿Sabe alguien alguna forma mejor?