Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
domingo, 29 de noviembre de 2009

Los controladores ASP.NET MVC que heredan de la clase Controller permiten procesar muy fácilmente las peticiones realizadas a acciones no definidas. Para ello, lo único que hay que hacer es sobrescribir el método HandleUnknowAction() e implementar la lógica que queremos que se ejecute en estos casos.

En el siguiente código, las peticiones realizadas a /Home/Index y /Home/About serán procesadas normalmente, pero /Home/BeberCerveza  será procesada por HandleUnknowAction, cuya  implementación mostrará la vista “Index” con un mensaje personalizado:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";
        return View();
    }
 
    public ActionResult About()
    {
        return View();
    }
 
    protected override void HandleUnknownAction(string actionName)
    {
        ViewData["Message"] = "¿Estás intentando " + actionName + "?";
        View("Index").ExecuteResult(this.ControllerContext);
    }
}

image

Publicado en: Variable not found.

martes, 24 de noviembre de 2009

En un post anterior dedicado a jqGrid y ASP.NET MVC vimos lo sencillo que resultaba implementar un potente grid para mostrar datos tabulares, permitiendo paginación, ordenación y redimensionado de columnas.

Pero, como ya comenté entonces, jqGrid es mucho más que eso. En este artículo estudiaremos la implementación de la funcionalidad de borrado de filas integrada en el propio componente, utilizando intercambio de datos Ajax con el lado servidor para actualizar el modelo.

Partiremos del ejemplo que desarrollamos anteriormente, y nos centraremos únicamente en introducir los cambios necesarios para introducir esta nueva capacidad. También, como en el caso anterior, encontraréis al final un enlace al proyecto de demostración, para Visual Web Developer Express 2008 SP1 y ASP.NET MVC 1.0.

1. Preparamos la infraestructura

Selección de módulos en jqGridAntes de nada, y es algo que es importante recordar cuando trabajemos con jqGrid, debemos pensar qué módulos de este componente necesitamos en nuestro proyecto.

En el post anterior descargamos exclusivamente los necesarios para implementar la visualización del grid; ahora, dado que vamos a utilizar más funcionalidades del componente, debemos seleccionar en la herramienta de descarga aquellos que nos harán falta, el base y los relativos a la edición de datos.

En teoría deberíamos seleccionar los módulos estrictamente necesarios para nuestros fines, pero en la práctica no es fácil adivinar cuál de ellos implementa justamente lo que estamos buscando. De hecho, es bastante frecuente encontrarse con errores de script cuando no acertamos con el módulo exacto, al que siguen bastantes iteraciones prueba-error hasta que conseguimos averiguar cuál debemos indicar en la descarga.

Por eso en este caso, seleccionaremos todos los módulos relativos a la edición; eso nos permitirá, además, seguir implementando funcionalidades como el alta o la modificación sin tener que volver a descargar componentes.

Aparte de los módulos a incluir en la descarga, el resto de los pasos de preparación de la infraestructura son idénticos a los descritos en los puntos 1 al 3 del post inicial:

  • Copiar los archivos de script (jqGrid y el archivo de localización) en el proyecto.
  • Descargar el tema visual de jQuery UI y añadirlo al proyecto.
  • referenciar las librerías de scripts y estilos en la master (o vistas donde vayamos a usarlo).

2. El modelo

Vamos a ampliar la clase GestorDeAmigos anterior para que sea capaz de emular el almacenamiento en una base de datos, pero utilizando como soporte una colección en memoria que haremos persistir en una variable de aplicación. Además, aprovecharemos para añadirle un método Delete() que nos permita eliminar del almacén la persona cuyo “Id” le pasemos como parámetro.

public bool Delete(int id)
{
    Amigo amigo = DatosAmigos.FirstOrDefault(a => a.Id == id);
    if (amigo == null)
        return false;
 
    DatosAmigos.Remove(amigo);
    return true;
}
 
No profundizaré más en el modelo, pues el código es de lo más convencional. El que tenga curiosidad por ver cómo se implementa el almacén en una variable de aplicación, que acuda al fuente del proyecto de demostración.

3. La vista

