Archive for Septiembre, 2008

Controlando controles… o la maldita Win32Exception.

Martes, Septiembre 23rd, 2008

Llevo unos cuantos días sin aportar nada al blog, no por falta de ganas sino por falta de tiempo debido en gran medida a que... 1) me he quedado sin coche y eso me hace perder varias horas útiles a lo largo de la semana... y 2) ha comenzado ya el cuatrimestre en la facultad y todavía me estoy organizando para intentar no morir en el intento de sacar adelante los proyectos profesionales y los estudios al mismo tiempo. De todos modos estoy trabajando tanto en la traducción de los dos posts con más visitas del blog (quiero añadir algunos artículos en inglés en este blog para intentar servir de ayuda a más gente -por eso traduciré sólo los posts que aparecen como más visitados en las estadísticas-) como en la creación de una librería que me han pedido ya varias veces.

De todos modos hoy dejo un dato curioso. ¿Cuál es el número máximo de controles que se pueden agregar a un formulario en VisualBasic.NET? Probablemente la mayoría no se lo habrá planteado nunca porque la respuesta es en realidad "los suficientes" o "más de los que nunca he necesitado". ¡Pues no! Adjunto código e imagen del error que genera:

'--------------------------------------------------------------------
' Author:      Albert Mata (www.albertmata.net)
' Date:        20080923
' Description: Class to get a 'too many controls Win32Exception'.
'--------------------------------------------------------------------
Public Class MaximumNumberControls

    '----------------------------------------------------------------
    ' Simple (and silly) code to show how many controls we can add 
    ' in a VisualBasic.NET form before it crashes.
    '----------------------------------------------------------------
    Private Sub MaximumNumberControls_Load( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
        Dim TXT As TextBox
        Dim i As Integer = 1
        While True
            TXT = New TextBox()
            Me.Controls.Add(TXT)
            i += 1
            'Win32Exception when i = 9973 for TextBox!!!
            'Win32Exception when i = 9974 for Label!!!
            'Win32Exception when i = 9973 for Button!!!
            'Win32Exception when i = 9973 for Panel!!!
        End While
    End Sub

End Class

Así que en un formulario se admiten máximo unos 10.000 controles...

Todo esto viene a que ayer y hoy he estado peleándome con este asunto. Por un lado tengo un control personalizado que se forma con unos 150 controles entre TextBox y Label. Este control representa una previsión de stock de un determinado material para los siguientes N meses. Los TextBox y Label en cuestión son muy pequeños, pero todos muestran información única imprescindible que en algunos casos se debe poder modificar. No me sirve un DataGridView ni similares.

Sin embargo no tengo un solo material, sino cientos. De modo que cuando intento mostrar en un formulario un número demasiado elevado de estos controles personalizados obtengo ese error Win32Exception (concretamente al intentar mostrar el material 66, lógico porque 9.973 / 150 = 66).

Finalmente he encontrado la solución dando un par de vueltas al diseño de las clases involucradas. No la posteo porque es demasiado específica y nada interesante aportaría su estudio. Pero ya sabéis... si estáis diseñando controles personalizados un poco complejos, ¡tened cuidado cuando os acerquéis a los 10.000 controles en total! ;-)

PD. Ignoro si el número exacto depende de plataforma, versión de sistema operativo y demás. En cualquier caso la cifra es representativa...

Jugando con formatos de fechas en MySQL.

Jueves, Septiembre 4th, 2008

Este post es muy simple, pero su contenido me facilita mucho la vida desde que lo programé. Se trata de un par de simples funciones para convertir los dos formatos de fecha que más utilizo en el proyecto que estoy desarrollando. Estos dos formatos son por una parte el habitual DATE de MySQL (YYYY-MM-DD) y por otra parte un formato texto VARCHAR(8) que simplemente elimina los guiones (YYYYMMDD) de la fecha. Lo de utilizar este formato en modo texto viene porque recopilo mucha información de tablas en una base de datos externa en la que las fechas están almacenadas de esta manera.

