Mostrando entradas con la etiqueta visual studio. Mostrar todas las entradas
Mostrando entradas con la etiqueta visual studio. Mostrar todas las entradas

viernes, 4 de junio de 2010

FileSystemWatcher: Pérdida de notificaciones

En días pasados hemos tenido que lidiar con otra de esas chapuzas que tienen a bien ponernos los majetes de Microsoft. Como dice el título de la entrada, se trata del FileSystemWatcher.

La idea del objeto es muy buena a la par que útil: "escucha" las notificaciones de cambios en el sistema de archivos y provoca eventos. Así te evitas el tener que poner un Timer para que consulte cada determinado tiempo un directorio buscando nuevos ficheros.

El problema viene por la forma en la que funciona, o mejor dicho en la forma en falla cuando no funciona. Y es que si el número de cambios es muy elevado, el señor sólo registra una cantidad de ellos, perdiendo el resto en el limbo de los justos.

El resultado de esto es que el objeto sigue funcionando normalmente, y si caen nuevos ficheros los detecta y notifica, pero aquellos que llegaron en una tanda numerosa y cuya llegada no se registró, seguirán en el mismo sitio sin que repare en ellos hasta que vuelvan a sufrir algún cambio -como por ejemplo moverlos a otro directorio y a continuación volver a dejarlo en el mismo-.

Lo mejor es que según la ayuda de MSDN, "por dependencias con el sistema operativo Windows, FileSystemWatcher no provoca un evento Error cuando falta un evento o cuando se supera el tamaño del búfer."

Vamos, que te lo cuentan como si la cosa no fuese con ellos, y como si el sistema operativo Windows lo hubiese programado algún desalmado que nada tiene que ver con los de MSDN.

Nosotros hemos tenido problemas cuando han llegado más de 800 ficheros de la misma tacada, así que hemos ampliado el parámetro que sugiere la ayuda -InternalBufferSize-, pero poco, que por lo visto también es malo ponerlo muy grande. Además hemos capturado el evento Error, que en contra de lo que dice la ayuda sí se dispara cuando se desborda el buffer. De esta forma gestionamos el error y al menos damos un aviso para ver qué ha pasado, ya que de otra forma pasa totalmente desapercibido.

viernes, 17 de octubre de 2008

vs2008: CheckForIllegalCrossThreadCalls

Introducción

El mundo de la programación es más o menos sencillo hasta que nos metemos en berengenales como el multi-hilo. Aquí la cosa se complica ligeramente, y si tratamos de seguir programando como si fuese programación mono-hilo, antes o después tenemos un error del depurador que viene a decir que un hilo está tratando de tocar datos que tiene otro hilo.


La Chapuza

Además, nos da una solución rápida en plan "que no se note", como es cambiar la propiedad CheckForIllegalCrossThreadCalls a false. En la edad media se cortaba una mano por cosas mucho más leves que esta. Eso es como si está sonando la alarma de incencido y le desactivamos el timbre para que no moleste. ¿No es mas razonable -además de otras muchas cosas- solucionar la causa?


La Solución Elegante

Por fin he entendido cómo hay que hacer las llamadas asíncronas a procesos síncronos en Windows Forms y los famosos métodos Delegados, así que voy a intentar explicarlo por aquí, aun a riesgo de meter la pata -vaya por delante que según la documentación, esta forma que voy a poner no termina de ser la mejor posible-.

En un escenario ideal, creamos un formulario que tiene unos componentes y unos métodos, y éstos cambian el estado de aquellos a discrección. Esto es lo habitual cuando un proceso va poniendo en pantalla información sobre lo que va haciendo, y no es ningún problema cuando se trata de una aplicación mono-hilo, de esas que van haciendo cosas una detrás de otra -en la mayoría de los casos dejando el interface frito- hasta que terminan.


Ponemos algo en un TextBox
de la forma tradiccional


Sin embargo, más tarde o más temprano se nos ocurrirá la feliz idea de evitar el famoso "no responde" en el Administrador de Tareas, e incluso -y esto ya es para nota- el botón de cancelar que de verdad cancele el proceso en ejecución. Y para hacer esto no hay nada que nos complique más la vida que lanzar ese proceso pesado en otro hilo distinto al utilizado por el interface.


