Vender humo

Published 203 weeks, 3 days ago
Tue Apr 18 2006
Atentos a la oferta de trabajo. Por si acaso retiran o modifican la oferta, copypasteo (las negritas son mías):
Cargo vacante: Comercial
Categoría: Comercial - Ventas
Departamento: DIRECCIÓN GENERAL
Número de Puestos Vacantes: 1
Descripción de la Oferta: 6 MILL ***


RBLE. DPTO. COMERCIAL

Se resposabilizará de todas las acciones comerciales de la empresa.

Será el responsable comercial, y sus funciones principales serán vender mediante realidad virtual.

Debe ser un profesional de vender ilusiones, ya que el producto se está construyendo.

*** JUNTO ESTACIÓN *** PARADAS CADA 15 MIN

DIETA PAGADA


No pasa nada si el luego el producto (en este caso, pisos o chalés) no responde a las expectativas, no pasa nada si luego el producto es una bazofia, incluso no pasa nada si al final el producto no existe. El responsable comercial ya ha cumplido, ya ha vendido ilusiones mediante realidad virtual (más claro, el agua) y ya se ha llevado calentitos los seis millones de las antiguas pesetas (espero).

Esta oferta en concreto es para una inmobiliaria, pero se puede aplicar a nuestro campo: cuántos ejemplos conocemos de comerciales que venden humo en cantidades industriales y prometen a los clientes cualquier cosa con tal de que el cliente firme y ellos hagan su venta y se lleven su comisión. Y cuando digo cualquier cosa quiero decir literalmente cualquier cosa: el pliego original de condiciones de mi proyecto actual especifica que la aplicación Web que estamos construyendo debe ser capaz de conectarse a dispositivos externos para el tratamiento de la información. Sin especificar si esos dispositivos externos se refieren a otros servidores, servicios Web, la PSP del hijo del Director General o la lavadora de su señora. Escenario virtual que puede parecer hilarante, pero que con los pliegos en la mano puede dejar de serlo rápidamente, si el cliente se pone cabezón. Y, por supuesto, el comercial original que firmó estos pliegos ya no está para al menos poder ponerle a parir: él ya vendió en su día las ilusiones mediante realidad virtual, el cliente picó y compró y ahora quiere que esa realidad deje de ser virtual, tarea que no corresponde al vendedor de ilusiones sino a los constructores de software. Que, curiosamente, en la mayoría de los casos no suelen llevarse seis millones, ni cinco, ni siquiera cuatro para arreglar los desaguisados que suelen provocar los vendedores de ilusiones.

Web 2.0 y eso...

Published 204 weeks, 3 days ago
Mon Apr 10 2006
Via Mike Gunderloy (otra vez... si no estáis suscritos ya, no sé a qué estáis esperando) me encuentro con dos librerías estupendas para ASP .NET.

Una, el BusyBoxDotNet, es un control de servidor que muestra un mensaje de espera en la página cliente cuando el servidor esté realizando alguna operación que consuma mucho tiempo. El mensaje aparece en una capa que se mueve automáticamente al hacer scroll, permite difuminar y bloquear el fondo (es decir, el resto) de la página mientras se está realizando la tarea y desaparece sin problemas al terminar. Podéis probar una demo aquí. Muy, muy útil.

Y por otro lado, siguiendo con la moda del Web 2.01, tenemos MagicAjax.NET. Permite introducir técnicas AJAX en controles de servidor ASP .NET normales y corrientes, por lo que se suponen óptimos para introducir AJAX en una aplicación ya hecha o en proceso. También tienen una demo disponible.


1 Que no, no consiste únicamente en poner bordes redondeados a los cuadrados. O en poner un feed RSS en tus páginas, ni en utilizar palabros como sinergia, comunidad, colaborativo o tags. Ni siquiera consiste en poner un Beta al lado del nombre de tu aplicación Web. Además de todo eso, hay que hacer que sea más intuitiva, más ágil en la respuestas que proporciona al usuario. Menos viajes al servidor para cargar información, al menos aparentemente. Vamos, que se parezca lo más posible a una aplicación de escritorio, incluso llegando a imitarlas por completo. Lo que hay que ver.

Bug en Peeker

