Skip to main content

Blog de Vicente Pazo

Go Search
Home
  

Blog de Vicente Pazo > Categories
Haciendo webs para Iphone: mostrar el teclado numérico

Estábamos desarrollando una aplicación web para un Iphone. Esta web es una tienda y, como tal, tiene un formulario de entrada de datos a la hora de hacer el pago.

Realmente estábamos haciendo una aplicación web pero adaptada con ciertos estilos para que tenga un "Iphone feeling", así que los controles del formulario eran controles de .NET de toda la vida (además, lo de "de toda la vida" era literal, ya que el framework en que se ejecuta es el 1.1).

Sin embargo, el cliente quería que el textbox de introducción del número de la tarjeta de crédito fuese numérico, es decir, que al seleccionarlo apareciese el teclado sólo con números en el iphone (por defecto aparece el teclado alfanumérico).

 

Empezamos por buscar cómo podríamos hacer esto en un HTML control, y googleando encontramos que podemos indicar el atributo type para que muestre un tipo de teclado u otro:

 

<input type='tel'/>

 


<input type='number'/>

 


<input type='email'/>

 


<input />

 

 

 

 

 

Esto es correcto, pero… ¿Cómo lo aplicamos a un control de .NET? Evidentemente, no podemos ponerle un atributo "type". Y evidentemente, cuando añadimos un textbox .NET nos hará el renderizado en HTML como un control "type=text".

La solución es extender la clase TextBox y sobreescribir el método de renderizado.

Sin embargo, aún queda un paso más, ya que no podemos borrar los atributos que no son nuestros directamente de la colección "Attributes".

Si hiciésemos esto:

this.Attributes.Remove("type");

 

Para luego añadir el atributo type:

this.Attributes.Add("type", "number");

 

No obtendríamos el resultado esperado, ya que no podemos borrar ese parámetro.

La solución que se nos ocurrió fue hacer un replace dentro del objeto writeruna vez el método render del padre ha sido llamado:

protected override void Render(System.Web.UI.HtmlTextWriter writer)

{

    StringWriter stringWriter = new StringWriter();

    HtmlTextWriter source = new HtmlTextWriter(stringWriter);

    base.Render(source);

    writer.Write(stringWriter.ToString().Replace("type=\"text\"", "type=\"" + _contentType + "\""));

}

Este es el ejemplo completo:

 

public class NumberTextBox:TextBox

{

    private string _contentType;

 

    public string ContentType

    {

        get

        {

            return _contentType;

        }

        set

        {

            _contentType = value;

        }

    }

 

    public NumberTextBox()

    {

            

    }

 

    protected override void Render(System.Web.UI.HtmlTextWriter writer)