En la vista debemos hacer muy pocos ajustes para permitir la eliminación de los datos. Básicamente, tendremos que habilitar un panel de botones en el grid, indicar que deseamos que aparezca el botón de eliminación, y configurar el comportamiento de éste, para lo cual invocaremos al método navGrid() después de la llamada de inicialización del grid que ya vimos el otro día:

<script type="text/javascript">
    jQuery(document).ready(function() {
    
        jQuery("#list").jqGrid({
            [...] // OMITIDO. Idéntico al del post anterior.
        });
 
        $("#list").navGrid(
                null,
                { refresh: true, add: false, edit: false, del: true, search: false },
                null, // parámetros para el alta
                null, // parámetros para la edición
                {     // parámetros para la eliminación
                    url: '<%= Url.Action("Eliminar") %>',
                    width: 500,
                    afterSubmit: function(r, d) {
                        return [r.responseText=="", r.responseText];
                    }
                }
        );
});

Los parámetros que estamos pasando al método navGrid() son los siguientes:

  • el primer parámetro debería ser jQuery('#pager'), que es la referencia hacia el control de paginación que estamos utilizando. En este caso es un nulo porque esta referencia se incluyó en la inicialización de jqGrid.
  • A continuación, creamos un objeto anónimo en el que establecemos a true las propiedades del y refresh, que indica que queremos mostrar los botones de eliminación y recarga de datos. El resto de propiedades predefinidas, add, edit, y search, equivalentes a los botones de añadir, editar y buscar registros, respectivamente, las establecemos a false con objeto de que no aparezcan botones para invocarlas; ya las activaremos en otros posts ;-)
  • el siguiente parámetro se trata de un objeto donde configuramos el comportamiento del botón de alta de registros. Dado que no vamos a implementarlo ahora, lo establecemos a nulo.
  • el cuarto parámetro es lo mismo que el anterior, pero para configurar la edición, por lo que también se encuentra establecido a null.
  • a continuación, y por último, el objeto en cuyas propiedades definimos el comportamiento del botón de eliminación:

    • La URL a la acción que se invocará en servidor, que la obtenemos utilizando el UrlHelper de MVC. En este caso, invocaremos a una acción llamada “Eliminar”, a la que el sistema enviará el Id del registro activo.
    • El ancho del cuadro de diálogo de confirmación.
    • En afterSubmit implementamos una función callback que jqGrid llamará cuando haya recibido el resultado de la petición Ajax. El primer parámetro que nos envía es el objeto XMLHttpRequest donde encontraremos la respuesta obtenida desde el servidor; el segundo parámetro contiene los datos que han sido enviados a la petición.

      El retorno de la función callback debe ser siempre un array con dos elementos. El primero es un booleano indicando si la eliminación ha tenido éxito, y el segundo es el mensaje de error, en caso de que se haya producido:

      return [ exito , "mensaje de error" ];

      Es importante ahora resaltar una cosa: salvo los parámetros de entrada y el tipo de retorno descritos anteriormente, jqGrid no nos impone ninguna particularidad más respecto a cómo debemos implementar este método, el tipo de información que recibiremos desde el controlador o cómo la procesaremos al recibirla. Somos totalmente libres de elegir la forma en la que haremos las cosas.

      En nuestro caso, vamos a hacer una implementación muy simple en base a la siguiente convención: el controlador retornará un string con una descripción del error en caso de que se produzca algún problema borrando el registro, y retornará un nulo cuando todo vaya bien. Esto nos permite implementar el callback utilizando la siguiente expresión:
      return [r.responseText=="", r.responseText];

      Si observáis, estamos llenando el array de retorno de tal forma que el primer parámetro será cierto si la respuesta obtenida está vacía (o sea, no hay error), y en el segundo parámetro introducimos la respuesta tal cual la hemos obtenido del servidor.

4. El controlador

Dado que la lógica la tenemos implementada en el modelo, y que la vista ya está preparada para ponerse en contacto con el controlador vía Ajax y para recibir el feedback sobre el éxito de la operación, sólo nos queda implementar muy rápidamente el método de acción:

public ActionResult Eliminar(int id)
{
    if (!gestorDeAmigos.Delete(id))
        return Content("Error eliminando el registro");
 
    return null;
}

Podréis observar que si se produce un error, retornamos un ContentResult describiendo el problema; en otros casos, devolvemos un nulo.