De paso tiene la ventaja de que es insensible a configuraciones regionales que tienden a cambiar mes y día a la mínima que se les da una oportunidad...

Así pues, para moverme indistintamente con los dos formatos de fecha y no tener que andar preocupándome en cada ocasión si está en uno u otro formato, tengo estas dos minifunciones almacenadas en MySQL que me sirven de ayuda.

La primera es para pasar de formato DATE a formato VARCHAR(8):

#---------------------------------------------------------------------
# STORED FUNCTION: sf_date2strng 
#---------------------------------------------------------------------
# Author:      Albert Mata (www.albertmata.net)
# Date:        20080904
# Description: Takes a date in YYYY-MM-DD format and returns it in
#              YYYYMMDD format.
#---------------------------------------------------------------------
DROP FUNCTION IF EXISTS sf_date2strng;

DELIMITER //

CREATE FUNCTION sf_date2strng(mydate DATE) RETURNS VARCHAR(8)

BEGIN
    RETURN CONCAT(MID(mydate,1,4),MID(mydate,6,2),MID(mydate,9,2));
END
//

DELIMITER ;

Y esta segunda es para pasar de formato VARCHAR(8) a formato DATE:

#---------------------------------------------------------------------
# STORED FUNCTION: sf_strng2date 
#---------------------------------------------------------------------
# Author:      Albert Mata (www.albertmata.net)
# Date:        20080904
# Description: Takes a date in YYYYMMDD format and returns it in
#              YYYY-MM-DD format.
#---------------------------------------------------------------------
DROP FUNCTION IF EXISTS sf_strng2date;

DELIMITER //

CREATE FUNCTION sf_strng2date(mydate VARCHAR(8)) RETURNS DATE

BEGIN
    RETURN STR_TO_DATE(mydate,'%Y%c%e');
END
//

DELIMITER ;

Para almacenarlas en la base de datos MySQL basta con copiar todo este código tal cual en un archivo de texto, añadirle una primera línea...

USE nameofddbb;

...y guardar el archivo con un nombre corto y en una ruta facilita (esto es opcional, pero para qué complicarse la vida), como por ejemplo C:\in.txt. Hecho esto, basta con escribir en un símbolo de sistema de Windows (antes de entrar en MySQL):

mysql -u root -p < C:\in.txt

También podemos hacerlo con un usuario no root, pero tiene que tener suficientes permisos para poder crear un procedimiento almacenado en la base de datos concreta que hayamos especificado en esa primera línea del archivo de texto.

Una vez tenemos las funciones almacenadas en MySQL comprobamos que funcionan. Primero le introducimos una fecha para que nos la convierta a texto y vemos que funciona sin problemas:

mysql> SELECT sf_date2strng('2008-09-04');
+-----------------------------+
| sf_date2strng('2008-09-04') |
+-----------------------------+
| 20080904                    |
+-----------------------------+
1 row in set (0.00 sec)

También podemos introducirle una fecha completa (con hora) y funcionará también bien. Sin embargo nos avisará de que se ha generado un warning. Si lo miramos vemos que no es nada grave, simplemente nos informa de que ha desestimado totalmente la hora, lo cual no nos importa.

mysql> SELECT sf_date2strng('2008-09-04 18:23:45');
+--------------------------------------+
| sf_date2strng('2008-09-04 18:23:45') |
+--------------------------------------+
| 20080904                             |
+--------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS;
+-------+------+---------------------------------------------+
| Level | Code | Message                                     |
+-------+------+---------------------------------------------+
| Note  | 1265 | Data truncated for column 'mydate' at row 1 |
+-------+------+---------------------------------------------+
1 row in set (0.00 sec)

A continuación probamos la segunda función, introduciéndole un texto que representa una fecha para que nos la convierta a tipo DATE:

mysql> SELECT sf_strng2date('20081026');
+---------------------------+
| sf_strng2date('20081026') |
+---------------------------+
| 2008-10-26                |
+---------------------------+
1 row in set (0.00 sec)

A partir de aquí podremos utilizar estas dos funciones en cualquier consulta, vista u otro procedimiento o función almacenada que deseemos.