El ojo hábil habrá notado que estoy llamando a otro método,
pero a estas alturas podría seguir siendo PonTexto.


Y justo aquí es donde llegamos a nuestro problema, ya que cuando ese subproceso trate de comunicarse con el proceso que lo ha creado -por ejemplo para indicar el estado en el que está en un TextBox-, tendremos esa excepción que dice que:


Operación no válida a través de subprocesos: Se tuvo acceso al
control 'textBox1' desde un subproceso distinto a aquel en que lo creó.


La solución a esto recuerda a la recursividad pero cambiándole el nombre -Microsoft, si no le cambia el nombre a las cosas, no puede decir que lo ha inventado-. La cuestión está en que si cuando vamos a modificar el control detectamos que no estamos en el hilo "padre" lo que hacemos es llamar al mismo método pero del hilo correcto -es decir, el hilo que creó el subproceso en el que estamos-. Más o menos.

La forma en la que sabemos si estamos en el hilo correcto es tan sencillo como consultar la propiedad InvokeRequired del control. Esta nos dirá si podemos modificar el control, o si por el contrario tenemos que "invocar" al método que lo creó. Realmente, la complicación está en realizar esta "invocación", ya que desde mi punto de vista no es nada intuitiva.

[Actualización 22/10/2008: Los dos siguientes párrafos, lejos de ser correctos, tienen errores importantes respecto a lo que es un delegado -la ignorancia es atrevida-. No obstante, los dejo tal y como los publiqué originalmente a la espera de escribir una entrada en la que hable de los mismos]

En primer lugar, para llamar a Invoke necesitamos un delegado del método que vamos a utilizar. ¿Y qué es un Delegado? Se trata de un método con la misma firma que el método a delegar. ¿Y qué significa tener la misma firma? Pues viene a significar que tiene el mísmo número de parámetros, y que éstos son del mismo tipo, incluido el tipo devuelto por el método ¿Por qué? Porque si.

En nuestro caso, la definición -olvidaba decir que un delegado no tiene implementación- del método delegado viene a ser algo sencillo como esto:



Y por fin, para hacer esta llamada segura, lo que nos queda es definir una variable de este delegado y llamar al proceso Invoke. He creado un proceso nuevo llamado PonTextoSeguro para mantener las dos formas de hacerlo. Así, esta es la implementación del nuevo método:



Resumen

De esta forma, lo que hacemos es:

1º La aplicación crea un hilo para ejecutar un proceso pesado.
2º El proceso manda información a la aplicación indicando dónde va (por ejemplo un texto a mostrar en textBox1).
3º El método que va a escribir en textBox1 detecta que está en un subproceso, así que se llama a sí mísmo pero en el proceso de la aplicación.
4º Después de la llamada, cuando ya si estamos en el proceso correcto, escribimos en textBox1.

Así hacemos llamadas asíncronas de una forma sencilla y sin chapuzas innecesarias. Naturalmente el ejemplo es lo más sencillo que se puede imaginar. La cosa se complica un poco cuando el método (PonTextoSeguro) recibe parámetros, pero sólo un poco.

viernes, 19 de septiembre de 2008

vs2008: Framewok

Me he llevado una alegría al ver esto, ya que al fin y al cabo no soy tan malo en inglés si me comparo con lo que viene en los productos que valen una pasta gansa.


Ahora la compañía de nuestros amores será fiel a su costumbre de sacar un nuevo Service Pack para corregir los errores que comenten en otro Service Pack -esta captura es de Visual Studio 2008 SP1-.

miércoles, 11 de junio de 2008

Buenas prácticas de programación

Por lo visto, este consejo figura en la primera posición de manual de "buenas prácticas" de PERL -para quién no lo sepa, se trata de un lenguaje de programación-:

"Siempre programa como si la persona que acabe manteniendo tu código sea un violento psicópata que sabe dónde vives"

La verdad, me ha parecido buenísimo, y no sólo por lo gracioso que me resulta. Y es que a veces nuestro trabajo sería un poco más fácil si todos tuviésemos esto en la cabeza cuando estamos tirando líneas de código.