    {

        StringWriter stringWriter = new StringWriter();

        HtmlTextWriter source = new HtmlTextWriter(stringWriter);

        base.Render(source);

        writer.Write(stringWriter.ToString().Replace("type=\"text\"", "type=\"" + _contentType + "\""));

    }

Y esto cómo se usaría el control:

<cc:NumberTextBox cssclass="ipTextfieldAllWidth" runat="server" id="Zip" ContentType="number" />

 

App_offline.htm II: Añadiendo imágenes

Vamos a añadir un poco layout a la página offline que citábamos en el anterior post:

Este será el resultado (evidentemente, el path de la imagen es correcto :P )

 

Por lo que parece, el IIS no es capaz de resolver el tag img dentro de esta página. Sin embargo, si usamos un atributo src con un stream Base64 podemos solucionar el problema.

Por ejemplo, vamos a crear un método en .NET para obtener el string correspondiente al stream Base64 de la imagen que queramos:

/// <summary>

/// Images to base64.

/// </summary>

/// <param name="image">The image.</param>

/// <param name="format">The format.</param>

/// <returns></returns>

private static string ImageToBase64(string sPath, ImageFormat format)

{

using (var ms = new MemoryStream())

{

try

{

 

System.Drawing.Image image = System.Drawing.Image.FromFile(sPath);

 

// Convert Image to byte[]

image.Save(ms, format);

var imageBytes = ms.ToArray();

 

// Convert byte[] to Base64 String

var base64String = Convert.ToBase64String(imageBytes);

 

return base64String;

}

catch

{

return null;

}

}

}

 

El string que nos devuelve este método podemos ponerlo dentro del atributo src del tag img (tras poner data:image/jpg;base64,):

 

Y este sería el resultado:

 

Poner una aplicación .NET offline usando app_offline.htm

A veces, necesitamos que nuestra aplicación permanezca durante un tiempo mostrando un mensaje para evitar que los usuarios accedan a la aplicación. Para ello, existe un método rápido y sencillo de poner una aplicación .NET en modo offline.

Creando una página llamada app_offline.htm e incluyéndola en el directorio raíz de nuestra aplicación conseguiremos que el IIS redireccione a ella automáticamente, sin tener que configurar nada:

 

 

De todas formas, tenemos que tener en cuenta ciertas consideraciones. Por extraño que parezca, si el tamaño de este archivo es inferior a 512 bytes, nuestro querido IE mostrará un error:

 

 

Para que funcione, podemos insertar cualquier texto dentro de <!-- --> para aumentar el tamaño del archivo y entonces sí funcionará:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>

<title>Página no disponible en este momento</title>
</head>
<body>

<h1>la página está offline</h1>
<!--
Vamos a añadir unos comentarios para hacer que la página sea mayor que 512 bytes
Vamos a añadir unos comentarios para hacer que la página sea mayor que 512 bytes
Vamos a añadir unos comentarios para hacer que la página sea mayor que 512 bytes
Vamos a añadir unos comentarios para hacer que la página sea mayor que 512 bytes
Vamos a añadir unos comentarios para hacer que la página sea mayor que 512 bytes
-->
</body>

</html>

 

Próximamente veremos cómo añadir imágenes, ya que no es posible usar directamente la etiqueta img

 

Disposing attachments in System.Net.Mail

If you are sending a mil with one or some files attached and you try to access the file after sending the mail, you'll receive an error because the file is "being used by another process".

Why?

When you attached a file to a mail message you need to make a dispose of it (after sending it). You can dispose your attachment by using Attachment.Dispose() or (better) using directly:

MailMessage.Attachments.Dispose();

Abrir un documento Office en .NET sin tener instalado Office

Muchas veces ejecutamos nuestras aplicaciones en servidores que no sólo tienen las aplicaciones justas para obtener sus servicios, y en alguna ocasión no tenemos ni siquiera acceso a ellos. Éste era el caso al que nos hemos enfrentado estos días, en el que necesitábamos abrir un archivo Excel desde una aplicación .NET, pero el servidor no tenía instalado Office y, por tanto, no tenía las dlls necesarias para abrir los archivos.

Este era el error que recibíamos:

The 'microsoft.jet.oledb.4.0' provider is not registered on the local machine.

Evidentemente, con registrar la dll con regsvr32 hubiese bastado, pero no podíamos (debíamos J ) tocar el servidor para esas cosas. Camino prohibido…

Investigando encontramos esta forma de abrir un Excel y generar un Dataset, usando la librería "Microsoft.Office.Interop.Excel":

private DataSet OpenExcelFile(string sPath)

{

try

{

 

//use this way to open the excel file without office:

Microsoft.Office.Interop.Excel.Application ExcelObj = new Microsoft.Office.Interop.Excel.Application();

 

DataSet ds = new DataSet();

System.Data.DataTable dt = new System.Data.DataTable();

dt.Columns.Add();

dt.Columns.Add();

dt.Columns.Add();

 

if (ExcelObj == null)

{

Log("Excel object not found", MessageType.Error);

}

 

Workbook theWorkbook = ExcelObj.Workbooks.Open(sPath, 0, false, 5, "", "", false, XlPlatform.xlWindows, "", true, false, 0, true, false, false);

 

Sheets sheets = theWorkbook.Worksheets;

Worksheet worksheet = (Worksheet)sheets.get_Item(1);

 

int index = 0;

object rowIndex = 2;

 

DataRow row;

 

while (((Microsoft.Office.Interop.Excel.Range)worksheet.Cells[rowIndex, 1]).Value2 != null)

{

rowIndex = 2 + index;

row = dt.NewRow();

object[] itemArr = { Convert.ToString(((Microsoft.Office.Interop.Excel.Range)worksheet.Cells[rowIndex, 1]).Value2),

Convert.ToString(((Microsoft.Office.Interop.Excel.Range)worksheet.Cells[rowIndex, 2]).Value2),

Convert.ToString(((Microsoft.Office.Interop.Excel.Range)worksheet.Cells[rowIndex, 3]).Value2) };

 

row.ItemArray = itemArr;

 

index++;

dt.Rows.Add(row);

}

 

ds.Tables.Add(dt);

 

Log("Dataset created properly", MessageType.Success);

 

return ds;

 

}

catch (Exception exc)

{

throw new Exception(exc.Message);

}

}

La única "pega" (leve) es que con el método convencional (conexión con el provider Microsoft.Jet.OLEDB.4.0) el dataset se creaba automáticamente y los datos podrían estar en cualquier columna del sheet, que él solito cogía los datos correctos. Aquí, como veis, los datos tienen que estar en la fila 1 columna 1. Se podría añadir código para que lo cogiese automáticamente, pero en mi caso no era necesario.

Algunas cosas sobre el control RichTextBox de .NET

Como soy mayoritariamente un programador de web, no me había pegado demasiado con los controles Windows forms. Es posible que algunas de las cosas que diga referentes a Windows forms ya las conozcáis, pero a mí me ha costado un poco averiguarlas.

Os sitúo, tenemos una aplicación que hay que añadirle un control de texto que vaya mostrando mensajes de la ejecución del programa. Necesitábamos conseguir dos cosas: el control tenía que mostrar colores según fuese el tipo de mensaje a mostrar y tenía que mostrar el último mensaje insertado (por defecto se queda arriba de todo).

 

Para el color, hay que seguir este código:

txtMessages.SelectionColor = Color.Red;
txtMessages.AppendText(sMessage);

Y para hacer que el control muestre lo último insertado basta con poner esta función:

txtMessages.ScrollToCaret();

 

Muy sencillo, sobretodo tras unos cuatro minutos de googling!