Cómo procesar correctamente en .NET fechas de un AS/400

Post original en JASoft.org: http://www.jasoft.org/Blog/post/Como-procesar-correctamente-en-NET-fechas-de-un-AS400.aspx

as400Esto es bastante básico, pero a los usuarios más noveles puede que le resulte complicado y precisamente llegué a ello a raíz de la pregunta de un alumno hace poco…

El caso es que los sistemas AS/400 suelen devolver los campos de fecha en un formato similar a este: 20110422, que es tal y como esta almacenado en la base de datos tipo texto. Es decir, el formato es YYYMMDD.

¿Cómo las conviertes en fechas de pleno derecho en .NET?

Si es un campo de texto y siempre tiene este mismo formato exactamente (es decir, cuatro cifras para el año, 2 para el mes y 2 para el día) es muy fácil convertirlo en una fecha usando el método ParseExact de la clase DateTime:

DateTime.ParseExact("20110422", "yyyyMMdd", CultureInfo.InvariantCulture)

Simplemente le indicas la fecha, el formato como segundo parámetro (en este caso cuatro cifras para el año, dos para el mes (ojo, el mes es una M mayúscula, la minúscula son minutos) y dos para el día del mes) y listo.

También podrías hacerlo analizando la cadena separándola en grupos de dígitos, pero no merece la pena complicarse la vida existiendo ParseExact en el propio framework.

Lo dicho: truco rápido y sencillo que seguro que a más de uno le resulta útil 🙂

Cómo poner un tono de teléfono propio en Windows Phone 7.5 "Mango"

Post original en JASoft.org: http://www.jasoft.org/Blog/post/Como-poner-un-tono-de-telefono-propio-en-Windows-Phone-75-Mango.aspx

La nueva versión de Windows Phone 7, la 7.5 que es conocida como «Mango», tiene por fin una capacidad muy trivial pero que hasta ahora no era posible: la posibilidad de usar un archivo de sonido propio como todo del timbre del teléfono. En la versión original de WP esto no era posible.

Sin embargo en Mango tampoco existe una forma muy intuitiva de conseguirlo, aunque sí es muy fácil.

En el MarketPlace existen diversas aplicaciones que contienen tonos de audio que podemos elegir para incorporar al teléfono, ya que ahora conseguir eso mediante programación es realmente fácil. Pero ¿qué pasa si queremos usar nuestro propio sonido e incorporarlo sin complicarnos la vida?.

Es muy sencillo conseguirlo usando Zune, aunque hay que saber el «truco».

Condiciones

Los sonidos de tono de llamada para el teléfono deben cumplir la siguientes condiciones:

  • Formatos admitidos: MP3 y WMA, sin protección (DRM).
  • Menores de 1 MB de tamaño
  • Máximo 39 segundos de duración
  • Sólo funcionará en Windows Phone 7.5 «Mango» o posterior

Puedes conseguir multitud de sonidos que cumplen con estas condiciones buscando un poco en Internet o puedes convertir y recortar tus propios archivos mediante multitud de programas gratuitos y Open Source  como por ejemplo Audacity.

Zune

Una vez que tienes el archivo de sonido debes abrir Zune y conectar a tu terminal con Windows Phone 7.5. Asegúrate de tener el sonido añadido a la biblioteca de música de Zune (basta con arrastrarlo encima).

Ahora pulsa en el archivo con el botón derecho y elige la opción de editarlo:

WP7_5_Musica_Tono_1

 

En la capa que se abre simplemente tienes que escribir en la casilla «Género» la palabra «RINGTONE»:

WP7_5_Musica_Tono_2

Con esto asocias ese archivo de sonido en particular con los tonos de llamada y te aparecerá automáticamente disponible en la configuración de tonos:

WP7_5_Musica_Tono_3

Pulsa para aumentar

¡Espero que te resulte útil!

Cómo detectar un cambio de sesión o que el usuario bloquea la sesión en Windows

Post original en JASoft.org: http://www.jasoft.org/Blog/post/Como-detectar-un-cambio-de-sesion-o-que-el-usuario-bloquea-la-sesion-en-Windows.aspx

LoginEn ocasiones resulta muy útil detectar desde tu aplicación Windows el hecho de que el usuario actual se esté conectando o desconectando a la sesión actual, o simplemente que se haya bloqueado el escritorio. Por ejemplo, si el usuario tiene el escritorio bloqueado es una tontería mostrarle mensajes o globos de notificación ya que no va a haber nadie para verlos ¿no?