Por cierto, lo he visto en este enlace de barrapunto, puesto por alguien que firma como "explorer".

jueves, 15 de mayo de 2008

SourceSafe: Control de versiones

1. Introducción

En el desarrollo de aplicaciones es importante (casi diría imprescindible) llevar un control de los cambios en el código fuente, especialmente cuando en dicho desarrollo intervienen varias personas. Para esta función tenemos varios programas (TeamSource de Borland, Git para Linux o SourceSafe de Microsoft).

Además, con bastante frecuencia necesitaremos tener los fuentes según estaban en un momento concreto del tiempo, con el objetivo que modificar algún error en la misma. Por ejemplo, el código fuente que corresponde a la versión de la aplicación que está desplegada en producción, ya que, como es evidente, no nos sirve la última versión del código fuente, puesto que ésta puede tener cambios que todavía no están preparados para pasar a producción.

En este documento vamos a definir los principales pasos para llevar un control del código fuente utilizando Microsoft SourceSafe 6.0, así como la gestión de las distintas versiones de un proyecto. No obstante, este documento no pretende suplantar la ayuda en línea en MSDN (muy completa en este caso):


2. Proceso de gestión de versiones

A lo largo del presente documento vamos a suponer un caso real, que va a ir evolucionando y en el que vamos a ir aplicando las secciones que mostramos.

Además, para los ejemplos concretos utilizaremos un proyecto en Microsoft Visual Studio 2005, aunque todas las acciones a llevar a cabo las realizaremos directamente en SourceSafe, con lo que no es en absoluto necesaria dicha herramienta.


2.1. Etiquetas (Flags)

Una vez el desarrollo de la aplicación alcanza un objetivo, llega el momento de poner una marca al código fuente indicando esta situación, especialmente con la idea de tener la posibilidad de volver a este punto del desarrollo en el futuro. Para ello se utilizan las “Etiquetas” o “Flags”, tal y como se puede observar en la imagen:


Aquí nos saldrá una ventana en la que nos pide un nombre para esta Etiqueta (hasta 31 caracteres), así como una descripción de la misma. Es mejor no ser demasiado críptico con el nombre ni demasiado rácano con la descripción, ya que cuando lo tengamos que utilizar en el futuro es mejor no tener que especular sobre qué queríamos decir semanas (o meses) antes.

De esta forma SourceSafe pone la etiqueta en la versión actual a todos los archivos que forman parte de la solución.

En nuestro ejemplo, vamos a suponer que los fuentes han alcanzado un grado de madurez suficiente como para liberar la versión 1.0 de nuestra aplicación, con lo que en el nombre de la etiqueta vamos a poner “v1.0”, y en la descripción algo como “Primera versión estable en producción”. Nótese que la etiqueta se la ponemos a todo el proyecto.


2.2. Mostrar Historial (Show History)

Desde SourceSafe siempre tenemos la opción de ver el historial de un elemento (proyecto, carpeta o simple archivo). Al hacerlo, ya sea desde el botón derecho o desde el menú Archivo, tendremos una imagen que se parecerá a esta (imagen tomada marcando la opción de “Recursivo” para que muestre el histórico de todas las carpetas que contiene el proyecto):


Desde este momento empezamos a modificar el proyecto con nuevas funcionalidades y correcciones con el objetivo de ir preparando una nueva versión del mismo. Así, el histórico pasa a mostrar este estado de los archivos del proyecto:


Es en este momento cuando detectamos (realmente nos lo detectan) un error en el programa que tenemos en producción, y que tiene que ser corregida inmediatamente. La versión que hay en producción es la v1.0, pero nuestros archivos con el código fuente ya han sufrido algunas modificaciones, lo que hace que no las podamos utilizar para corregir este error porque incluiríamos cambios que todavía no están aprobados.


2.3. Compartir (Share)

Con la acción de Compartir creamos una copia de los archivos del proyecto a partir de un momento dado. En nuestro caso, lo que necesitamos es modificar el código fuente según estaba en el momento de la versión 1.0, con lo que buscamos en el histórico esta Etiqueta (podemos seleccionar “Mostrar sólo etiquetas” en la ventana de búsqueda para simplificar la selección):


