Teclado en pantalla con WPF

ScreenKeyboardEn algunas de nuestras aplicaciones es posible que necesitemos controlar de forma muy fina la entrada de datos por parte del usuario, o que no dispongamos de un teclado físico. Con WPF podemos crear de forma sencilla un teclado en pantalla que se maneje con el ratón o de forma táctil. Lo primero de todo será crear nuestra aplicación WPF y añadirle un TextBox y varios botones.

La aplicación resultante la podéis ver aquí a la izquierda…os servirá para daros cuenta de que esto no va de diseño, sino de programación. Además al final del post hay un link para que podáis descargárosla y comprobar lo sencilla que es.

Es interesante utilizar un TextBox en lugar de otro control de texto como una Label o un TextBlock, puesto que así tendremos soporte para mostrarle al usuario la posición del cursor (caret) mientras edita el texto. Una vez hecho esto, necesitamos gestionar una serie de temas para que el teclado funcione correctamente. Lo primero de todo, manejaremos el click de los botones para añadir texto al TextBox:

private void KeyboardPanel_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)e.Source;
Text.AppendText(button.Content.ToString());
}

Una vez hecho esto, solo nos quedaría hacer que el campo de texto muestre la posición del cursor mientras se esta editando. Para ello, en el constructor de nuestra ventana añadiremos estas dos líneas después del InitializeComponent:

this.InitializeComponent();

Text.CaretIndex = 0;
Text.Focus();

La primera línea sitúa la posición del cursor en el 0, ya que de no hacerlo el TextBox no la gestionará correctamente. La segunda línea le da el foco de tal manera que muestre el cursor. Si probamos nuestra aplicación ahora veremos que aun nos queda un problema por resolver. Al hacer click en cualquiera de los botones para añadir texto, el TextBox perderá el foco, perdiendo así la funcionalidad del cursor. Para evitar esto hay varios métodos: hacer que los botones no puedan recibir el foco (Focusable=false), hacer que en cada click el TextBox recupere el foco…Lo que haremos será algo muy sencillo: evitar que el TextBox pierda el foco. Añadimos al constructor la siguiente linea:

Text.PreviewLostKeyboardFocus += new KeyboardFocusChangedEventHandler(Text_PreviewLostKeyboardFocus);

Manejando el evento PreviewLostKeyboardFocus podemos realizar las acciones que deseemos antes de que el control pierda el foco. Y lo que deseamos es…

void Text_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
e.Handled = true;
}

Marcar el evento como manejado. Con algo tan simple como esto evitamos que el control pierda realmente el foco, y tenemos nuestro teclado listo para su uso.


Rock Tip. Poca gente hace canciones y videos tan divertidos como Electric Six. Lo mejor de todo es que además suenan realmente bien…y no tienen ninguna relación con el tema del post, para no perder la costumbre 😉 Cierran hoy Electric Six con Danger! High Voltage. Enjoy!!!

Los secretos del casting al descubierto

Visto lo animada que estuvo la discusión en el anterior post sobre los diferentes casts en C#, no podía dejar pasar la oportunidad de explicar un poco en que consistía el acertijo. Como algunos comentabais en los comentarios, las dos operaciones, as y cast explicito, son distintas en funcionalidad y también internamente. Vamos a ver por qué:

concursante = (Concursante)o;

En esa línea, intentaremos convertir el objeto o a Concursante. Si podemos, devolveremos un objeto de tipo concursante, si no podemos lanzaremos una excepción.

concursante = o as Concursante;

En esta otra línea, comprobaremos primero si el objeto es convertible a Concursante. Si lo es, lo convertiremos y devolveremos, si no lo es devolveremos null. Internamente, el operador as se convierte en una instrucción de tipo isinst, mientras que el cast directo se convierte en una instrucción castclass. Pero dejémonos de teoría y vamos a lo que nos interesa. Con un sencillo programa podemos comprobar cual de los dos es más rápido. Por ejemplo, así:

Concursante concursante = null;
object o = new Concursante();

ArrayList array = new ArrayList();

Stopwatch watch = new Stopwatch();
Console.WriteLine("Begin casting");

for (int i = 0; i < 100; i++)
{
    watch.Start();

    for (int j = 0; j < 10000000; j++)
    {
        concursante = (Concursante)o;
        concursante.Nombre = "Rosa";
    }

    watch.Stop();

    array.Add(watch.ElapsedMilliseconds);
    watch.Reset();
}

float ms = 0;

for (int i = 0; i < array.Count; i++)
{
    ms += float.Parse(array[i].ToString());
}

ms = ms / array.Count;

Console.WriteLine("Median elapsed time: {0} ms", ms);

Si, el programa tiene un poco mas de miga, pero tiene su sentido. Hemos de recordar que un casting, de una forma u otra, es una operación con un coste muy, muy pequeño por si sola. Para poder medirla con un poco de precisión, realizaremos diez millones de casting cien veces, y calcularemos la media. Ejecutado en mi portátil mientras escribo esto, el resultado es de unos cuantos milisegundos (40, 50…) en cada uno de los casos. Recordemos que estamos hablando del tiempo de ejecución de 10.000.000 operaciones. Lo curioso es que al ejecutar el mismo código en modo release o en modo debug, con o sin optimizaciones, nos dará resultados distintos. Qué es lo que esta pasando?

Esa será la respuesta que dejaré sin responder, por una sencilla razón. Mi compañero Luis Guerrero me ha prometido realizar un interesantísimo post sobre este mismo tema, metiéndose hasta el fondo del asunto con WinDBG. Así pues, le paso el testigo 😉


Rock Tip. Ya he nombrado por aquí a los finlandeses Turisas, pero es justo que después de verles la semana pasada en directo por segunda vez, vuelva a recordarlos. Con vosotros, Battle Metal. Enjoy!!