Para probar el funcionamiento de una eliminación errónea podéis, por ejemplo, abrir dos navegadores contra la aplicación, borrar un registro desde uno de ellos e intentar borrarlo también desde el otro.

Y… ¡Voilá!

Una vez habiendo implementado el modelo, la vista y el controlador, sólo nos queda probar la aplicación, que veremos que funciona perfectamente ;-P

jqGrid+ASP.NET MVC en acción

Resumiendo, en este post hemos seguido profundizando en las capacidades de jqGrid, implementando paso a paso la funcionalidad de eliminación de registros. Hemos podido observar también el escaso código (¡a pesar de la longitud del post!) que hay que añadir para disponer de esta funcionalidad, y de lo sencillo y reutilizable que resulta su implementación.

Descargar proyecto de demostración:

Publicado en: Variable not found.

lunes, 23 de noviembre de 2009

.Less: CSS Dinámicos para .NET Seguro que más de una vez has sufrido de lo lindo al tocar un archivo de hoja de estilos. Suelen estar mal organizados, ser muy extensos, difíciles de mantener, hay declaraciones duplicadas por todas partes… Y seguro que siempre te has preguntado si hay otra forma menos complicada de conseguir lo mismo.

K. Scott Allen nos habla en su post Keeping CSS Files DRY de .Less (dot less), un interesante componente portado del mundo Ruby capaz de “compilar” hojas de estilo que utiliza una notación extendida y mucho más potente del estándar CSS, permitiendo las siguientes características:

  • Uso de variables. Less permite declarar y utilizar variables para los valores que utilizaremos frecuentemente en nuestra hoja de estilos, así:

    @nice-blue: #5B83AD;
     
    #header { color: @light-blue; }
    #footer { color: @light-blue; }

  • Operaciones. Podemos utilizar expresiones para obtener valores, por ejemplo:

    @base: 5%;
    @filler: @base * 2;
    @other: @base + @filler;
     
    color: #888 / 4;
    background-color: @base-color + #111;
    height: 100% / 2 + @filler;

    Fijaos en un detalle de lo más interesante: Less es capaz de distinguir cuándo está operando con colores y cuándo con unidades, ofreciendo el resultado correcto en cada caso.

  • Mezclas (mixins), otra potente capacidad de Less que seguro que puede venirnos bastante bien, que consiste en incluir dentro de un conjunto de reglas dentro de otro, sólo haciendo referencia a su selector:

    .bordered {
      border-top: dotted 1px black;
      border-bottom: solid 2px black;
    }
     
    #menu a {
      color: #111;
      .bordered;
    }
     
    .post a {
      color: red;
      .bordered;
    }

  • Reglas anidadas, que nos permiten definir reglas siguiendo una estructura más legible y fácilmente reconocible por su paralelismo con la estructura del documento:

    #header {
      color: black;
     
      .navigation {
        font-size: 12px;
      }
      .logo {
        width: 300px;
        :hover { text-decoration: none }
      }
    }

  • Visibilidad de variables. Las variables pueden ser declaradas de forma global (como en el primer ejemplo) o asociadas a algún selector, lo que les aporta ámbito de visibilidad de forma muy similar a los lenguajes tradicionales:

    @var: red;
     
    #page {
      @var: white;
      #header {
        color: @var; // color será “white”
      }
    }

  • Accesores. Less permite utilizar valores de propiedades y variables como contenido para otras propiedades de forma muy natural:

    #defaults {
      @width: 960px;
      @color: black;
    }
     
    .article { color: #294366; }
     
    .comment {
      width: #defaults[@width];
      color: .article['color']; 
    }

    Fijaos que el bloque #defaults no tiene por qué corresponder con un elemento del documento a formatear, se trata sólo de una forma de agrupar variables y reglas, consiguiendo un efecto empaquetador muy similar a los espacios de nombres.

  • Comentarios en línea, al más puro estilo C++ ó C#:

    // Get in line!
    @var: white;

  • Importación de archivos, permitiendo acceder a reglas y variables definidas en otros archivos:

    @import "library"; // Si la extensión es .less, se puede omitir
    @import "typo.css";

¿Y cómo funciona esto? Muy sencillo. En primer lugar, escribimos la hoja de estilos en un archivo .less, en el que podemos utilizar las características descritas anteriormente, y lo incluimos en las páginas donde nos interese, como lo hacemos siempre:

<link href="/Content/estilos.less" rel="stylesheet" type="text/css" />

Es interesante tener en cuenta que Less es compatible con CSS, lo que quiere decir que si simplemente renombramos el .css a .less, ya tendremos una hoja de partida.

A continuación, debemos introducir en el web.config una declaración que asocie la extensión .css al compilador Less:

<httpHandlers>
  <add validate="false" path="*.less" verb="*"
    type="dotless.Core.LessCssHttpHandler, dotless.Core" />
</httpHandlers>

Cuando llega una petición, el handler se encargará de compilar el archivo .less, generar el .css correspondiente y enviarlo minimizado al cliente, cacheando el resultado para posteriores usos, si se lo indicamos en la configuración, una sección opcional del web.config.

.Less está diseñado para ASP.NET 3.5 o superior, y por supuesto, es válido tanto para Webforms como para ASP.NET MVC. Se distribuye bajo licencia Apache 2.0, y está aún en fase beta, por lo que habrá que estar atento a la evolución de este interesantísimo proyecto.

Enlaces:

Publicado en: Variable not found.

miércoles, 18 de noviembre de 2009

mvc-logo-landing-pageHace sólo unas horas Haack ha anunciado en su blog la publicación de la versión beta del  framework ASP.NET MVC 2, que tenemos a nuestra disposición tanto a nivel de código fuente como de instalador binario.

Echando un vistazo rápido a las release notes, las novedades son:

  • introducción de los nuevos métodos Html.RenderAction() y Html.Action(), el primero de ellos para escribir directamente la salida resultado de ejecutar la acción dada, y el segundo para obtenerla como cadena.
  • helpers para la generación de controles de edición en las vistas, basados en expresiones lambda o, en otras palabras, con tipado fuerte: ValidationMessageFor(), TextAreaFor(), TextBoxFor(), HiddenFor()y DropDownListFor().
  • mejoras en TempDataDictionary, destinadas a controlar de forma más pormenorizada la persistencia de los datos.
  • se incluyen las librerías de validación en cliente, MicrosoftAjax.js y MicrosoftMvcAjax.js. Para utilizar este automatismo, sólo hay que incluir ambos script en la vista (o master) e invocar al método de activación: <% Html.EnableClientValidation(); %>.
  • Visual Studio incorpora ahora una opción “Add Area” en el menú contextual, así como un cuadro de diálogo para crearlas.
  • es posible crear controladores de ejecución asíncrona heredando de la clase AsyncController. Esto permite crear acciones que se ejecuten en segundo plano, sin bloquear el hilo principal.
  • nueva plantilla de proyectos en blanco, es decir, sin toda la parafernalia de las plantillas actuales (páginas de ejemplo, autenticación, etc.).
  • capacidad para registrar múltiples proveedores de validación, permitiéndonos adoptar otras soluciones o frameworks, o crear nuestras propias opciones.
  • separación del proveedor de datos, el componente utilizado para obtener datos del contexto, en subcomponentes especializados en función del origen de la información (formularios, ruta, cadena de petición, y archivos). También existe la posibilidad de crear proveedores personalizados.
  • Y otras mejoras, de menor calado, citadas de forma concisa en las notas de la revisión, como:
    • adición de nuevos métodos y propiedades a la clase ModelMetadata.
    • cambios en la plantilla por defecto de las vistas para generar los controles utilizando los nuevos helpers.
    • helpers con soporte para datos binarios en el modelo, generando automáticamente campos ocultos codificados en Base64.
    • etc.

Y ojo, que esta beta sólo funciona con Visual Studio 2008 SP1, no podemos probarla con la última beta de Visual Studio 2010. Eso sí, puede convivir en nuestros equipos con MVC 1.0 sin interferencias.

El siguiente paso será la publicación de la RC, que está prevista “antes de finalizar el año”, aunque no se planea incluir funcionalidades nuevas, simplemente corregir bugs y pequeñas mejoras que puedan derivar de esta fase de pruebas y del feedback de los usuarios. Por tanto, en líneas generales, lo que nos trae esta beta será prácticamente lo que vendrá en la versión 2 :-)