En la siguiente pantalla nos da la opción de indicar desde dónde compartimos (“Compartir desde …” en español o “Share from …” en inglés), pero lo de opción no es muy correcto. En la práctica hay que pegarle al raíz (root) del proyecto. Es importante que no esté marcada la casilla de “Bifurcar después de compartir” (“Branch alter share”).



A continuación nos pide un nombre, y en honor a la originalidad de ponemos “PruebaVersiones v1.1”. Nuevamente, es importante que esté marcada la casilla de “Recursivo” para no dejarnos los subproyectos que pueda tener.

Este proyecto compartido no tiene definido ningún directorio de trabajo, así que podemos pasar a indicárselo. Es recomendable nombrar el directorio de una forma suficientemente clara para evitar confusiones. En este caso vamos a crear un nuevo directorio de trabajo (Working Folder) llamado igual al nombre compartido: “PruebaVersiones v1.1”, aunque podríamos ponerle cualquier otro. Una vez hecho esto nos traemos la última versión de la forma habitual (marcando “Recursivo” y “Construir árbol de directorios”).

De esta forma ya tenemos los archivos tal y como estaban en el momento en el que le pusimos la etiqueta de v1.0, sin los cambios posteriores. Sin embargo esto no es una copia, sino que SourceSafe sigue controlando internamente las diferentes acciones en los archivos. Para empezar, el hecho de “Compartir” hace que todos los archivos creados como v1.1 estén “Fijados”, es decir, están marcados de tal forma que no nos permite ninguna modificación en los mismos.


2.4. Bifurcar (Branch)

El siguiente paso es quitar la fijación (desfijar suena demasiado mal) de los archivos que necesitan ser modificados para corregir los errores encontrados en producción. Para ello utilizamos la opción de bifurcarlos en lugar de simplemente quitarles esa fijación (me estoy dando cuenta que esto tampoco suena muy bien).

La diferencia entre “Desfijar” (“Unpin”) y “Bifurcar” (“Branch”) es que el segundo permite volver a combinar los cambios con la versión inicial de una forma automática, mientras que el primero no. Por lo tanto, aplicamos la bifurcación al archivo que tenemos que modificar, esta vez desde el menú “Versiones -> Bifurcar” teniendo seleccionado el archivo deseado (yo tampoco entiendo porqué no está la opción en el menú contextual o “de botón derecho”).


Una vez hecho ya podemos hacer las modificaciones necesarias en dicho archivo. Generar nuevamente el proyecto, probarlo, instalarlo en producción y cualquier cosa que queramos o necesitemos según nuestra metodología de trabajo (si tenemos).

Después de realizadas las modificaciones para la versión 1.0, es posible que queramos incluirlas también en la versión actual del proyecto. Recordemos: esa que está en plena fase de desarrollo con el objetivo de lanzar la versión 2.0. Esto lo podemos hacer de forma manual en el código fuente actual, pero también puede que queramos, sin encomendarnos a nadie, que sea el propio SourceSafe de forma automática.


2.5. Combinar versiones bifurcadas (Merge Branches)

Esta es la forma en la que SourceSafe incluye los cambios de una versión de un archivo en otra. Para ello se selecciona el archivo en el que queremos incluir las modificaciones hechas, a través del menú “Versiones -> Combinar versiones” (ésta opción tampoco está en el menú contextual; las preguntas a Microsoft):


Insisto: “en el archivo en el que queremos incluir las modificaciones”. Es decir, si nuestra intención es incluir en el archivo de desarrollo actual (próximo a la versión 2.0) los cambios hechos en la versión antigua (etiquetada como v1.1) es el archivo actual el que debemos tener seleccionado al pulsar esta opción.

A continuación nos aparece una ventana en la que tenemos que elegir con qué archivo lo queremos combinar. Lo normal es que elijamos el mismo archivo de la versión v1.1. Por suerte, SourceSafe está atento y sólo nos habilita el botón de Combinar (Merge) cuando hemos seleccionado un archivo válido.


El ojo hábil habrá notado que he cambiado el fichero de la prueba (antes Class1.cs, ahora Form1.cs). El motivo es que el previo ha sufrido demasiadas pruebas y la pantalla de ejemplo no iba a ser nada clara.

