Curiosidad (o no) en WinForms y focus…

Os cuento una curiosidad de la que me acabo de dar cuenta ahora mismo… Un funcionamiento, como mínimo curioso en Winforms… siempre entendiendo como curioso que “yo no lo sabía y mi no encaja en mi (poco) sentido común”.

El titular sensacionalista sería: Control desactivado recibe el focus.

La realidad es que si en el evento “Leave” de un control, desactivamos el siguiente control que debe recibir el foco, este control NO recibe el foco (como es de esperar) pero su evento “Enter” se ejecuta…

… y no sólo eso! La siguiente vez que pulsemos TAB (o que con el mouse pongamos el foco en cualquier otro control incluyendo el propio control que tiene el foco), el evento “Leave” del control deshabilitado se ejecutará!

A partir de aquí, el focus ya no “entrará” ninguna otra vez en el control deshabilitado (no lanzará más eventos Enter/Leave).

Si “quereis” jugar con esta curiosidad, aquí os dejo un pequeño programilla, con el que comprobar in-situ esta curiosidad…

Link al programa para testear la curiosidad. Compilado con VS2008 y .NET 3.5 SP1, bajo Windows XP.

Y una capturilla para que lo veais (al tener la checkbox activa, se deshabilita el textbox que está a la derecha del TextBox con fondo naranja-suave (que he llamado en un alarde de pereza textBox3)):

image

Evidentemente, si alguien tiene más información al respecto y sabe el porqué de esta causa… pues le estaré muy agradecido!!! 😉

Saludos!

Recaptcha, ASP.NET MVC, SimpleModal y un poco de Ajax…

Hola família!

