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

Un formulario para varias tablas

Digamos que tengo una docena de tablas con la misma estructura: 3 campos (Id para la clave primaria, Nombre y Descripción) con las mismas características. Estas tablas, además, van a tener pocos registros, no llegarán a 20. Concretamente, forman parte de una base de datos de series y películas y almacenan los datos de, por ejemplo, códecs, formatos de archivo, resoluciones, el soporte (DVD, CD, disco duro) donde están almacenados, etc. La pregunta es ¿cómo gestiono el contenido de estas tablas (altas, bajas y modificaciones)? A cada cual se le ocurrirá una opción, la mía, con pocas ganas de complicarme la vida, es la siguiente:

Tengo un formulario sencillo con los siguientes elementos: un ListBox que mostrará el nombre de todos los elementos de la tabla y servirá para navegar por los registros, dos TextBox, uno para el Nombre y otro para la Descripción, con sus respectivas etiquetas, y los controles necesarios para la gestión (podrían ser botones, pero yo uso un MenuStrip): Nuevo, Modificar, Eliminar, Aceptar y Cancelar. Añadimos un BindingSource a la ecuación y, si nos olvidamos por el momento de la generación de la clave primaria, esto ya está hecho, independientemente de lo que pongamos debajo del BindingSource.

Sigue leyendo

Corrigiendo un… problema de diseño con un evento

Si ya me lo decían: «no vayas a Z’ha’dum«. Pero yo, ni caso. Me puse a hacer una interfaz MDI tan ricamente para la aplicación de fábrica sin pensarlo con detenimiento (eso también me lo decían, en este caso Coco y Barrio Sésamo, lo de «tienes que planificar», pero, igualmente, ni caso) y ahora me encuentro con lo normal: mucho impedirle cambiar de registro cuando está modificando una pieza, por ejemplo, pero nada le impide irse a otro formulario a trastear allí. Y, como es un luser, lo hará. O sea, que me pueden coger en un renuncio (error de concurrencia en el mejor de los casos) sin necesidad de tener dos lusers, con uno me basta y sobra.

Vale, que no cunda el pánico. Pensemos. O cubro bien todas las posibilidades o me curo en salud y no le dejo tocar al usuario donde no deba. Como soy alumno de mi profe, he optado por lo segundo. Así que quiero que cuando el luser quiera modificar o dar de alta «algo» en uno de los formularios, todos los demás se hagan los suecos (disabled). Pregunta: ¿cómo lo hago?

Sigue leyendo

Error al acceder a una DataRow borrada mediante una consulta LINQ to DataSet

Estaba haciendo unas pruebas a una parte de la aplicación de fábrica que debería funcionar cuando me casca todo, de manera catastrófica, dando un bonito y magnífico error de DeletedRowInaccessibleException, así, en mi cara. Estaba añadiendo, borrando y modificando varias filas de un datatable y, en un momento dado, ha intentado acceder a una de las filas borradas. Guay.

Rastreando el error lo he encontrado en una sentencia LINQ que se ejecuta sobre la datatable. La muy puñetera arrampla con todo y, claro, lo que está borrado está borrado aunque siga estando ahí. Tsktsktsk.

La solución ha sido rápida: añadir una cláusula WHERE a la sentencia LINQ para que no incluya las filas borradas. O sea, algo como:

Where Fila.RowState <> DataRowState.Deleted

Mover los elementos de un ListBox

Hay veces que lo que parece más simple da unos dolores de cabeza terribles. Estaba yo, feliz y despistado, queriendo montar un listbox y dos botoncitos, uno con una flecha para arriba y otro con una flecha para abajo, de forma que, al pulsar el botoncito con la flecha hacia arriba, el elemento seleccionado en el listbox intercambie su posición con el que tiene encima (vulgo, suba) y cuando pulse sobre el botoncito con la flecha para abajo… en fin, creo que os lo imagináis.

Pues tiene su aquél. Bueno, vale, no tiene mucho «aquél» pero me ha costado un buen rato dar con la tecla. O sea, sacar el elemento, borrarlo, insertarlo en su nueva posición. Ahora mismo no estoy seguro, pero creo haber hecho algo parecido en VB6 sin necesidad de dar tantos pasos. Por ejemplo, el código del click del botón de subir quedaría más o menos así:

Dim MiMecanizado As MecanizadoPieza
Dim i As Integer = Me.lstOrdenMecanizado.SelectedIndex
If i > 0 Then
    MiMecanizado = TryCast(Me.lstOrdenMecanizado.SelectedItem, _
                          MecanizadoPieza)
    If MiMecanizado Is Nothing Then Exit Sub
    Me.lstOrdenMecanizado.Items.RemoveAt(i)
    Me.lstOrdenMecanizado.Items.Insert(i - 1, MiMecanizado)
    Me.lstOrdenMecanizado.SelectedIndex = i - 1
End If

Donde MecanizadoPieza es una clase que incluye un campo Descripción (DisplayMember) y un campo Id (ValueMember) y lstOrdenMecanizado es el listbox de marras.

Si alguien lo encuentra útil, pues mejor.

Trasteando en un dataset: seguimos jugando con valores timestamp

Hace unas semanas hablé de usar valores timestamp para controlar la versión del registro de cara a controlar a su vez los problemas de concurrencia, para actualizar datos, etc. Hoy voy a dar un posible uso a todo eso, usando dataset tipados. Vamos a trabajar con una tabla de ejemplo. Supongamos que:

  1. La tabla en cuestión tiene definida una columna, Control, del tipo timestamp.

  2. En el TableAdapter correspondiente hemos añadido dos consultas: una que nos recupera el MAX(Control) de la tabla (el valor más alto de control) y otra que nos devuelve las filas actualizadas desde que obtuvimos ese MAX(Control), o sea, aquellas cuyo valor timestamp sea mayor que el que tenemos guardado.

Sigue leyendo

No es conmutativo, no

Supongamos lo siguiente: tengo un formulario con, entre otros, los siguientes controles:

  1. Un ListBox que se usa como control de navegación. En este caso, muestra los nombres de todos los clientes. Está enlazado mediante un BindingSource a la DataTable de clientes.

  2. Un TextBox que muestra la fecha de adquisición de «algo» por el cliente.

  3. Un DateTimePicker que permite seleccionar la fecha de adquisición de «algo» por el cliente.

El DateTimePicker no se muestra cuando estamos en modo «Ver datos», sólo al crear o modificar un «algo» comprado por el cliente. Para ello tenemos un método que se encarga de que en modo «Ver datos» se vea el ListBox y el TextBox y en modo «Altas y modificaciones de algos» se vea el DateTimePicker y no el ListBox ni el TextBox.

Hasta aquí sencillo, ¿no?

Bien, pues si al pasar a modo «Ver datos» el orden es el siguiente:

MiListBox.Visible = True

MiDateTimePicker.Visible = False

MiTextBox.Visible = True

Si el DateTimePicker tiene el foco se selecciona el último cliente que hay en el ListBox. Sin embargo, si el orden es:

MiListBox.Visible = True

MiTextBox.Visible = True

MiDateTimePicker.Visible = False

Se queda seleccionado el cliente en el que estaba.

Supongo que sus razones habrá, pero a mí me lleva dando la tarde. Y no ha sido mucho tiempo porque la gestión de las Visitas también tiene su DateTimePicker y su TextBox, pero por casualidad en el orden correcto y sólo he tenido que comparar línea a línea qué se hacía en Visitas y no en «algos», que si no…

Mecanizado de pieza, te odio

Llevo unos días más gruñón y contestón que de costumbre. Las razones son, principalmente, dos. La primera es que estoy incubando algo: me duele la garganta, me zumban los oídos… veremos mañana como me levanto. La segunda es que estoy atascado con la aplicación que ando haciendo para fábrica.

El jefe decidió este verano dos cosas: la primera es que había que hacer una base de datos de lo que fabricamos para poder sacar unas órdenes de fabricación en condiciones, gestionar nuestros productos, control de stock y lo que se tercie, que las hojas manuscritas al final se pierden o de tantas tachaduras y enmiendas no se entienden. Y más teniendo en cuenta que anda reduciendo plantilla y, claro, si echa al responsable de tal colección la llevamos clara porque es él quien sabe cómo se hace cada pieza.

La segunda cosa que decidió el jefe es que me encargara yo del asunto. Sin quejas por mi parte, porque así practico y le doy caña a Visual Basic. Llevo dos meses muy entretenido dando caña a los datasets. Este julio estuve preparando las especificaciones (no las hice bien, para variar) y documentándome sobre LINQ to SQL.

Sigue leyendo