Enlaces:

Publicado en: Variable not found.

lunes, 16 de noviembre de 2009

¡Programadiseñadores al poder! Ya sabemos lo que suele ocurrir cuando los programadores diseñamos interfaces de usuario ;-). Para seguir profundizando en este curioso e inevitable fenómeno, Ian Voyce ha publicado hace unas semanas el divertido post The 7 signs your UI was created by a programmer, en el que recoge pistas que nos ayudarán a determinar si el interfaz de la aplicación que usamos a diario está creado por un diseñador experto en usabilidad, o ha sido el propio desarrollador el encargado de dichas tareas.

Bueno, en realidad se trata más bien de una lista con malos hábitos en el diseño de interfaces, y no necesariamente creados por programadores, pero ciertamente me he reconocido en alguno de los puntos y me han hecho gracia. Los cito y comento a continuación:

  • Iconos de exclamación en cuadros de diálogo
    Efectivamente, cuando incluimos una exclamación en un cuadro de diálogo no pensamos en que es posible que el usuario vea el mensaje 50 veces al día, con lo que dejaría de ser ese mensaje asombroso y puntual que pensamos en un principio. Hoy en día, muchas aplicaciones utilizan un tono mucho más neutral y cercano para enviar mensajes al usuario.

  • Mensajes con dobles negaciones en cuadros de diálogo, del tipo “¿Seguro de que no quieres perder tus cambios?” -- Hmm… sí… digooo, no…  También son muy propias construcciones más complejas de la cuenta, paradójicas o inesperadas, como “Su mensaje no se ha enviado, ¿desea descartarlo?”, que ponen a prueba los reflejos de cualquier usuario.

  • Orden de tabulación inexistente, o incorrecto, que fuerzan a que la navegación se realice exclusivamente a golpe de ratón. Y especialmente en aplicaciones de entrada masiva de información, el cambio de teclado a ratón y viceversa es terrible. Y paraGroupboxes para todo.. porque mola hacernos conscientes de ello, nada como dar vacaciones un rato al animalito y ver lo bien que manejamos determinadas aplicaciones.

  • Groupboxes rodeándolo todo… porque hay sitios donde queda bonito, aunque no esté agrupando nada ;-). Me confieso: durante años, he rodeado de groupboxes todo lo que se me ponía por delante, porque me parecía más agradable a la vista. Ahora lo estoy dejando ;-)

  • IconoIconos creados en el IDE, je, este es otro de mis puntos fuertes: soy un fiera diseñando iconos desde el editor de Visual Studio. El problema es que, a pesar de lo orgulloso que estoy siempre de mis creaciones artísticas, no quedan bien e incluso introducen un cierto tufillo a cutre en las aplicaciones.

  • Utilización de grids, debido a lo que llama Voyce “la enfermedad de considerar que Excel es lo máximo en diseño de interfaces de usuario”, que fomenta la creación de rejillas con controles de lo más potentes y variopintos en las filas de datos (calendarios, gráficos, etc.).

  • Message Boxes no implementados, el equivalente en los interfaces de usuario a los TODO en el código fuente :-DD. Buena analogía. Algo parecidos serían los mensajes del tipo “Este mensaje no debería aparecer nunca” que alguna vez he encontrado por ahí, auténticos actos de rebeldía contra sus respectivos creadores.

  • Uso excesivo de cuadros de diálogo, incluso en situaciones en las que perfectamente podríamos obviarlos por no ofrecer información útil para el usuario, que no va a poder entender, o donde no se requieren decisiones por su parte.

    Los componentes necesarios han sido detectados. Accediendo...Esto es algo muy frecuente de ver. Por ejemplo, el otro día accediendo a una aplicación web para la que es necesario contar con certificado digital, me saltó la alerta que muestro a la derecha:

    Sí, probablemente los componentes Java necesarios para la autenticación y firma electrónica hayan sido detectados con éxito, pero… ¿realmente era necesario interrumpir mi navegación para informarme de ello? ¿Un usuario, sabrá interpretar el mensaje? Y en cualquier caso, ¿le aporta algo? No sé, no sé… me da que es un alert() creado por un desarrollador durante las pruebas y que al final se quedó ahí para la posteridad.