Cabe la posibilidad de que tengamos conflictos en este proceso, principalmente cuando la misma línea ha sido modificada en ambas versiones. En estos casos nos saldrá una ventana en la que tendremos que indicar qué líneas tienen que ser incluidas en la versión. Esta elección se realiza de una forma visual y totalmente intuitiva, “pinchando” el cambio que queremos incluir (se pueden incluir ambos). Francamente, me ha sorprendido (para bien):


Con esto ya tenemos los cambios tanto en el parche para salir del paso en producción (versión 1.1) como en la versión del código fuente en desarrollo. Sólo nos queda volver a “Fijar” los archivos modificados.


2.6. Fijar (Pin)

Una vez que hemos terminado de hacer los cambios, lo ideal es volver a “Fijar” los archivos de la versión v1.1. ¿Para qué? Para evitar que en un descuido volvamos a modificar esta versión y olvidemos propagar el cambio a la última versión del código fuente.

Desde mi punto de vista (que es personal e intransferible) es preferible que para volver a hacer una modificación sobre esta versión “del parche”, tengamos que repetir los pasos de “Bifurcación” y “Combinación”.

Para fijar manualmente una versión de un archivo (la vez anterior se fijó de forma automática al compartirla) abrimos su histórico y pulsamos en el botón Fijar (Pin):



3. Conclusión

Los sistemas de control de versiones (SCV) como Microsoft SourceSafe son muy útiles, abarcando una amplia gama de funciones desde el simple sistema de copia de seguridad del código fuente, hasta tareas mucho más complejas como gestión de Ramas (Bifurcaciones o Branches) como la que se ha apuntado ligeramente en este documento.

La utilización de estos SCV se vuelve imprescindible cuando la complejidad de los programas crece, tanto por su tamaño como por las personas involucradas en su desarrollo.

Independientemente de (o además de) estos motivos, es una buena idea mantener los saludables hábitos de su (correcta) utilización para evitar sustos y disgustos cuando la vida real se cruza con la bonita teoría del diseño y desarrollo de aplicaciones.


4. Enlaces

Como dijimos al principio, todo lo nombrado en este documento está mucho mejor explicado en la página de la ayuda de MSDN de Microsoft SourceSafe:

http://msdn2.microsoft.com/es-es/library/ms181038(VS.80).aspx


Sin embargo, no es ni mucho menos el primero ni el único programa con este fin. Un buen punto por donde empezar a conocer algún otro es Git: el programa utilizado actualmente para controlar las distintas versiones de kernel de Linux:

http://git.or.cz/


Mucha más información en la Wikipedia:

http://es.wikipedia.org/wiki/Control_de_versiones (español)
http://en.wikipedia.org/wiki/Version_control_system (inglés)


5. Agradecimientos

Este documento ha sido redactado para uso interno de mi departamento en mi trabajo y está publicado aquí con el conocimiento y la aprobación de mis jefes. Pues eso.

viernes, 15 de febrero de 2008

ReportDocument: "No se ha podido cargar el informe"

Una nueva entrega de errores en Visual Studio y cómo evitarlos, aunque esta vez se trata de una mala utilización del componente ReportDocument -empleado para mostrar informes en Crystal Report-. O mejor dicho, trataré de cómo evitar las malas prácticas de programación. Por decirlo fino.

Tenemos una aplicación web desarrollada en ASP.NET en la que mostramos información sobre albaranes de envío a través de un Crystal Report. Esta aplicación está publicada en una intranet donde, en determinados momentos, puede llegar a haber hasta 40 usuarios mas o menos simultáneos.

Después de hacer los desarrollos y probadas todas las opciones sin ningún error, decidimos pasar el sistema a producción. Pero, oh campos de soledad, oh mustios collados, resulta que después de llevar unos días empieza a dar un error de lo más raro:

No se ha podido cargar el informe.

Primer problema: Estábamos capturando la excepción y mostrando únicamente la información de la propiedad "Message". Esto a veces está muy bien, pero en otras la información contenida aquí no dice nada, y nos hace falta saber mas, es decir, "InnerException".

