Selling smoke

Wed 19 April, 2006

Take a look at this job offer (in spanish, sorry). Just in case you don’t speak it :) , I’ll translate (and yes, I’ve chosen which words to make bold):

Vacant job: Salesman
Category: Salesman - Sales
Department: GENERAL MANAGEMENT
Number of vacancies: 1
Offer Description: 6 MILL 1

HEAD OF SALES DEPARTMENT

Will be responsible of any and all commercial actions of the firm.

Will be the head salesman, and his/her main tasks will be selling with virtual reality.

Must be a professional illusion seller, because the product is on construction.

It doesn’t matter if later on the product (on this particular case, houses) does not respond to the initial expectatives, it doesn’t matter if later on the product is a complete piece of crap, even more it doesn’t matter if at the end the product doesn’t even exist. The head salesman has already done his job, he has already illusions with virtual reality (you can talk louder, but you can’t talk clearer) and has already taken home his hard earned money.

This here offer is for construction business, but it can be sadly and perfectly translated to our field: how many examples do we know about salesman execs selling first rate smoke and promising prospective customers virtually anything just to get the customer to sign a contract, the make the sale and get their bonus. And we I mean anything I mean it: the original contract for the disaster project I’m currently working on specifies that the Web application we’re building has to be able to connect to external devices to information treatment purposes. It doesn’t specify if said devices are external servers, Web services, the CEO’s son PSP or his wife microwave oven. Virtual scenario that can seem hilarious to the untrained eye, but if the customer watns to give us a difficult time they can do, and there’s nothing we can do legally. There’s a contract that one of our sales people happily approved and our CEO signed without revising. Not the problem of the salesman, of course: he has already make the sale, he has already make his job; and now the customer wants that his illusions make the step from virtual to real, a trivial task not corresponding to the illusion seller but to the software developers. People that, funny enough, on most cases don’t make six million1, neither they make five, nor even four on some cases for the ungrateful and unsung job of making right the awful wrongs usually made by the illusion sellers.


1.- Six million pesetas/year, gross. Roughly equivalent to €36K/year, gross. Though Spain is not using our old currency, the peseta, since 2002 at least there are still some job offers speaking the old language. By the way, that kind of figures more or less matches the salary of a well paid project leader.

Web 2.0 and all that…

Mon 10 April, 2006

Via Mike Gunderloy (again… if you’re not suscribed already, don’t know what you’re waiting for) I’ve found two amazing libraries for ASP .NET.

First, BusyBoxDotNet, a server control which displays a wait message on the page stating that the server is performing a time-consuming operation and unabling the user to do nothing else but wait. The message is shown on a layer which moves automatically when scrolling, and allows to gray out and lock the back of the page (that is, all the rest of it) while the message is displayed and the task is (hopefully) being performed, to dissapear without a fuss at the end. You can try a demo right here. Really useful. Kudos.

And second, hot in the Web 2.01 hype, we find MagicAjax.NET. It allows us to enter AJAX techniques on standard out-of-.the.box ASP .NET server controls, which I find particularly useful for applying AJAX on an already developed or well underway ASP .NET application. It also has a demo avaliable.


1 And no, it’s not only to make rounded borders to visible layers. Neither putting an RSS feed on your pages, neither using words such as sinergy, community, collaborative or even tags. Even if you place a Beta label neatly by the side of your application’s title, you fall short. Besides all that, you have to make your web application more intuitive, more responsive, more agile. Less goings to the server for information, at least in appeareance. Yes, it has to be as similar as possible to an old desktop application, even imitating them completely. Live and see.

Peeker bug

Mon 3 April, 2006

I’ve just realised there’s a bug in Peeker, my image preview application for Windows Explorer.

To check out the bug, simply make a right button click on any image of your hard drive, and then click on the image preview in the context menu of Peeker. You will open, as usual, a new window with the fully scaled image. Close that window. Now, try to delete the file from Windows Explorer. That’s right, it doesn’t work. Windows says that the file can’t be deleted because it’s being used by another process.

What’s happening is quite simple: while closing the full size image view form I don’t specifically free the loaded image, so Windows finds the file is not free when trying to delete it. It’s not a show-stopper, in fact is already corrected in my local version, but it’s got me thinking.

I want to add more functionality to Peeker, besides correcting this and any other bug that could appear; but I don’t want to add a new post every other day stating that a new Peeker version is avaliable. And I’m quite willing to develop a WinForms application with an auto-update feature, so I hope that really soon you’ll be able to download a new and final version of Peeker which will auto update, so we don’t have to pay attention to this kind of things.

Writing and reading images on a Microsoft Access database

If just a few days ago we talked about how to store binary files on SQL Server, today’s Microsoft Access turn. The process is similar, though for this example I’ve chosen to use image files as the binary data we are going to store and retrieve from the database.

Let’s say we have a simple Access database, with a single table called Images, with the following layout:

Field Name Type
imgID int (Autonumeric Key)
FileName Text(255)
ImageData OLE Object

imgID will store a unique autonumeric key for identifying each record. FileName will have the name of the image file, and ImageData will store the image itself, as binary data.

The procedure to insert images on the Access database is as follows:

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

And the procedures to extract images from the database are like these:

/// <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

In any case, you can download a complete code example here, source code commented in English and Spanish. Please feel free to download it and play a little with it.


NOTE.- On a future entry I’ll tell you how to compact and repair a Microsoft Access database with C# code. Applications like the source code you can download from this post need that compact and repair capabilities, and they need it badly.

ReadOnly WinForms DataGrid

Windows Forms’ DataGrid control allows users, by default, to modify the data shown and even to create new records.

What if we want to make a grid that only shows data, without the power to edit o make new data?

For that, one of the things we can make is modifying the default properties of the DefaultView from the table we’re going to feed the grid with. For instance, let’s say we have a DataTable dtData, acting as the grid DataSource. If we modify the AllowDelete, AllowEdit and AllowNew properties from the default view of the table, like this:

dtData.DefaultView.AllowDelete = false;
dtData.DefaultView.AllowEdit = false;
dtData.DefaultView.AllowNew = false;
grdData.DataSource = this.dtDatos;

Colorized by: CarlosAg.CodeColorizer

the grdData data grid won’t allow users to modify, delete or add new records.

In the other hand, if we want to select the whole row of the grid with just clicking on any of its cells, we have to write the following code on the CurrentCellChanged datagrid event.

private void grdData_CurrentCellChanged(object sender, EventArgs e)
{
    grdData.Select(grdData.CurrentRowIndex)
;
}

Colorized by: CarlosAg.CodeColorizer

Quite simply, this code tells the datagrid to select the entire row, knowing which row is the one thanks to the CurrentRowIndex property.