Published 205 weeks, 3 days ago
Mon Apr 03 2006
Acabo de darme cuenta de la existencia de un bug en Peeker, mi aplicación para la pre-visualización de imágenes desde el Explorador de Windows.

Para comprobarlo, haced click con el botón derecho del ratón en cualquier imagen de vuestro disco duro, y pinchad la previsualización de la imagen en el menú contextual de Peeker. Se os abrirá, como de costumbre, una nueva ventana con la imagen cargada a tamaño completo. Cerrad esa ventana. Ahora, intentad borrar el archivo de imagen desde el Explorador de Windows. Exacto, no funciona. Dice que no se puede borrar el archivo porque está siendo usado por otro proceso.

Sencillamente lo que ocurre es que al cerrar el formulario de visualización de la imagen a tamaño completo no libero específicamente la imagen cargada, por lo tanto Windows no encuentra el fichero libre cuando intentamos eliminarlo. No es un bug importante, de hecho ya está corregido, pero esto me ha dado que pensar.

Quiero añadir más funcionalidades a Peeker, amén de corregir este y cualquier otro error que pudiera producirse; y no quiero estar cada dos por tres añadiendo una entrada avisando de que hay una nueva versión disponible. Y tengo mucha curiosidad por implementar un sistema de actualización automática de una aplicación Windows, así que espero que muy pronto podáis descargar una nueva y definitiva versión de Peeker que se auto actualizará, para que no tengamos que estar atentos a esta clase de cosas.

Introducir y recuperar imágenes en Microsoft Access

Published 205 weeks, 3 days ago
Mon Apr 03 2006
Si hace unos días hablábamos de cómo guardar ficheros binarios en SQL Server, hoy le toca a Microsoft Access. El proceso es similar, aunque para este ejemplo he decidido usar imágenes como los datos binarios que vamos a guardar y recuperar de la base de datos.

Imaginemos que tenemos una sencilla base de datos Access, con una única tabla llamada Images, que tiene la siguiente estructura:

Nombre Tipo
imgID int (Clave autonumérica)
FileName Texto(255)
ImageData Objeto OLE


El campo imgID almacenará una clave única autonumérica para identificar a cada registro. FileName contendrá el nombre del fichero de imagen, e ImageData contendrá la imagen propiamente dicha, en formato binario.

El procedimiento para insertar imágenes en la base de datos Access es como sigue:

private void SaveData(string fileName)
{
    
try
    
{
        
//Creamos un nuevo FileStream a partir del fichero parámetro
        //
        //We create a new FileStream from the file specified as parameter
        
FileStream fs = new FileStream(fileName, FileMode.Open);
        
//Declaramos un array de bytes del tamaño del FileStream
        //
        //We declare a new byte array as big as the FileStream
        
Byte[] data = new byte[fs.Length];
        
//E introducimos los datos del FileStream en el array
        //
        //And we enter the data from the FileStream into the array
        
fs.Read(data, 0, Convert.ToInt32(fs.Length));
        
fs.Close();

        
//Creamos un nuevo DataAdapter, DataSet, CommandBuilder and DataRow
        //para insertar el nuevo registro
        //
        //We create a new DataAdapter, DataSet, CommandBuilder and DataRow 
        //to perform the insertion of the new record
        
if(conn.State !ConnectionState.Open ) conn.Open();
        
OleDbDataAdapter adap 
             new 
OleDbDataAdapter("SELECT * FROM Images", conn);
        
adap.MissingSchemaAction MissingSchemaAction.AddWithKey;
        
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(adap);
        
adap.InsertCommand cmdBuilder.GetInsertCommand();
        
DataSet ds = new DataSet();
        
adap.Fill(ds);
        
DataRow dr ds.Tables[0].NewRow();
        
dr["FileName"fileName
        
dr["ImageData"data//The byte array
        
ds.Tables[0].Rows.Add(dr);
        
adap.Update(ds);
        
conn.Close();

        
//Esta funcion refresca un ListBox para que se refleje 
        //el nuevo registro insertado
        //
        //This function refreshes a ListBox to show the newly inserted record
        
LoadData();
        
MessageBox.Show("Done!\nImage file: " + fileName + 
            " added to the database.");
    
}
    
catch (Exception e)
    {
        MessageBox.Show(e.Message)
;
    
}
}

Colorized by: CarlosAg.CodeColorizer

Y los procedimientos que extrae las imágenes de la base de datos es tal que así:

/// <summary>
/// Coloca en el control Image la imagen recuperada de base de
/// datos por la función LoadImage, en base al índice único de 
/// la imagen en la base de datos.
/// 
/// Sets in the Image control an image recovered from the database
/// via the LoadImage function, thanks to the unique identifier 
/// of the image on the database.
/// </summary>
/// <param name="imgIndex">El identificador único de la imagen 
///en la base de datos</param>
/// <param name="imgIndex">The unique identifier of the image 
///on the database</param>
private void LoadImageControl(string imgIndex)
{
    
try
    
{
        
//Creamos un nuevo stream de memoria con el resultado 
        //de la función LoadImage, que devuelve un array de bytes
        //
        //We create a new MemoryStream with a byte array,
        //returned by the LoadImage function
        
MemoryStream mem = new MemoryStream(LoadImage(imgIndex));
        
//Y establecemos que la imagen del control picImage es una nueva imagen, 
        //a partir del stream en memoria mem
        //
        //And we set the picImage control's image is a new image, 
        //built from the memory stream mem
        
picImage.Image Image.FromStream(mem);
    
}
    
catch (Exception e)
    {
        MessageBox.Show(e.Message)
;
    
}
}

/// <summary>
/// Carga la imagen de la base de datos en un array 
//de bytes y devuelve dicho array
/// 
/// Loads the image from the database to a byte array and returns that array
/// </summary>
/// <param name="imgIndex">El ID unico de la imagen</param>
/// <param name="imgIndex">The unique ID of the image</param>
/// <returns>Devuelve la imagen en un array de bytes</returns>
/// <returns>The image on a byte array</returns>
private byte[] LoadImage(string imgIndex)
{
    
if(conn.State !ConnectionState.Open ) conn.Open();
    
sSQL "SELECT ImageData FROM Images WHERE imgID = " + imgIndex;
    
OleDbCommand cmd = new OleDbCommand(sSQL, conn);
    byte
[] imgData (byte[]) cmd.ExecuteScalar();
    
conn.Close();
    return 
imgData;
}

Colorized by: CarlosAg.CodeColorizer

En cualquier caso, tenéis disponible aquí el código fuente completo, con comentarios en inglés y en español para esta aplicación. Descargadlo y jugad un poco con él.


NOTA.- Más adelante publicaré la forma de compactar y reparar mediante código C# una base de datos Microsoft Access. Aplicaciones como el código fuente que tenéis para descargar lo necesitan, y mucho.

DataGrid de sólo lectura en Windows Forms

Published 205 weeks, 4 days ago
Mon Apr 03 2006
Las DataGrid en Windows Forms permiten al usuario, por defecto, editar los valores que muestran e incluso crear registros nuevos.

¿Y si queremos cargar una rejilla que sólo muestre datos, sin posibilidad de editarlos o de añadir nuevos datos?

Para ello, una de las cosas que podemos hacer es modificar las propiedades del DefaultView de la tabla con la que vamos a alimentar de datos a la rejilla. Por ejemplo, pongamos que tenemos un DataTable dtDatos que va a ser el DataSource de la rejilla. Si modificamos las propiedades AllowDelete, AllowEdit y AllowNew de la vista por defecto de la tabla origen de datos, tal que así:

dtDatos.DefaultView.AllowDelete = false;
dtDatos.DefaultView.AllowEdit = false;
dtDatos.DefaultView.AllowNew = false;
grdDatos.DataSource = this.dtDatos;

Colorized by: CarlosAg.CodeColorizer

la rejilla grdDatos no permitirá al usuario que modifique, borre o añada nuevos registros.

Por otro lado, si lo que queremos es que se seleccione la fila entera de la rejilla al clickar en cualquiera de sus celdas, implementamos el siguiente código en el evento CurrentCellChanged de la rejilla, que se dispara al cambiar de celda seleccionada.

private void grdDatos_CurrentCellChanged(object sender, EventArgs e)
{
    grdDatos.Select(grdDatos.CurrentRowIndex)
;
}

Colorized by: CarlosAg.CodeColorizer

Sencillamente, lo que hacemos es decirle a la rejilla que seleccione toda la fila, sabiendo qué fila es la que tiene que seleccionar gracias a la propiedad CurrentRowIndex.