¿Cómo podemos detectar estas circunstancias?

La API de .NET dispone de un espacio de nombres especial llamado Microsoft.Win32 que contiene clases que encapsulan funcionalidad atada al sistema operativo Windows. Dentro de este espacio de nombres existe una clase llamada SystemEvents que ofrece multitud de eventos estáticos que nos permiten responder a diversas cuestiones que ocurren en el sistema operativo: cambio de los ajustes de pantalla, agregar o quitar fuentes, que el equipo se está apagando o suspendiendo, cambios de preferencia de usuario, cambio de la hora del sistema, etc… Es realmente útil, y utiliza por debajo llamadas a la API de Windows, evitándonos tener que conocerlas al detalle y facilitándonos su uso.

Para la pregunta que nos ocupa en esta ocasión la clase define un evento llamado SessionSwitch que es llamado automáticamente cuando cambia el estado de "logueado" del usuario actual. Este evento es un delegado de tipo SessionSwitchEventHandler, por lo que deberemos definir una función apropiada para este tipo de delegado, la cual toma como argumentos un objeto (como origen del mismo, como siempre) y otro de tipo SessionSwitchEventArgs.

Así, que una vez añadido el espacio de nombres Microsoft.Win32 a nuestra clase, podemos definir un método como este:

   1: public static void OnSessionSwitch (Object sender, SessionSwitchEventArgs e)

   2: {

   3:     //Código para responder al evento

   4: }

y asignarlo al evento SessionSwitch de la siguiente manera (por ejemplo en el método Main de nuestra aplicación):

   1: SystemEvents.SessionSwitch += new SessionSwitchEventHandler(OnSessionSwitch);

Con esto, cuando cambie el estado de login del usuario se llamará a nuestro método OnSessionSwitch.

El único miembro interesante del segundo parámetro del evento (llamado "e" en el ejemplo) es Reason, que devuelve un tipo enumerado SessionSwitchReason y nos indica el motivo para haber lanzado el evento. Así e.Reason puede tomar los siguientes valores:

Valor Descripción
ConsoleConnect Se ha conectado una sesión desde la consola.
ConsoleDisconnect Se ha desconectado una sesión desde la consola.
RemoteConnect Se ha conectado una sesión desde una conexión remota.
RemoteDisconnect Se ha desconectado una sesión desde una conexión remota.
SessionLogon Un usuario ha iniciado una sesión.
SessionLogoff Un usuario ha cerrado una sesión.
SessionLock Se ha bloqueado una sesión.
SessionUnlock Se ha desbloqueado una sesión.
SessionRemoteControl Una sesión ha cambiado su estado a o desde el modo controlado remoto.

He marcado en negrita las más comunes.

Por lo tanto es muy sencillo actuar en función de este valor y bloquear los mensajes, por ejemplo, cuando se bloquee la sesión o se desconecte remotamente el usuario (cuando se conecta a través de escritorio remoto),  restaurarlos nuevamente cuando se vuelva a conectar o se desbloquee.

Cómo detectar el estado actual con la API de Windows

Todo lo anterior está muy bien y es muy sencillo, pero ¿qué pasa si queremos determinar el estado actual de "logueo" y no sólo detectar cuando cambie?. Por ejemplo, para determinar si nuestro programa se ha lanzado desde una sesión de Terminal Server o con la sesión local bloqueada (por ejemplo con una tarea programada).

Para determinar si el usuario actual está trabajando en remoto, bajo una sesión de Terminal Server (o escritorio remoto, es exactamente lo mismo), la API de Windows Forms nos ofrece una propiedad de la clase SystemInformation, por lo que es muy sencillo hacer una función IsTerminalServer así:

   1: public static bool IsTerminalServer()

   2: {

   3:     return System.Windows.Forms.SystemInformation.TerminalServerSession;

   4: }

Para averiguar si la sesión actual está bloqueada en este momento o no, tendremos que recurrir a la API de Windows directamente.

Os dejo aquí el código necesario extraído de una de las primeras versiones internas de mi software Open Source Twitter Followers Monitor (es un proyecto en inglés, por lo que los comentarios, etc… están en este idioma):

   1: using System;

   2: using System.Runtime.InteropServices;

   3:  

   4: namespace TwitterFollowersMonitor

   5: {

   6:     /// <summary>

   7:     /// Common utilities class

   8:     /// </summary>

   9:     class Utils

  10:     {

  11:  

  12: #region "API Calls"

  13:         private const Int32 DESKTOP_SWITCHDESKTOP = 0x100;

  14:         [DllImport("user32", EntryPoint = "OpenDesktopA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]

  15:         private static extern Int32 OpenDesktop(string lpszDesktop, Int32 dwFlags, bool fInherit, Int32 dwDesiredAccess);

  16:         [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]

  17:         private static extern Int32 CloseDesktop(Int32 hDesktop);

  18:         [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]

  19:         private static extern Int32 SwitchDesktop(Int32 hDesktop);

  20: #endregion

  21:  

  22:         /// <summary>

  23:         /// Determines if the current session is locked so that I can choose not to show any visual clues (like balloons)

  24:         /// </summary>

  25:         /// <returns></returns>

  26:         public static bool IsWorkstationLocked()

  27:         {

  28:             Int32 p_lngHwnd;

  29:             Int32 p_lngRtn;

  30:             Int32 p_lngErr;

  31:  

  32:             p_lngHwnd = OpenDesktop("Default", 0, false, DESKTOP_SWITCHDESKTOP);

  33:  

  34:             if (p_lngHwnd == 0)

  35:             {

  36:                 return false;

  37:             }

  38:             else

  39:             {

  40:                 p_lngRtn = SwitchDesktop(p_lngHwnd);

  41:                 p_lngErr = Marshal.GetLastWin32Error();

  42:                 if (p_lngRtn == 0)

  43:                 {

  44:                     if (p_lngErr == 0)

  45:                     {

  46:                         p_lngHwnd = CloseDesktop(p_lngHwnd);

  47:                         return true;

  48:                     }

  49:                     else

  50:                     {

  51:                         p_lngHwnd = CloseDesktop(p_lngHwnd);

  52:                         return false;

  53:                     }

  54:                 }

  55:                 else

  56:                 {

  57:                     //Not locked

  58:                     p_lngHwnd = CloseDesktop(p_lngHwnd);

  59:                     return false;

  60:                 }

  61:             }

  62:         }

  63:  

  64:     }

  65: }

Como vemos se necesitan tres funciones de la API de Windows (OpenDesktop, CloseDesktop y SwitchDesktop, declaradas con Interop al principio de la clase. se usan para intentar abrir el escritorio actual y activarlo (sólo hay uno, así que si está disponible y no bloqueado deberían funcionar las llamadas, fracasando en otro caso).

¡Espero que te sea útil!

¿Te ha gustado este post? – Aprende .NET con los cursos on-line tutelados de campusMVP:

   ·
Preparación del examen 70-515: Desarrollo Web con .NET 4.0 (Tutelado por mi)

   · Desarrollo Web con ASP.NET 4.0 Web Forms (Tutelado por mi)

   · ASP.NET 4.0 Web Forms desde cero (Tutelado por mi)

   · Desarrollo Web con ASP.NET MVC 3 

   · Silverlight 4.0 – Aplicaciones Ricas para Internet (RIA)

   · jQuery paso a paso para programadores ASP.NET

   · Visual Studio 2010 desde cero

Recibir resultados de una página en otra: Transferencias y Cross-page posting

Post original en JASoft.org: http://www.jasoft.org/Blog/post/Recibir-resultados-de-una-pagina-en-otra-Transferencias-y-Cross-page-posting.aspx

Hay ocasiones en las que tenemos una página muy poblada de controles que se deben mostrar en pasos sucesivos. Desde una simple recogida de datos que se haya complicado un poco, hasta varios pasos de un asistente, por ejemplo.

Existen muchas maneras de solucionar esta situación y hacer que las páginas estén un poco más despejadas. Por ejemplo, podemos usar un control Wizard nativo de ASP.NET Web Forms. Sin embargo una forma mucho más natural de conseguirlo es recibir los resultados de la primera página en una nueva que los procesa y continúa el flujo de la aplicación. De hecho, en los paradigmas de programación convencionales (o sea, en todo lo que no sea Web Forms, incluyendo ASP.NET MVC pero también PHP, etc…) realmente es como se ha hecho siempre: unas páginas pasan datos a otras para procesarlos.

Transferencia en el lado del servidor

En ASP.NET disponemos de la instrucción Server.Transfer que nos permite derivar la ejecución actual de una página hacia otra, como si se ejecutasen en serie pero sin pasar por el lado cliente como ocurre con Response.Redirect. Además, durante la ejecución de la segunda página tenemos acceso al contexto de la llamada inicial y también a los controles de la página original y sus valores. Esta instrucción está disponible desde siempre en todas las versiones de ASP.NET ya que en realidad venía heredada de ASP 3.0 clásico, donde ya se utilizaba.

El principal inconveniente de la transferencia en el lado servidor es que los usuarios no son conscientes jamás de que este proceso ha tenido lugar, es decir, no ven reflejado en la barra de direcciones del navegador que en realidad se ha procesado otra página.

Si nos fijamos en la página de ejemplo que podrás descargar al final de este post, la página original es "Default.aspx":

PagEjemplo

Al ejecutar una transferencia en el servidor pulsando el correspondiente botón, aunque la página que se ha ejecutado es la que en el ejemplo he llamado "Transfer.aspx" en realidad en el navegador esto no se ve reflejado:

EjemploTransfer

Esto, además de despistar a los desarrolladores más noveles, puede tener inconvenientes dependiendo de cómo esté hecha la página cuando se pulsa el botón de volver en el navegador, por ejemplo.

Otro inconveniente menos frecuente de este método -pero que yo he tenido ya que sufrir en alguna ocasión- es la generación de excepciones de tipo ThreadAbortException debido a que se interrumpe la ejecución de un hilo. Ello es debido a que, internamente, Transfer llama siempre a Response.End (como se observa en la captura siguiente del código fuente), interrumpiendo la ejecución del hilo actual, lo cual puede dar lugar a este tipo de problemas:

ServerTransfer_Codigo
Pulsa para aumentar

Es un comportamiento documentado en la Knowledge Base de Microsoft, y se recomienda sustituir la llamada a transfer por otra a Server.Execute.

El método Transfer tiene un parámetro adicional aparte de la página que se quiere ejecutar que sirve para indicar si queremos que la página a la que transferimos tenga acceso a las colecciones Form y QueryString del objeto Response de la página original:

   1: Server.Transfer("~/MiOtraPagina.aspx", true);

Con esto podremos acceder desde la página transferida a los datos enviados a la página original desde un formulario o a través de la URL.

Cross Page Posting

Con ASP.NET 2.0 se introdujo una nueva forma de hacer la transferencia entre páginas llamada Cross Page Posting. Ésta se parece a la transferencia o la ejecución de páginas pero en realidad lo que hace es enviar la página directamente desde el cliente con un PostBack normal, sólo que en lugar de dirigir éste a la página original lo hace a una nueva página. Es, básicamente, provocar el envío del formulario a una nueva página en lugar de hacerlo a la actual que es lo habitual.

Lo que conseguimos es que se ejecute la página de la manera normal pero enviando los datos a un nuevo Web Form.

Se parece mucho a la transferencia de la página pero tiene dos diferencias fundamentales:

  1. El cambio de flujo se realiza desde el lado cliente, no desde el servidor.
  2. El usuario ve en su navegador la URL de la nueva página: no se queda en la misma página.

Si vemos el resultado de ejecutar el ejemplo adjunto con el botón de hacer "Cross page posting", observamos que la página resultante es la esperada y no la original:

EjemploCrossPage

Implementar Cross page Posting es muy sencillo: basta con asignar en el control que queremos que lo provoque, la propiedad PostbackUrl a la página a la que queremos enviar los datos:

PostBackUrlProp

Al hacerlo con un botón por ejemplo, se modifica el código de lado cliente para el evento "click" del botón HTMl resultante, de modo que envía los datos a la nueva página:

CodigoHTMLCrossPost

Pulsa para aumentar

Una cuestión adicional: aunque los datos se envían finalmente a otra página diferente a la original, el evento de servidor del botón se ejecuta igualmente. Los cambios que se hagan desde dicho evento a los controles de la página original no se verán reflejados al acceder a éstos en la página final, pero sí se pueden ejecutar otro tipo de tareas, como escribir a una base de datos, por ejemplo. Basta con poner un punto de interrupción en Visual Studio para ver que es así.

¿Cómo accedemos a los datos de la página original?

Tenemos diversas formas de acceder a los datos de los controles de la página original, tanto al hacer un Transfer como un Cross Page Posting.

La manera más fácil es obtener una referencia a la página original usando la propiedad PreviousPage de la página actual. Una vez obtenida la referencia es fácil encontrar cualquier control usando el método FindControl:

   1: if (Page.PreviousPage != null)

   2: {

   3:     TextBox txtPrev = (TextBox)Page.PreviousPage.FindControl("TextBox1");

   4:     if (txtPrev != null)

   5:         lblMsg2.Text = txtPrev.Text;

   6: }

   7: else

   8: {

   9:     lblMsg2.Text = "No es Cross page ni transferencia";

  10: }

Como vemos se verifica si hay una página anterior o no, en cuyo caso se busca el control TextBox original y se muestra su contenido en la página actual (todo esto dentro del evento Load de la página).

Sin embargo acceder a los controles usando FindControl es un método muy frágil y propenso a errores. Basta con que el control se cambie de ubicación en la página para introducirlo dentro de un contenedor, por ejemplo, para que ya no se encuentre. Eso por no mencionar la posibilidad de que le cambiemos el ID sin darnos cuenta de esta dependencia y que de repente todo deje de funcionar.

Por ello un método mucho más recomendable es definir una o más propiedades de sólo lectura en la página original para tener acceso a los controles subyacentes. en nuestro ejemplo podemos definir una página NombreAsaludar en la página Default.aspx, de este modo:

   1: //Definimos una propiedad para facilitar la lectura de los controles

   2: public string NombreASaludar {

   3:     get

   4:     {

   5:         return TextBox1.Text;

   6:     }

   7: }

Ahora podemos acceder en la página secundaria a la que hemos transferido o hecho CrossPosting al valor del control usando un código un poco más seguro:

   1: if (Page.PreviousPage != null)

   2: {

   3:     Default pagPrev = (Default)Page.PreviousPage;

   4:     lblMsg2.Text = pagPrev.NombreASaludar;

   5: }

   6: else

   7: {

   8:     lblMsg2.Text = "No es Cross page";

   9: }

Como vemos ahora en lugar de usar FindControl convertimos la PreviousPage al tipo correcto de la página original y ya podemos llamar a su propiedad NombreASaludar directamente.

Sin embargo aún existe una forma mejor de hacerlo y es incluir en la página secundaria una directiva @PreviousPageType, así:

   1: <%@ PreviousPageType VirtualPath="~/Default.aspx" %>

Con esto le estamos indicando a la infraestructura de ASP.NET el tipo de la página original, por lo que la propiedad PreviousPage tendrá ya automáticamente el tipo correcto y nos ahorraremos incluso la conversión:

   1: if (Page.PreviousPage != null)

   2: {

   3:     lblMsg2.Text = this.PreviousPage.NombreASaludar;

   4: }

   5: else

   6: {

   7:     lblMsg2.Text = "No es Cross page";

   8: }

¡Ahora sí que es directo!

¿Cómo se distingue entre una transferencia y Cross Page Postback?

Todo lo anterior funciona tanto en transferencias como en envíos de datos cruzados entre páginas. Sin embargo en ocasiones será necesario que podamos distinguir entre unas y otras. ¿Cómo podemos hacerlo?

Las páginas tienen una propiedad llamada IsCrossPagePostBack que nos permite saber si se ha producido o no este tipo de acción. Esta propiedad será verdadera para la página anterior en el caso de un Cross page postback pero no en el caso de una transferencia, así que basta con poner esto para distinguirlas:

   1: if (Page.PreviousPage != null && Page.PreviousPage.IsCrossPagePostBack)

Fíjate que no hay peligro de que rompa la comparación cuando no es ni una cosa ni la otra ya que el operador lógico "AND" (&&) está cortocircuitado, es decir, si la primera condición no se cumple ya no se comprueba la segunda, así que aunque PreviousPage sea nulo la segunda comparación no provoca una excepción.

Dejo un ejemplo con todas las posibilidades implementadas para que lo puedas estudiar y ver las pequeñas diferencias entre unos casos y otros: CrossPagePostingYTransfer.zip (14,3 KB)

¡Espero que te sea útil!

¿Te ha gustado este post? – Aprende .NET con los cursos on-line tutelados de campusMVP:

   ·
Preparación del examen 70-515: Desarrollo Web con .NET 4.0 (Tutelado por mi)

   · Desarrollo Web con ASP.NET 4.0 Web Forms (Tutelado por mi)

   · ASP.NET 4.0 Web Forms desde cero (Tutelado por mi)

   · Desarrollo Web con ASP.NET MVC 3 
   · Silverlight 4.0 – Aplicaciones Ricas para Internet (RIA)

   · jQuery paso a paso para programadores ASP.NET

   · Visual Studio 2010 desde cero