Archivo de la categoría: Programacion

Leer ficheros csv y de campos de ancho fijo en Visual Basic

En mi vuelta al servicio activo he tenido que procesar dos ficheros en texto plano: uno con campos separados por un carácter y otro con campos de ancho fijo. En su día ya lo hice (concretamente para un csv, valores separados por comas), pero no me acordaba de cómo lo hice (fue al poco de empezar con VB2005) y no tenía a mano ni el código que hice ni el libro que usé de referencia, así que busqué en Internet.

Y fíjese usted por donde, encontré una forma más simple de hacer las cosas que no conocía: usando la clase TextFieldParser.

En la forma más fácil, le pasamos en el constructor la ruta del archivo a tratar. Le indicamos luego qué es, si de ancho fijo (FixedWith) o separado por carácter (Delimited) con la propiedad TextFieldType. Si es el segundo caso, con el método SetDelimiters indicamos el delimitador (por ejemplo, MiTfp.SetDelimiters(vbTab) para indicar campos separados por una tabulación).

En el primero, indicaríamos el ancho de los campos mediante una lista de enteros que le pasamos al método SetFieldWiths: MiTfp.SetFieldWiths(2, 3, 12, 85, 5).

Para leer cada línea tenemos el método ReadFields, que nos devuelve un array de cadenas con cada campo de la línea del fichero en la posición correspondiente del array. Se tarda más en explicar que en hacer.

Hay un pequeño problema adicional: la codificación de caracteres. Por defecto, se usa UTF8. Ahora mismo no recuerdo si Vista y 7 usan UTF8 por defecto, pero XP y versiones anteriores de Windows, no, y podemos encontrarnos con problemas con algunos caracteres, como ñ, tildes, etc. En ese caso, seguramente estemos usando Windows occidental (Windows 1252) o, más raramente, ISO-8859-1. Podemos indicarle esto al TextFieldParser en el constructor, pasándole el código de la página de códigos a emplear:

Dim MiTfp as New TextFieldParser(MiRuta, System.Text.Encoding.GetEncoding(1252))

Lo más largo, como siempre, es el control de errores, o el propio tratamiento de los valores que hayamos leído, pero el hecho de leer en sí más simple no puede ser.

DataRepeater y el evento Enter de sus controles

A la hora de evitar que el usuario manazas edite el valor de algún control, una opción habitual es controlar el evento Enter de ese control o de su contenedor (un GroupBox, un Panel…), mandando el foco a otro control mediante el método OtroControl.Select(). Esto funciona para buena parte de los controles disponibles en Windows Forms, pero no para todos (dtpicker, optionbutton… hay varios que pasarán de nosotros).

Si nosotros queremos usar este sistema con controles que estén dentro de un DataRepeater… En fin, probadlo si queréis, pero ya os digo que la aplicación se quedará muerta si mandáis el foco a un control que esté fuera del DataRepeater. ¿Entonces?

Pues no salgamos. Pasemos el foco al ítem del DataRepeater al que pertenezca el control y listo:

MiDataRepeater.CurrentItem.Select()

El control DataRepeater es más versátil que el habitual DataGridView, pero es caprichoso como él solo.

Evitar error en consultas de agregado de LINQ

Nombre pomposo de la entrada pero que contesta a un problema que se me ha presentado esta mañana (volver a trabajar tiene estas cosas): al calcular el valor máximo de un campo en un conjunto de datos con una consulta LINQ sobre un dataset (por ejemplo, el importe más alto de un determinado cliente) se producía una excepción si no se devuelve ningún valor (en el ejemplo, si ese cliente no tiene pedidos). Una búsqueda por Internet me ha apuntado como solución usar extensiones de métodos, algo que no domino y se me antojaba excesivamente complicado para la tarea.

Tras pensarlo un poco, he probado a convertir el valor del campo pasado a la instrucción MAX a un tipo Nullable, que acepta valores nulos. En mi caso, como era valores enteros, esa parte de la consulta LINQ quedaría tal que así:

Into Max(Ctype(Fila.Campo, Integer?))

La consulta ya no da error, y sólo nos queda controlar si hay valor devuelto (propiedad HasValue), convertirlo al tipo que queramos (Integer, en el ejemplo) y seguir adelante. Más sencillo se me hace que otras ideas que he visto por ahí, y más rápido.

Como siempre, si a alguien le resulta de utilidad, pues mejor.

Jugando con colecciones Dictionary

Esta entrada podría valer como la tercera de la serie «Mantener actualizados unos ComboBox», pero va en modo corto, que es nochebuena y me dan ganas de hacer algo más acorde con la fecha (ver La tumba de las luciérnagas, por ejemplo). Hagamos un breve resumen: tenemos un formulario maestro-detalle de series y las releases o distintas versiones de esas series. Como el número de releases por serie es pequeño (0 a 3 ó 4) y el número de campos de la tabla releases es amplio, en lugar de usar una rejilla o algún control parecido, decidí emplear un TabControl que mostrara tantas pestañas como releases y, en cada una, un UserControl con todos los datos. Es decir, esto:

Sigue leyendo

Usando un TableAdapterManager

Una de las entradas de este blog que más visitas registra hace referencia a un error en el código generado de los TableAdapterManager, un componente de acceso a datos para usarlo junto con datasets tipados y sus respectivos tableadapters. Dicho error fue solucionado en el SP1 de Visual Studio y de las versiones Express, por lo que ya creía el tema olvidado. Sin embargo, parece que su uso despierta bastantes dudas, así que me vais a permitir que retome el tema con un pequeño ejemplo.

Un TableAdapterManager es un componente que se genera al crear un dataset tipado. Por lo tanto, si no usamos un dataset tipado ni necesitamos saber que tal cosa existe. Un TableAdapterManager automatiza el proceso de actualizar varias tablas relacionadas, manteniendo (o intentándolo) la integridad de los datos al hacer los update, insert y deletes en el orden apropiado. Hemos dicho que el TableAdapterManager se crea con un dataset tipado, por lo que sólo tendrá utilidad si este dataset tipado tiene varias tablas relacionadas.

Veamos el siguiente ejemplo, un dataset tipado, SeriesDataSet, con tres tablas: Series, Género y GeneroSerie. GeneroSerie es la tabla que permite la relación n:m entre géneros y series (una serie puede tener varios géneros, puede ser un drama romántico de ciencia ficción, por ejemplo).

Sigue leyendo

Mantener actualizados unos ComboBox (II)

No tenía intención de volver sobre este tema pero, la verdad, tampoco tengo nada más que escribir. Y, al final, fue un problema curioso cuya resolución no me quedó tan elegante como a mí me hubiera gustado. El primer problema que me dio la solución que comentaba fue cuando intenté ordenar el contenido de la BindingList. Mi gozo en un pozo: la BindingList no ordena. Me resulta chocante que la BindingList, que se supone va mejor para enlace a datos, no ordene ni busque, y una List sí. De hecho, por internet e incluso en la misma documentación de MSDN encontramos clases derivadas de BindingList que ordenan y/o buscan. De hecho, encontré una para un caso general que me ha gustado mucho y me la he guardado, pero para esta aplicación he preferido usar una clase preparada para la ocasión.

Partíamos, recuerdo, de una clase muy sencilla, IdMasDescripcion, con dos propiedades, Id de tipo Integer que se corresponde a la clave primaria, y Descripcion de tipo String. La clase, además, implementa la interfaz IComparable(Of T), y el método CompareTo lo que hace es comparar las descripciones. Lo que voy a hacer, entonces, es una BindingList(Of IdMasDescripcion) personalizada que sea capaz de ordenarse. Pero no siempre va a estar ordenada (depende de qué tabla muestre) y la ordenación puede ser alfabética o por resoluciones (de momento; en un futuro puede que me haga falta un tercer tipo). Voy a necesitar un método para ordenar (Sort), y dos campos que me indiquen si la colección está ordenada (IsSorted) y si hay que ordenarla (ToSort):

Sigue leyendo

Ordenando por resoluciones

Esto es una tontería del tipo tonterida, pero me ha llevado un ratito escribir el código, así que lo anoto aquí para cuando me vuelva a pasar, que entonces, seguramente, ya se me habrá olvidado y me tocará echar otro ratito y no me apetece. El problema es sencillo: hay que ordenar una lista de elementos de forma no trivial. En este caso concreto, hay que ordenar por una propiedad de tipo String, cadena de caracteres, que va a contener resoluciones de imagen, esto es, dos valores numéricos separados por una «x»: 640×480, 1024×768 y así. También serviría para cualquier lista de pares de valores y se podría extender a situaciones más complejas, como coordenadas espaciales y cosas así.

Lo primero ha sido elegir el algoritmo de ordenación. Como van a ser pocos elementos, me he tirado a los sencillitos y, de estos, al de inserción, que es el único que soy capaz de desarrollar sin mirar la chuleta (el de la burbuja lo tengo atravesado). El proceso es muy simple: vamos ordenando por el primer valor. Si este es igual, pasamos a ordenar por el segundo valor. En este caso concreto, ordenamos por largo (primer valor) y si son iguales, por ancho. Para ello, lo primero que he hecho ha sido crearme dos funciones que me devuelvan el largo o el ancho a partir de la cadena:

Sigue leyendo

Mantener actualizados unos ComboBox

Vamos con una de programación, que hace mucho que no cuento nada (básicamente, porque llevo unos meses sin programar; a ver si arranco motores). Me he puesto con mi aplicación de anime (un front-end para una base de datos de series, nada más, que me sirve de aprendizaje y experimentación). Ya me toca meter datos, sacar fallos, pulir el funcionamiento y terminar algunas funciones pendientes. Y recordar lo que estaba haciendo, que hace seis meses o así que no la tocaba.

Una de estas funciones, o funcionalidades, si lo preferís para no confundirlo con functions, es la de mantener actualizados una serie de comboboxes que hay en el formulario principal de las series. Estos comboboxes contienen los datos de una serie de tablas de la base de datos que se caracterizan por tener pocos datos: el formato de archivo, la resolución, el códec de vídeo… En otras circunstancias, los rellenaría usando mi clase RellenarCombo, pero aquí se me presenta un problema curioso: como podéis ver en la imagen, hay una buena cantidad de comboboxes (una docena o así) que se corresponden a una serie de campos de las Releases (versiones) de una serie. El formulario muestra conjuntamente las series y sus releases, estando en la parte superior los datos de la serie y en la inferior, en cero o más pestañas los datos de las releases. O sea, tengo una docena de combos por cada pestaña.

Aquí empezaron mis problemas: ¿cómo rellenar esos combos y mantenerlos actualizados?

Sigue leyendo

40 minutos perdidos por una tontería

Ayer hice el enésimo formulario de búsqueda (son aburridos de narices). Uno que me hacía falta para un usercontrol que forma parte de un asistente (vulgo, siguiente-siguiente-siguiente) que no sé en qué feliz día se me ocurrió que sería una buena idea. Hoy he embutido el usercontrol en un formulario de pruebas, para comprobar que hace lo que debe, y de paso ir ajustando tanto el usercontrol como el formulario de búsqueda: orden de tabulación, ámbito de los controles, propiedades varias.

En una de esas, el formulario de búsqueda ha decidido pasarse el ShowDialog por donde le da la gana y cerrarse sin hacer lo que debiera. Vale, partiendo de que no puede hacer eso, o sea, el ShowDialog es sagrado y que antes funcionaba, queda claro que alguien ha metido la pata. Como yo no he sido, la culpa debe ser, por narices, del manazas que comparte silla, ordenador y cuerpo con un servidor. Seguro que jugando con las propiedades de algo ha cambiado lo que no debía por error, y ahora no recuerda.

40 minutos después, 40 jodidos minutos de romperme la cabeza y revisar todos los controles (porque en ejecución paso a paso nothing de nothing) encuentro la causa: botón que tiene la propiedad DialogResult a algo que no es None. Creo que tocando algunas cosas del formulario asigné como AcceptButton o CancelButton al botón, luego lo quité, pero la propiedad DialogResult del puñetero botón siguió asignada.

En fin, el diseñador de Visual Studio ahorra trabajo, pero a veces dan ganas de pasar de él y escribir el código uno mismo.

MenuStrip heredado y vista diseño

Esto es un problema viejo y es fácil encontrar soluciones en Google, pero, bueno, supongo que repetirlo una vez más no viene mal.

La situación es la siguiente: tengo un formulario base, a modo de plantilla, que lo uso cuando tengo acceso a datos. Tiene una serie de controles y métodos entre los que se encuentra un MenuStrip con varios ToolStripMenuItem. Es un control (él y sus ítems) que da problemas con el diseñador de formularios de Visual Studio cuando es un control heredado. Aparece como de sólo lectura y no nos deja trabajar con él y lo digo en serio: no podemos añadir elementos al menú en vista diseño, ni cambiar ninguna propiedad (ni siquiera el texto mostrado) de los ítems que ya tenga. Tenemos que hacerlo directamente en código. Lo que es más incómodo: no podemos controlar un evento de un elemento ya existente.

Sigue leyendo