Hasta aquí las citadas en el post original, aunque siguiendo en la misma línea, puedo añadir algunas más de propia cosecha:

  • Cuadros de diálogo con mensajes y botones inconsistentes. Mensajes del tipo “¿desea cancelar la operación?” en cuyo pie aparece un botón “Aceptar” y otro “Cancelar” siempre me dejan pensando un rato. Si pulso aceptar, ¿estoy aceptando la cancelación, o justo lo contrario?… Muchas veces, parece que sólo podemos confiar en el azar.

  • Cuadros de mensaje con demasiados (o demasiados pocos) botones. No siempre atendemos a la Ley de Hick, que nos dice que “el tiempo que se tarda en tomar una decisión aumenta a medida que se incrementa el número de alternativas”, y eso nos lleva a crear cuadros de diálogo con muchos, demasiados, botones que el usuario debe estudiar.

    Y justo en el lado contrario, pero igualmente desconcertantes, son los cuadros de diálogo de introducción de datos sin ningún botón de aceptación, en los que su cierre (con la X de la esquina superior) implica el salvado de datos.

  • Interfaz descuidado. Y no hablo de bonito o feo, términos bastante subjetivos, sino de descuidado: controles sin alinear, con tamaños no proporcionales al contenido pretendido, sensación de desorden, faltas de ortografía… Esto no es un problema de interfaces creados por desarrolladores, sino creados por gente que no pone el más mínimo cariño en lo que hace, independientemente de su puesto de trabajo.

  • Interfaces monocromos, mi especialidad. Como inútil reconocido en temas de diseño, me considero absolutamente incapaz de crear algo medio aparente con más de un color (eso sí, empleando distintas tonalidades ;-)). Nunca he entendido la compleja lógica que hay detrás del “eso no pega con aquello”, que algunos/as parecen dominar de serie.

  • Controles con trastorno de personalidad. Un clásico es encontrar un grupo de checkboxes actuando como un grupo de radios. A ver, si sólo vamos a poder elegir una de las opciones, ¿por qué no usamos directamente el control específicamente ideado para ello? ¿Y un desplegable con las opciones "Activar" y "Desactivar"? ¿No sería más claro poner un checkbox?

