Acerca de...
El equipo
Encuestas
Política de privacidad
WinTablets

Encuesta

¿Cual crees que triunfará?

Ver Resultados

Cargando ... Cargando ...

últimas entradas importantes

Categorías

Archivos

09
Ago 2014
Desarrollorfogdevtrucos

Desarrollo: Control de edición con soporte para claves de acceso y hint

A veces te piden unas cosas que son de cajón pero tu plataforma parece que haya sido pensada con el extremo opuesto a la cabeza, y te toca hacer alguna viguería con lo que tienes disponible. Vamos aquí a explicar cómo tener un control con la funcionalidad descrita. Vamos a crear un control personalizado por herencia.

Si no sabes qué diferencia hay entre heredar y agregar respecto a los controles de Windows, ya os hice una pequeña introducción en una entrada anterior.

Puede ocurrirte que un control existente de Windows no cumpla todas tus expectativas. En mi caso la situación se produjo cuando necesitaba un control de edición que tuviera de forma simultánea un hint (eso que aparece como fondo cuando el campo está vacío y que se borra nada más meter texto) y la introducción de una contraseña (lo que tecleas se va convirtiendo en asteriscos).

Windows Phone (y Windows) tiene un control llamado PasswordBox que te presenta un cuadro de edición para introducir una clave de acceso y que sigue todas las reglas para ello. Pero no tiene hint.

Windows Phone tiene otro control llamado PhoneTextBox que es un campo de edición extendido que soporta, entre otras muchísimas cosas, el uso de un hint. Pero no tiene la opción de poner asteriscos (u otro carácter) como sustitutivo de lo tecleado. Si os fijáis en el artículo que os he enlazado más arriba, pese a que se llena la boca con la palabra password, en ningún lado pone lo de los asteriscos.

Lo cierto es que la situación termina resultando un poco kafkiana (me gusta el palabro, ¿qué pasa?). Si estuviéramos desarrollando con C++ quizás podríamos heredar una nueva clase a partir de las dos existentes, pero en .NET eso es un animal mitológico.

La solución pasa por crearnos un control que sea la mezcla de los dos. O bien podemos añadir al PasswordBox el tema del hint o bien añadir al PhoneTextBox el de los asteriscos. Si buscáis por internet hay varias soluciones implementadas, pero desde mi punto de vista todas ellas son demasiado alambicadas y complejas, sobre todo las que utilizan un control de usuario agregado.

Nosotros vamos a realizar una solución de compromiso y vamos a heredar directamente de PhoneTextBox para crear un nuevo control llamado PhonePasswordBox. Este nuevo control sólo tendrá una propiedad extra y un evento. Más conciso no se puede ser.

Primero os pongo el código y luego os lo explico:

    public partial class PhonePasswordBox:PhoneTextBox
    {
        private string m_password;

        public string Password
        {
            get { return m_password; }
        }

        public PhonePasswordBox():base()
        {
            m_password = String.Empty;
            this.TextChanged += PhonePasswordBox_TextChanged;
        }

        void PhonePasswordBox_TextChanged(object sender, system.Windows.Controls.TextChangedEventArgs e)
        {
            this.TextChanged -= PhonePasswordBox_TextChanged;
            var pl = m_password.Length;
            var tl = Text.Length;
            if (pl > tl)
                m_password = m_password.Substring(0, tl);
            else if (pl < tl)            
            {
                var car=Text[tl-1];
                m_password += car;                 
                if (tl > 1)
                {
                    Text = Text.Substring(0, tl - 2);
                    Text += "*";
                    Text += car;
                }
                Select(tl, 0);
            }
            this.TextChanged += PhonePasswordBox_TextChanged;
        }
   }

¿Cómo lo veis? Yo suelo poner el constructor justo debajo de la declaración de la clase, pero aquí he cambiado el orden por mor de sencillez.

Primero creamos una cadena miembro que va a almacenar lo que el usuario ha teclado, en conformidad con el control PasswordBox. Su propiedad, de sólo lectura, nos devuelve el valor.

Luego tenemos el constructor, que previamente a su construcción llama al de la clase padre para luego asignar un valor no nulo a lo que el usuario va a teclear y lo más importante, añadimos un nuevo método miembro al evento TextChanged de nuestra clase, que como hemos heredado de PhoneTextBox, se corresponderá a su propio evento, que se dispara cada vez que el texto editado cambia.

Y luego tenemos el evento en sí, el que nos va a cambiar la letra por el asterisco. Y no, no es tan fiero como parece. Básicamente lo que hace es medir el tamaño de la cadena que se está mostrando y el guardado. Si el tecleado es menor, se que se ha borrado un carácter, lo que hacemos en el de la clave. Si es mayor, es que se ha añadido uno nuevo, y tenemos que cambiar el anterior por un asterisco y poner el nuevo en el campo de edición, y añadirlo a nuestra cadena de clave.

La línea del Select() nos pone el cursor al final del cambio de edición, pero solo cuando se haya añadido algo.

¿Qué falta? La desconexión y conexión del evento cuando entramos y salimos de él. ¿Alguien se imagina el motivo? … Efectivamente, en cuanto toquemos la propiedad Text del control, se volverá a disparar de forma recursiva. Con el código que os muestro no pasa nada, pero en otras situaciones sí y haría que nuestra aplicación generase una excepción por desbordamiento de pila.

El único problema aquí sería que el evento se llamaría recursivamente varias docenas de veces ante cada pulsación del usuario, lo que si bien no afecta a nada, sí que vuelve la edición un poco más lenta.

Una vez que tenemos el código escrito, usar el control es tan sencillo como con cualquier otro. Sólo tenemos que añadir en la página XAML el espacio renombres y luego usarlo en referencia a él:

 

 xmlns:customControls=«clr-namespace:MiPrograma.UI.CustomControls»

<customControls:PhonePasswordBoxName=«PasswordTextBox»MaxLength=«30»Hint=«{Binding Path=LocalizedResources.PasswordHint, Source={StaticResource LocalizedStrings}}»InputScope=«Password»/>

 

¿Os mola?

A mi no, porque es un compromiso entre funcionalidad y diseño. Respecto a lo que sería un control en condiciones es una chapuza. Por ejemplo, el método Text sigue estando disponible para el usuario, con lo que podría usarlo para leer una cadena de asteriscos en lugar del valor real tecleado.

Podríamos ocultar la propiedad conforme a lo explicado antes, pero entonces se producen efectos laterales no deseados porque lo más seguro es que el propio control padre, PhoneTextBox, tampoco tenga su código limpio en exceso. Si queréis podéis hacer la prueba. Usando el ejemplo de la semana pasada ocultad el campo y haced llamadas a “base.Text” en el evento en lugar de “Text”. Que san Apapurcio os pille confesados. :-P

Otra chapuza es quitar y poner el evento. La solución a esto es redefinir un nuevo evento con el mismo nombre y ocultar el antiguo, y luego redireccionar el que se haya podido suscribir el usuario al antiguo y nosotros usar el nuevo, poniendo un bloqueo para evitar que se dispare el del usuario cuando nosotros estemos trasteando con el campo de texto, lo que nos lleva al punto anterior sobre la ocultación de dicho campo…

Pero como no vamos a vender el control, lo que hemos hecho nos basta.

Por RFOG | 13 Comentarios | Enlaza esta entrada
contacto@wintablet.info tema WinTablet.info por Ángel García (Hal9000)