Este error tenía unos efectos colaterales nada desdeñables, puesto que a partir de este momento ya no se podía mostrar ningún otro informe, ni en esta sesión ni en ninguna del mismo usuario o de otros, teniendo que reiniciar el IIS -el día que deje de funcionar lo de salir y volver a entrar no se qué vamos a hacer-.

Reproducir el error en desarrollo era bastante complicado porque no sabíamos bajo qué condiciones se daba, así que lo siguiente fue sacar toda la información del error. Como ya he dicho, fue en "InnerException" donde empezamos a encontrar la luz al final del túnel con este mensaje:

Se ha alcanzado el límite máximo de tareas de procesamiento de informes configuradas por el administrador del sistema.

Genial. Desde el inicio ya había un cierto tufillo a recursos no liberados, y este mensaje parece que va por ahí. Próximo paso, ver si estamos liberando todo lo que utilizamos. Después de una búsqueda rápida no encontramos en todo el código ningún Dispose() ni ningún Close() del objeto que utilizamos para mostrar el informe. Mecachis.

Lo que faltaba era cerrar el ReportDocument pero claro, la programación web no tiene mucho que ver con la basada en formularios, y no siempre tenemos claro cuándo se producen los eventos. Además tenemos los famosos postback, que hace necesario entender cómo funcionan para no andar repitiendo procesos que ralentizan la carga de la página sin necesidad.

Así pues, ¿dónde cerramos el ReportDocument? No puede ser en el mismo procedimiento que lo crea, porque todavía no lo ha mostrado. El sitio natural es en el evento Page_Unload, ya que es el momento en el que termina de procesarse la página... cuyo concepto es algo parecido al evento Leave de un Windows.Form -insisto, parecido-. Es decir, algo como:


protected void Page_Unload(object sender, EventArgs e)
{

if ((informe != null) && informe.IsLoaded)

informe.Close();

}

[Actualización 06/11/2008: Además de informe.Close(), no estaría demás hacer también informe.Dispose(), tal y como me han dicho en varios comentarios de esta entrada. :-)]

"informe" es una variable del tipo ReportDocument privada de la página. En este fragmento de código se puede ver cómo primero comprobamos si está instanciada -no lo estará si no hemos encontrado el albarán de carga buscado- y después comprobamos si tiene datos cargados -esta propiedad cambia a true al utilizar el método SetDataSource(), entre otros-.

Con esto terminaron nuestros quebraderos de cabeza, al menos en lo relativo a este error.


PD: Al que hizo esa página le tenemos escribiendo 1024 veces "Liberaré los recursos cuando termine de utilizarlos". :-)

martes, 12 de febrero de 2008

El que avisa no es traidor

Eso es lo segundo que pensé después de ver el siguiente mensaje; lo primero fue que había leido mal. El aviso corresponde al Service Pack 1 de Visual Studio 2005:


Pues eso, que el que avisa no es traidor. Es avisador.

miércoles, 6 de febrero de 2008

ComboBox en Visual Studio 2005

Cuando estás acostumbrado a hacer las cosas de una forma, suele costar un poco cambiar la mentalidad y hacerlas de otra distinta. Independientemente de que ésta nueva forma sea mejor o más cómoda.

Cualquiera que haya tratado con los famosos ComboBox -en algunos libros se traduce como cajas desplegables- ha podido utilizar esa característica mediante la cual no puedes escribir en él -se suele decir que el estilo es DropDownList- pero si pulsas varias teclas, se posiciona en el elemento cuyo comienzo corresponde con ese inicio.

Ahora las cosas han cambiado, y esto ya no funciona así en Visual Studio 2005. En esta versión, la herramienta de desarrollo de Microsoft ignora las teclas que has pulsado previamente, y sólo se queda con la última, seleccionando el primer elemento que comienza con esa letra -o número-.

Esto descoloca al más pintado, que con la incredulidad todavía pintada en la cara se pone a buscar una solución por el basto mundo de los foros de ayuda. Y la solución por llamarle de alguna forma está en esta nota del Soporte Técnico. Lo mejor de todo es la explicación oficial de la causa, que viene a ser literalmente:

This problem occurs because the ComboBox search is based on one character instead of the complete character set.