Artículo original: The 7 signs your UI was created by a programmer

Publicado en: Variable not found.

miércoles, 11 de noviembre de 2009

Imaginemos el siguiente código HTML:

<input type="text" name="nombre" /><br />
<input type="text" name="nombre" /><br />

Si desde un servidor ASP.NET, como respuesta a un submit (o postback en webforms), queremos acceder a los valores del campo del formulario “nombre”, podemos utilizar el clásico Request[“nombre”] para obtener los valores de todos ellos separados por comas.

Así, si introducimos en el primer campo el valor “Juan” y en el segundo “Pedro”, Request["nombre"] contendrá “Juan,Pedro”. Esto podría ser suficiente para procesarlo troceando la cadena a partir de las comas, por ejemplo así:

string[] nombres = Request["nombre"].Split(',');
 
string n1 = nombres[0]; // n1 = "Juan"
string n2 = nombres[1]; // n2 = "Pedro"

Sin embargo, es obvio que esta técnica genera problemas si un usuario introduce en alguno de los campos una coma, el carácter que estamos considerando como separador de los elementos.

La forma correcta de hacerlo sería utilizando el método GetValues() sobre la propiedad Form de la petición, la cual nos devuelve un array con los valores de cada campo. Por ejemplo, si en el primer campo introducimos “Juan,Jose” y en el segundo “Luis”, el resultado sería el que sigue:

string[] nombres = Request.Form.GetValues("nombre");
string n1 = nombres[0]; // Juan,Jose
string n2 = nombres[1]; // Luis

En ASP.NET MVC, el sistema de bindings es capaz de facilitarnos aún más las cosas. Si en la vista existen diferentes campos llamados “nombre”, y el submit se realiza sobre una acción en cuyos parámetros aparece un array con la misma denominación, recibiremos los datos directamente:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Editar(string[] nombre)
{
    // nombre[0] -> "Juan,Jose"
    // nombre[1] -> "Luis"
}

Publicado en: Variable not found.