En los dos últimos posts (http://geeks.ms/blogs/etomas/archive/2009/04/14/mostrar-un-formulario-modal-con-asp-net-mvc-y-ajax.aspx y http://geeks.ms/blogs/etomas/archive/2009/04/15/mostrar-un-formulario-modal-con-asp-net-mvc-y-ajax-ii.aspx) comenté como he usado SimpleModal, junto con ASP.NET MVC para mostrar un formulario modal y comunicarlo via AJAX con nuestro controlador.

En mi caso, este formulario era el formulario de registro… y para evitar los spammers (tal y como diría cierto ministro, yo no digo que haya que prohibir el spam, pero yo lo prohibiría :p) decidí usar un captcha.

Paso 1: El captcha

Nevagando por esas webs de dios, llegué a Recaptcha un sitio donde ofrecen de forma totalmente gratuita un servicio de captchas bastante interesante… Empecé a ver como integrar Recaptcha en ASP.NET MVC… la API no es muy complicada y hay gente que ha hecho utilidades para casi todos lenguajes de servidor (como se puede ver en la página de recursos de Recaptcha). Aunque no aparece en esta página de recursos, googleando un poco más vi que Andrew Wilinksi ya había hecho una API para recaptcha usando ASP.NET MVC Beta 1: RecaptchaMvc. En su blog anuncia que es para la Beta 1 de ASP.NET MVC, así que me descargue el código fuente desde http://recaptchamvc.codeplex.com/SourceControl/ListDownloadableCommits.aspx (el changeset 27773) y lo compilé contra la versión final de ASP.NET MVC.

El resultado es que NO compila, debido al uso de la interfaz IValueProvider y la clase DefaultValueProvider, que existían en la Beta y que fueron eliminadas. La solución es bastante simple, en el fichero controllerextensions.cs, cambiar las referencias a IValueProvider por IDictionary<string, ValueProviderResult>. Al final del post adjunto el fichero modificado para que compile contra la versión final. Este es el único fichero que debe ser modificado.

RecaptchaMvc incorpora métodos de extensión para HtmlHelper, de forma que poner un Captcha es tan sencillo como:

<%= Html.Recaptcha(this.Model) %>

Entiendendo que estamos en una vista tipada cuyo tipo de modelo es IRecaptchaModelState, una interfaz proporcionada por RecaptchaMvc, que también proporciona la clase RecaptchaModelState que implementa la interfaz. Dicha clase espera nuestra clave pública de Recaptcha.

Paso 2: El formulario modal…

Y este post se habría terminado ya, si en mi caso yo no estuviese mostrando el captcha en un formulario modal via SimpleModa. A ver, a grandes rasgos el problema es que la llamada a Html.Recaptcha, genera un tag <script>. Si mostramos un formulario usando Ajax, lo que hacermos básicamente es rellenar un <div> via javascript con cierto contenido html. En este caso, los tags <script> son ignorados, por lo que el captcha no se ve…

La solución? Una vez esté cargado el formulario modal, llamar via Ajax a recaptcha y mostrar entonces el captcha. Por suerte esto es posible porque la gente de recaptcha ofrecen una API Ajax, porque si no… buf! En la página de la API cliente de recaptcha se describe como funciona la API de recaptcha si queremos usar Ajax. Hay también un ejemplo y la verdad es que es bastante sencillo… De hecho son dos pasos muy sencillos:

  1. Incluir el fichero de script recaptcha_ajax.js
  2. Llamar a la función Recaptcha.Create. Esta función espera la clave pública, el ID del objeto DOM a rellenar con el captcha y un objeto con varias propiedades adicionales para personalizar el captcha.

Por lo tanto, lo que he hecho ha sido:

En la vista principal (la que muestra el popup cuando se pulsa un enlace), que en mi caso se llama Home/Index.aspx, he añadido el tag <script>:

<script type="text/javascript" src="http://api.recaptcha.net/js/recaptcha_ajax.js"></script>

Y luego he definido una función showRecaptcha, que llama Recaptcha.Create:

<script type="text/javascript">
    function showRecaptcha() {
Recaptcha.create("clave_publica_de_recaptcha",
"div_recaptcha", {
theme: "red",
tabindex: 0 }); } </script>

Finalmente, llamo a esta función en cuanto se ha mostrado el formulario modal. Para ello uso el callback onOpen de SimpleModal. Si mirais mi post anterior, vereis que ya usaba este callback para añadir un manejador de eventos javascript para ir comprobando (via AJAX) si el login del usuario estaba libre o no, sin necesidad de hacer submit de todo el popup. En mi caso tenía una función popup_open, y ha sido en ella que he añadido el código para mostrar el captcha:

function popup_open(dialog) {
    dialog.overlay.fadeIn('slow', function() {
        dialog.container.fadeIn('slow', function() {
            dialog.data.fadeIn('slow', function() {
                showRecaptcha();
// resto de código… }); }); }); }

Paso 3: Comprobar el captcha

Esto, usando RecaptchaMvc es muy fácil. En mi caso, cuando el usuario pulsa el botón “Enviar” de mi popup, se hace un POST a una URL gestionada por la acción Signup del controlador Account, cuyo código es:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Signup(FormCollection values)
{                       
    string privateKey = this.GetRecaptchaPrivateKey();
    var response = this.TryValidateRecaptcha
        (privateKey, values.ToValueProvider());
    if (response.IsValid) {
        // Recaptcha ha sido Ok
    }
    else { 
        // Recapcha ha ido mal
    }
}

Muy simple: Obtenemos la clave privada de Recaptcha (el método GetRecaptchaPrivateKey) es un método extensor mío, que obtiene la clave privada que guardo en el web.config. Luego simplemente llamamos a TryValidateRecaptcha (método extensor que incorpora RecapthcaMvc) y con esto ya sabemos si el captcha es correcto o no!

Y aquí teneis una captura de pantalla con todo funcionando:

image

(Sí, sí… el estilo es muy… ASP.NET MVC :p :p :p)

Saludos!

Enlace del fichero controllerextensions.cs de RecaptchaMvc preparado para ASP.NET MVC versión final.

Mostrar un formulario modal con ASP.NET MVC y Ajax (ii)

En el post anterior (Mostrar un formulario modal con ASP.NET MVC y Ajax) comenté como había usado SimpleModal en una aplicación ASP.NET MVC para mostrar un formulario modal al usuario.

En este post voy a comentar como podemos comunicar nuestro formulario modal con nuestros controladores, para así poder validar (parcialmente o totalmente) el formulario desde servidor, sin necesidad de hacer un submit, usando Ajax.

En mi caso p.ej. a medida que el usuario va entrando un posible nickname, se le indica si dicho nickname ya está ocupado o no (aunqué en la versión final probablemente sea un boton “comprobar nick”, ya que una llamada Ajax cada vez que se entre un carácter en un textbox quizá es demasiado… pero eso no afecta al sentido del post). Para realizar las llamadas Ajax cada vez que el usuario pulse una tecla en el campo “nick” del formulario modal vamos a usar jQuery.

Lo primero que debemos cambiar respecto al post anterior es la forma en como abrimos el formulario modal: el método modal de SimpleModal, admite varios parámetros, uno de los cuales es una función de callback que SimpleModal llamará. A través de esta función vamos a registrar una función gestora del evento KeyPress usando jQuery. Recordáis la función show_popup() que teníamos en la vista Index.aspx? Su código ahora queda así:

function show_popup() {
    $("#popup").modal( { onOpen: popup_open});
}

Hemos añadido el parámetro onOpen con el nombre de una función javascript que SimpleModal nos llamará cuando deba abrir el formulario modal.

Una particularidad de SimpleModal es que si usamos la función de callback onOpen, debemos encargarnos nosotros de abrir el formulario modal. Así el código de la función popup_open queda así:

function popup_open(dialog) {
    dialog.overlay.show(1);                
    dialog.container.show(1);
    dialog.data.show(1);
}

Debemos llamar al método show (un método estándar definido en jQuery) con los tres elementos que componen el formulario modal de SimpleModal: Overlay (que lo que hace es inhabilitar el resto de la pantalla), Container (que muestra el borde del formulario) y Data (que muestra el contenido del formulario).

Otra forma “más jQuery” de realizar lo mismo es encadenar las llamadas: Generalmente todos los métodos jQuery aceptan un parámetro callback con código a realizar cuando se termine el método. Así también podríamos escribir la función popup_open como:

function popup_open(dialog) {
    dialog.overlay.show(1,function() {
        dialog.container.show(1,function() {
            dialog.data.show(1);
        });
    });
}

El siguiente paso es añadir el código para suscribirnos al evento onKeyPress del textbox cuyo id era “nick”. jQuery unifica todos los eventos de los distintos browsers en un conjunto de eventos propio, lo que permite más fácilmente desarrollar aplicaciones cross-browser. El método keypress de un objeto jQuery permite suscribir un callback al evento de pulsación de una tecla. Un objeto jQuery es un objeto javascript que se obtiene generalmente usando la función selector (comúnmente llamada $) de jQuery. Así la llamada:

$("#popup")

Me devuelve el objeto jQuery asociado al elemento DOM cuyo ID sea “popup”. Esto no es equivalente a document.getElementById(“popup”) que me devuelve el objeto DOM directamente… es mucho mejor, ya que sobre el objeto jQuery puedo usar todas las propiedades de jQuery (como el método show() que hemos visto antes o el método modal() que define SimpleModal)!

Así pues, para suscribirnos al keypress del textbox cuyo ID es “nick” usando jQuery, el código de popup_open queda:

function popup_open(dialog) {
    dialog.overlay.show(1, function() {
        dialog.container.show(1, function() {
            dialog.data.show(1, function() {
                $("#nick").keypress(function(e) { });
            });
        });
    });
}

Ahora sólo nos rellenar la función anónima que pasamos como parámetro a la llamada a keypress con el código que realice una petición Ajax a un controlador para que compruebe si el nick que se ha entrado está libre o no. Para ello vamos a usar la función getJSON de jQuery, que lo que hace es realizar una petición Ajax a la URL especificada, esperar la respuesta en formato JSON, deserializar la respuesta en un objeto Javascript y ejecutar el método de callback que nosotros le indiquemos.

Así, pues usando getJSON el código de popup_open queda así:

function popup_open(dialog) {
    dialog.overlay.show(1, function() {
        dialog.container.show(1, function() {
            dialog.data.show(1, function() {
                $("#nick").keypress(function(e) {
                    if (e.which != 13 && e.which != 8 
&& e.which != 0) { var str = this.value +
String.fromCharCode(e.which); var url = "/Account/Check"; $.getJSON(url, { nick: escape(str) },
function(data) { if (data.existeix) { $("#invalid_nick").show(); } else { $("#invalid_nick").hide(); } }); } }); }); }); }); }

Dentro de la función gestora del evento keypress:

  1. Miramos que la tecla pulsada NO sea enter, backspace o tabulador
  2. Llamamos a getJSON con la URL /Account/Check (parámetro 1), con el valor del textbox codificado como parámetro (parámetro 2) y la función de callback que queremos ejecutar cuando recibamos la respuesta del servidor (parámetro 3 que es un método anónimo).
    1. Dentro del método anónimo, miramos si el valor del campo “existeix” del objeto recibido como parámetro es true para mostrar u ocultar un objeto cuyo ID es “invalid_nick”.

¿Que nos queda por hacer? Pues por un lado modificar la vista parcial que es el formulario modal (en mi caso era SignupPopup.ascx), para añadir un <DIV> con un id “invalid_nick” con un mensaje que ponga “NICK INCORRECTO” (o algo así). P.ej:

<div id="invalid_nick" style="display:none">
    NICK IS INVALID
</div>

Inicialmente lo tenemos oculto (evidentemente usaríamos CSS y alguna imágen para hacerlo más “bonito”), puesto que lo mostramos via jQuery.

Por último lo que nos queda es hacer la función correspondiente en el controlador. En mi caso el controlador es AccountController y la acción es “Check” (como se puede deducir de la URL /Account/Check):

public ActionResult Check(string nick)
{
    return new JsonResult() { 
        Data = new {existeix = nick.Length % 2 == 0 }
    };
}

Esta acción en lugar de devolver una vista, devuelve un objeto serializado en JSON, usando JsonResult. Básicamente cuando queráis devolver un objeto codificado en JSON usando ASP.NET MVC:

  1. Creais un JsonResult.
  2. A la propiedad Data la asignais el objeto a serializar.

Esta función devuelve un objeto con una propiedad “existeix” que vale true si el nick tiene un número par de carácteres.

¡Ya lo tenemos todo listo! Ahora si vais tecleando carácteres en el formulario, se ve como se muestra o se oculta la etiqueta “NICK IS INVALID”.

¿Fácil, verdad?

Saludos!

PD: Recordais lo que os dije, que cuando usabamos el callback onOpen de SimpleModal debíamos “abrir” manualmente el overlay, el container y la data y que eso nos daba capacidades interesantes? Este “interesantes” viene por la API de animación de jQuery. P.ej. si podríamos cambiar los show() por llamadas a fadeIn para que la aparición del formulario sea más espectacular:

function popup_open(dialog) {
    dialog.overlay.fadeIn('slow', function() {
        dialog.container.fadeIn('slow', function() {
            dialog.data.fadeIn('slow', function() {
                // A partir de aquí todo igual...
           });
        });
    });
}            

¡Y observad como se despliega suavemente el formulario! 😉

Mostrar un formulario modal con ASP.NET MVC y Ajax

¿Os gusta ASP.NET MVC? A mi personalmente me encanta… aunque está un poco verde, en el sentido que comparándolo con webforms hay varias cosas que debes hacerte tu mismo, el modelo de programación es simple y elegante… Gran parte del mérito lo tiene (además del uso del patrón MVC evidentemente), jQuery genial librería de Javascript donde las haya.

Hay mucha gente desarrollando en jQuery (al margen de que usen o no ASP.NET MVC) y dado lo bien que se entienden ASP.NET MVC y jQuery es muy fácil realizar tareas que antes eran un poco… complejas.

Yo me he encontrado con la necesidad de mostrar un pop-up (modal) en una aplicación ASP.NET MVC. Un par de búsquedas por google me han llevado a SimpleModal, un genial plugin para jQuery que precisamente hace esto: mostrar formularios modales. En su página web hay varios ejemplos (en su caso él usa PHP).

Os cuento como he integrado SimpleModal en mi aplicación ASP.NET MVC por si a alguien le interesa… Esta ha sido mi manera de hacerlo, no pretendo sentar cátedra porque hay muuuuuuucha gente que sabe más que yo (especialmente de jQuery).

En concreto la necesidad era mostrar un link, que al pulsarse desplegase un pop-up modal para que la gente pudiera darse de alta en la página.

La página que muestra el enlace (en mi caso Index.aspx) tiene el siguiente código ASP.NET:

<%= Ajax.PopupLink ("Join the game", "Signup","Account", "popup") %>
and start playing!
<div id="popup" />

El método PopupLink es un método extensor de AjaxHelper:

public static string PopupLink(this AjaxHelper helper, string linkText, 
string actionName, string controllerName, string popupId) { AjaxOptions options = new AjaxOptions() { UpdateTargetId = popupId, OnComplete = "show_popup", HttpMethod = "GET" }; string link = helper.ActionLink(linkText, actionName,
controllerName, options); return link; }

Ok… no es un método muy configurable, pero a mi me va bien 🙂 Lo que hace es mostrar un enlace con el texto especificado y le establece unas opciones por defecto: Que la llamada sea via Ajax usando GET, que se llame a una función javascript “show_popup” al terminar y que se actualice el elemento DOM especificado (en este caso el último parámetro llamado ‘popup’). Los parámetros “actionName” y “controllerName” del método sirven para especificar que acción de que controlador debe devolver la vista parcial que contiene el popup. En mi caso la acción “Signup” del controllador “AccountController” que está definida tal y como sigue:

public ActionResult Signup()
{
    if (Request.IsAjaxRequest())
    {
        return PartialView("SignupPopup");
    }
    else return RedirectToAction("Index", "Home");               
}

Como podeis ver me limito a devolver la vista parcial SignupPopup que es la que contiene el código HTML del popup. Cuando el usuario haga click en el enlace “Join the game” se llamará via Ajax a la acción Signup que devolverá la vista parcial “SignupPopup”, el código de la cual se incrustará dentro del div “popup”.

El código de la vista parcial en mi caso es muy simple:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<div>
    <h1>Join the Game!</h1>
    <% using (Html.BeginForm("Signup", "Account", FormMethod.Post)) { %>
        <label for="nick">*Nick Name:</label>
        <input type="text" id="nick" name="nick" tabindex="1001" />
        <br />
        <label for="email">*Email:</label>
        <input type="text" id="email" name="email" tabindex="1002" />
        <br />
        <label>A email for validate your account will be sent at 
email address you specified.</label> <br /> <button type="submit" tabindex="1006">Send</button> <button type="reset" tabindex="1007">Cancel</button> <br/> <% } %> </div>

Basicamente tenemos un formulario con dos campos: nick y email. Cuando hagamos un submit del formulario (via POST) se llamará a la acción Signup del controlador AccountController, acción que está definida como sigue:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Signup(string nick, string email)
{
    // Codigo para dar de alta el nuevo usuario...
    // Mostramos la vista de Bienvenida
    return View();
}

No mucha cosa… El controlador da de alta el usuario y finalmente muestra una vista de bienvenida.

Finalmente en la página Index.aspx, debemos tener el método javascript show_popup, que será el encargado de mostrar el popup usando SimpleModal:

<script type="text/javascript">
    function show_popup() {
        $("#popup").modal();
    }
</script>    

El código es muy simple: accedemos al elemento div con id=”popup” que hemos rellenado con el contenido de la vista parcial, y usamos el método modal() que define SimpleModal para mostrar este div como un formulario modal…

… y listos!

Simple y sencillo… en otro post mostraré como comunicar nuestro formulario via Ajax con nuestros controladores (p.ej. para poder validar datos en servidor sin necesidad de hacer submit del formulario).

ASP.NET MVC, Controles Chart y Ajax…

Supongo que la gran mayoría de vosotros, conoceréis los controles de gráficos de ASP.NET. José M. Aguilar hizo un excelente post sobre ellos aquí (http://geeks.ms/blogs/jmaguilar/archive/2008/12/14/microsoft-chart-control-para-asp-net-3-5-sp1.aspx).

Utilizarlos es realmente simple… basta con que os los descargueis de la web de Microsoft y después de instalarlos agregueis las siguiente líneas en el web.config:

<add path="ChartImg.axd" verb="GET,HEAD" 
type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler,
System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35
" validate="false"/>

En la sección <httpHandlers> y la siguiente:

<add tagPrefix="asp" 
namespace="System.Web.UI.DataVisualization.Charting"
assembly="System.Web.DataVisualization, Version=3.5.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
"/>

En la sección <controls>.

Despues ya podeis arrastrar un Chart control desde la toolbox a vuestra página ASP.NET y empezar a trabajar con él.

Si, como yo, os encanta ASP.NET MVC sabed que podeis usar este control sin ningún problema (http://code-inside.de/blog-in/2008/11/27/howto-use-the-new-aspnet-chart-controls-with-aspnet-mvc/).

El único temilla a tener en cuenta es si se quiere actualizar sólo el gráfico mediante Ajax (usando ASP.NET MVC).

Suponed una vista parcial (Chart.ascx) con el siguiente código que muestra un gráfico con contenido random:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<asp:Chart ID="chart" runat="server" Palette="Fire" >
    <Series>
        <asp:Series Name="D1" ChartType="StackedColumn" />
        <asp:Series Name="D2" ChartType="StackedColumn" />
    </Series>
    <ChartAreas>
        <asp:ChartArea Name="ChartArea1">
        </asp:ChartArea>
    </ChartAreas>
</asp:Chart>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        Random r = new Random();
        this.chart.Series["D1"].Points.Add(r.Next(100));
        this.chart.Series["D2"].Points.Add(r.Next(100));        
    }
</script>

Y otra vista (Victories.aspx) que contiene el siguiente código (entre otro):

    <%=Ajax.ActionLink("Actualizar", "Victories", 
new RouteValueDictionary(new { Days = 7, Interval = 1 }),
new AjaxOptions() { UpdateTargetId = "chart" }) %> <div id="chart" />

El enlace “Actualizar” envia una petición Ajax al controlador actual para que invoque la acción “Victories” y con el resultado actualice el div “chart”.

La acción “Victories” está implementada en el controlador tal como sigue:

public ActionResult Victories(int? days, int? interval)
{
    return PartialView("Chart");
}

De este modo a cada click del enlace se genera un nuevo gráfico aleatorio y se actualiza via Ajax la página…

… en teoria, porque en la práctica no se ve nada. Analizando con firebug lo que ha sucedido se puede observar que se lanza una excepción:

[HttpException (0x80004005): Error executing child request for ChartImg.axd.]

La solución? Caer en la cuenta de que las peticiones Ajax usan POST por defecto, así que o bien cambiamos la línea que añadimos en el web.config para que soporte POST:

<add path="ChartImg.axd" verb="GET,HEAD, POST" 
type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler,
System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35
" validate="false"/>

o bien le indicamos  a la petición Ajax que sea usando “GET”:

<%=Ajax.ActionLink("Last Week", "Victories", 
new RouteValueDictionary(new { Days = 7, Interval = 1 }),
new AjaxOptions() { HttpMethod="GET", UpdateTargetId = "chart" }) %>

Saludos!