Cuando Double.MaxValue, no es Double.MaxValue

Creo que es uno de los primeros post que voy a escribir por absoluta desesperación. Y el título lo dice todo.

Estamos desarrollando una aplicación en mi empresa, donde usamos un librería “open source” construida de manera intachable. Bien estructurada, con sus clases, con sus interfaces, y todo en NET 2.0.

Ahora viene lo bueno, dentro de la librería, hay un:

public const double Missing = Double.MaxValue;

Esta línea, parece encarnar el mal, y tiene el número #630 dentro del sistema de bugtracking del proyecto (a partir de ahora el número del mal). En ciertos equipos (si, en ciertos, sin diferenciación mayor que ser otro equipo), provoca que el programa se salga (mejor dicho: explote), sin generar ninguna excepción (lo que implica un error por debajo incluso del NET) y sin dejar ningún registro de error. En cambio, si ponemos:

Si hacemos una asignación quitando el alias:

public const double Missing = 1.7976931348623157E+308;

Tenemos otro crash silencioso al ejecutar el programa. Incluso rebajando el exponente, tenemos el mismo fallo. Pero esperen, que viene lo mejor:

public const double Missing = 999999999999999999;

Entonces, todo el felicidad, los pájaros cantan y las nubes se levantan… No falla.

¿¿¿Porqué???. De verdad que no lo entiendo, y los oscuros mundos del NET me tienen frito.

Pues no termino de entender el porque. Puedo conjeturar que viene por la representación que le da .NET, no es representable por algún nivel más bajo de la máquina, o algún redondeo que implique un overflow. Pero la verdad que no he encontrado nada por la red, y menos aun por los foros de la librería. Pueden ser muchos factores, y me estaré dejando muchos sin tener en cuenta, pero desde luego ahora mismo estoy falto de ideas.

Please follow and like us:

Excel y .NET, malos amigos (III): ¿Como poner una imagen en un rango de celdas?

Otra nueva guerra con Excel, desde C#. Ahora el problema era introducir una imagen en un rango de celdas. Un verdadero problema, ya que la imagen tenia que ajustarse a ellas aunque se deformara. Además tenía que funcionar en Office 2003 y 2007. ¿Más madera?, no importa soy informático :P.

Pues bien, primer intento, me pongo manos a la obra y a tirar líneas:

// Rango donde se intertará la imagen
Microsoft.Office.Interop.Excel.Range r1;
r1 = (Microsoft.Office.Interop.Excel.Range) objSheet.get_Range("F5", "AB37");
r1.Select(); //Es necesario seleccinar un rango para poder insertar

// Objeto de todas las imagenes del excel
Microsoft.Office.Interop.Excel.Pictures oPictures =
(Microsoft.Office.Interop.Excel.Pictures)objSheet.Pictures(System.Reflection.Missing.Value);

//La ruta de la imagen donde esta la imagen a insertar,
//OJO es necesario indicar bien la ruta, de lo contrario marcará el siguiente error
//"Error en el método Insert de la clase Pictures" y pues eso despista ya que solo es
//la ruta y no la implementacion del método
string pathTEMP_BMP = System.IO.Path.GetTempFileName();
this._datos.im.Save(pathTEMP_BMP, System.Drawing.Imaging.ImageFormat.Bmp);

// Crear un objeto Picture de Office
Microsoft.Office.Interop.Excel.Picture p =
oPictures.Insert(pathTEMP_BMP, System.Reflection.Missing.Value);
// Redimensionar
p.Width = (double) r1.Width;
p.Height = (double) r1.Height;

Bueno, pues un medio “Fail”. Funciona bien en 2003, pero cuando se abre con Excel 2007, la imagen no se ajusta al rango “r1” de celdas. Miro a la pantalla con resignación torera y exclamo mi mítico: “Me cago en tó”.

Vale, no desesperemos, tras bucear un poco por internet, veo este articulo que me da otra buena idea. No usar el objeto “Pictures” de Excel, si no usar el “Shapes”. Vamos a intentarlo:

// Eliminamos apartir de la línea 17 en adelante y incluimos esto
objSheet.Shapes.AddPicture(pathTEMP_BMP, 
               Microsoft.Office.Core.MsoTriState.msoFalse, 
               Microsoft.Office.Core.MsoTriState.msoCTrue,
               float.Parse(r1.Left.ToString()), float.Parse(r1.Top.ToString()), 
               float.Parse(r1.Width.ToString()), float.Parse(r1.Height.ToString())); 

Perfecto, ahora si funciona bien en 2003, y 2007. Pero lo que cuesta hacer la cosas bien :).

Please follow and like us:

Como poner un label transparente sobre un picturebox en .NET

Programando, y programando me he encontrado con muchas cosas curiosas, entre ellas esta.

Imagina que quieres poner un texto encima de una imagen, lo normal sería, poner un picturebox, y luego poner encima un label pero si pones un label sobre un picturebox o cualquier otra imagen o control, pasan cosas como esta:

Fallo marcado en rojo

Fallo marcado en rojo

Como veis en la captura de arriba (programa que me he hecho en 5 minutos), esto es un problema como la copa de un pino, ya que se ve gris. Bueno no realmente, se ve del color del Form en el cual están alojados el picturebox y el label. Y este el problema, el “parent” del label es el Form, por lo que al hacer la transparencia, que la esta haciendo, la hace con su padre y no con la imagen que tiene encima, de ahí el color gris.

¿Como lograr entonces la transparencia?, pues hay dos métodos, uno sencillo que es cambiar el “parent” del label y poner el picturebox, por lo que la transparencia la tratará de hacer con el picturebox, y otra, escribir el texto en el evento “Paint” del control picturebox que es más complejo pero infinitamente mas potente.