O utilizando la herramienta de traducción de la misma página:

Este problema se debe a la búsqueda ComboBox basarse en un carácter en vez del juego de caracteres completo.

Pensarás genial, si está encontrada la causa, ahora sólo queda poner un remedio. Pero no, resulta que la solución que propone el mismo documento técnico es la utilización de un Timer, el evento KeyUp del ComboBox y un código que funciona fatal y crea un efecto visual como para echar la pota. Así, al más puro estilo Pepe Gotera y Otilio.

Hasta aquí la crítica a la chapuza. Ahora pasamos a la solución de verdad, y es que la gente de Microsoft ha decidido que esto ya no se hace así, sino con otras dos nueva propiedades y una forma de trabajar muy distinta. Estoy hablando de:

AutoCompleteMode = SugestAppend
AutoCompleteSource = ListItems

La primera propiedad va mostrando los valores que coinciden (de la misma forma que funciona la barra de direcciones de Firefox o Internet Explorer) al mismo tiempo que completa el texto con la primera coincidencia (hay otras dos opciones igual de válidas, pero a mi me gusta ésta). La segunda propiedad dice que la lista de valores posibles para “autocompletar” son los propios Items del ComboBox.

El único problema es que así no te aseguras de que te han elegido un elemento de la lista -imaginemos que no permitimos la iniciativa individual -. Así, no nos quedará otra opción que incluir en el evento Leave del ComboBox un código más o menos como este:

if (comboBox1.Text != "") && (comboBox1.Items.IndexOf(comboBox1.Text) == -1)
{
MessageBox.Show("El valor elegido no está en la lista.");
comboBox1.Focus();
}


Por cierto, en el código anterior he puesto comboBox1 para facilitar su lectura y comprensión. Lo ideal es poner en su lugar (sender as ComboBox), ya que de esta forma el mismo código sirve para todos los eventos Leave de todos los ComboBox, consiguiendo así ese mito de la reutilización.

Esta es la forma que he encontrado para hacerlo, pero si estás leyendo esto y sabes otra mejor, por favor, no dudes en decírmelo.

jueves, 31 de enero de 2008

AllowDBNull

Mi inglés es bastante malo, pero esto se entiende bastante bien. Se trata de una propiedad de los DataSet fuertemente tipados, de Visual Studio 2005. Sus posibles valores son True o False. Bueno vale, realmente es una propiedad de los campos de las tablas de los DataSet fuertemente tipados, pero nos entendemos.

Parece muy sencillo, ¿verdad? Y lo es hasta que intentas utilizarlo. Pongamos por caso que siguiendo las recomendaciones te creas un DataSet tipado y en una de las columnas, por necesidades del guión, le marcas ese AllowDBNull a True. Luego, en un arranque de euforia decides cargar datos desde un fichero XML -que no se cómo hemos podido vivir hasta hoy sin el formato XML-.

Y es en ese momento donde el muy borde de Visual Studio te suelta un mensaje del estilo "estás intentando meter un valor null". Claro, contestas tú, y ese campo admite valores nulos, porque yo lo he puesto, y porque da la casualidad de que ese es precisamente el valor en la base de datos. No un cero, ni una cadena de caracteres vacía, ni el 01/01/1900. El valor exacto es null, que se representa en Visual Studio 2005 como DBNull.

Y es que resulta que no, que el hecho de que la propiedad se llame AllowDBNull no significa que "Allow DBNull", sino que debe significar otra cosa misteriosa y desconocida que un cachondo de Microsoft puso en un día de inspiración.

Como no terminaba de creérmelo, busqué una solución por los foros de Microsoft, pensando que era yo quién lo estaba haciendo mal. Pero resulta que no, que en un post de los foros en inglés un tipo -que tenía la oportunidad de hablar con el desarrollador del invento-, contaba que la implementación de esta propiedad fue paralela a la de las variables tri-estado -recordemos que hasta C# 3.0 no hay variables que permitan null-, así que pa'qué te digo que si, si no.

Con lo que si estás leyendo esto ya sabes, no te molestes en creerte lo de AllowDBNull, porque es un engaña-tontos. Y yo el primero.