Os adjunto el código para que nos entendamos, con comentarios:

private void Form1_Load(object sender, EventArgs e)
        {
            // Metodo 1º
            // ---------
            // Sencillo y barato... al menos es el que uso para cosas rápidas

            // Inicialización chorra
            this.label1.Text = "Esta no funciona...";
            this.label2.Text = "Esta si funciona...";

            // No funciona
            // ¿PERO QUE COÑO?
            this.label1.BackColor = Color.Transparent;

            // Funciona
            // Me cago en tó... ¿pero porqué?
            this.label2.Parent = this.pictureBox1;
            this.label2.BackColor = Color.Transparent;

        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            // Metodo 2º
            // ---------
            // Bastante mas complicado pero con muchas mas posibilidades
            Font fuente = new Font("Verdana", 8);
            SolidBrush brocha = new SolidBrush(Color.Black);

            e.Graphics.DrawString("Este si funciona, pero es mas complejo", fuente, brocha, 30, 110);

        }

Si queréis echar un ojo al programa os lo adjunto también: transparenciasLabel.

Please follow and like us:

Excel y .NET, malos amigos (II): Proteger un Excel y su contenido

Por desgracia, mis problemas con el maldito Excel no han acabado. Pero ni mucho menos.

Nuevo problema, nueva comedura de cabeza. Necesito proteger el contenido de un Excel, mediante programación (en C#) para que un usuario no pueda ni modificar sus valores ni el formato de las celdas.

Existen varias protecciones que se pueden aplicar: a Celda, a Hoja (Worksheet en MSDN) y a Libro (Workbook en MSDN).

La protección del libro, nos permite proteger la estructura general del libro, el orden de las hojas, añadir mas, o eliminar. El protección de la hoja, es la parte mas interesante, nos permite un montón de cosas, desde prohibir modificar contenido, hasta ordenar las filas/columnas.

Un código de ejemplo es este, que restringe por completo las acciones del usuario:

// Protejo una hoja en particular:
objSheet = (Microsoft.Office.Interop.Excel.Worksheet)objWorkbook.Sheets["Hoja 1"];
objSheet.Activate();
objSheet.Protect("clave", true, true, true, false, false, false, false, false, false, false, false, false, false, false, false);
// Protejo el libro completo:
m_Excel.Application.ActiveWorkbook.Protect("clave 2", true, false);

Si queréis, saber mas, solo visitar las urls que os he dejamos mas arriba para dar los permisos que queráis, y no los mas restrictivos.

Please follow and like us:

Excel y .NET, malos amigos: Cambiar el rango de datos a un gráfico

Como casi todos los hemos programado algo para .NET, hemos visto la potencia que tiene. Pero cuando ponemos de nuevo los pies en la tierra es cuando tenemos que volver a usar los objetos COM de Windows.

Arrrrggg… Malditos. ¡Luego me preguntarán que porqué odio a muerte el Office!.

Imaginaros el caso: Un excel “plantilla”, donde solo hay que rellenar ciertos datos, pero con unas columnas de datos de longitud dinámica, puede haber desde 10 a 300. Como no, la cosa se complica, también quieren un gráfico que sea variable (es decir, si solo hay 5 datos, que muestre 5, y si hay mas pues mas…). Todo esto se arregla fácilmente con una macro o con una complicada función de esas que trae excel.

Pero ya que el excel lo rellena un programa en .NET (C# para ser mas exactos) lo vamos ha hacel elegantemente:

// Elegir el Gráfico a modificar, es curioso pero si tipo lo declarais con "Chart" en vez de "_Chart" también funciona
Microsoft.Office.Interop.Excel._Chart chart = (Microsoft.Office.Interop.Excel._Chart)objWorkbook.Charts["Mi gráfico"];

// Ahora a cambiar las series que tienen que estar definidas, se podria utilizar el nombre tambien
// FILA es un double donde esta la fila donde acaban los datos
serie = (Microsoft.Office.Interop.Excel.Series) chart.SeriesCollection(1);
serie.XValues = objSheet.get_Range("A4", "A" + Math.Floor(Fila));
serie.Values = objSheet.get_Range("C4", "C" + Math.Floor(Fila));
// Otra serie que tiene el gráfico
serie = (Microsoft.Office.Interop.Excel.Series) chart.SeriesCollection(2);
serie.Values = objSheet.get_Range("D4", "D" + Math.Floor(Fila));

Tachán… tus gráficos con datos dinámicos en un par de líneas. Eso si, tras mucho pensar 🙂

Please follow and like us:

.NET: Como hacer una única función de “Close” para multiples paneles

Os pongo en situación: una aplicación escrita en Visual C# 2005 con múltiples paneles a modo de ventanas flotantes en el form principal. Por necesidades del cliente, ahora se necesita que todos estos paneles tengan un botón de “salida”, o “close”.

Imaginaros, tengo 10 paneles, ¿tengo que hacer 10 funciones?. No hombre… es muy sencillo:

  • Poner los botones en cada panel. Haz doble click sobre uno, y escribe el código siguiente:
    private void btnCerrar_Click(object sender, EventArgs e)
    {
    System.Windows.Forms.Button btn = (Button)sender;
    btn.Parent.Visible = false;
    }
  • Ahora te vas a cada boton y le asignas esta función en cada evento click. Ya está.

Como veras, se puede usar para cerrar panels (en este caso hacerlos invisibles), mover cosas, cerrar un form, y todo lo que se te ocurra que sea muy repetivivo. Espero que os sirva de ayuda.

Please follow and like us: