Feeds:
Entradas
Comentarios

Archive for the ‘WPF’ Category

Esta vez trataremos temas un poco más complejos e interesantes a nivel aplicación. Muchas veces necesitaremos que nuestra aplicación sea una aplicación SingleInstance, sin embargo en WPF no contamos con una forma nativa para manejar este tipo de aplicaciones, por lo que vamos a tener que programar un poco y utilizar cosas que ya conocíamos de WindowsForm.

En este post, lo que voy a tratar de explicar es lo básico que tendremos que saber para trabajar con este tipo de aplicaciones. También veremos los eventos que tenemos para manejar el cierre de una aplicación y veremos cómo podemos manejar la interacción entre los documentos que tengamos abiertos.

Cerrando la aplicación

Lo primero que veremos es que como manejar el cierre de la aplicación. Normalmente la aplicación no se cierra hasta el momento en que cerramos la última ventana de la aplicación. Este comportamiento por supuesto que lo podemos cambiar, seteando la propiedad Application.ShutdownMode. Esta propiedad la podemos setear ya sea por código o desde el XAML. En el supuesto caso que hayamos creado nuestra aplicación desde código, deberemos setear la propiedad Application.ShutdownMode antes de invocar al método Application.Run();

Los valores posibles para esta propiedad son los pertenecientes a la enumeración System.Windows.ShutdownMode:

Nombre 

Descripción 

OnLastWindowClose 

Por defecto cualquier aplicación creada toma este valor. Con la propiedad seteada en este valor, la aplicación no se cerrará hasta que se cierre la última ventana. Este comportamiento lo podemos ver en el Word, por ejemplo, donde tenemos varios documentos abiertos y hasta que no se cierra el último, la aplicación sigue funcionando.

OnMainWindowClose 

Este es el comportamiento que deberemos setear para una aplicación MDI, donde lo que pretendemos es que cuando se cierra la ventana principal se cierren todas sus ventanas contenidas.

OnExplicitShutdown 

Esta es una forma más extrema de cerrar nuestra aplicación ya que no se va a cerrar hasta que no la cerremos explícitamente llamando al método Shutdown(). Esto es útil para aplicaciones que deben quedar corriendo en background como sucede con el Messenger.

 

Sin importar el modo de cierre que hayamos seleccionado para nuestra aplicación, siempre podremos cerrarla invocando al método Application.Shutdown().

El método Application.Shutdown() devuelve el puntero de la aplicación al punto donde se llamó al método Application.Run(), es decir que cualquier línea de código que siga a la llamada del método Application.Run() se ejecutará antes de terminar de cerrar la aplicación, como así también se ejecutará el código que este en el evento Application.Exit().

Es importante notar que cuando el ShutDownMode este configurado como OnMainWindowsClose, al cerrarse la aplicación, cada ventana que este abierta se va a cerrar automáticamente, de la misma forma que si lo hiciéramos de forma manual, por lo tanto se va a ejecutar el código que tengan los eventos manejados. Una vez que se terminaron de cerrar todas las ventanas de la aplicación se cierra finalmente la misma. En caso de querer cancelar el cierre, esto se debe hacer desde el Evento Close de alguno de los formularios, para evitar que se cierren. Una vez que se disparó el evento Application.Exit ya no se podrá detener el cierre.

Evitando tener varias instancias de la aplicación

Hay dos maneras de lograr esto, una utilizando la clase System.Threading.Mutex
y la otra es aplicando el patrón singleton en el startup de la aplicación.

Si bien la primera de las dos opciones que veremos, es más sencilla, es menos flexible y en vez de devolver la aplicación que se encuentra abierta, crea una nueva aplicación y luego verifica si ya existía en memoria, en ese caso la cierra. Esto genera una sobrecarga adicional que no es necesaria, y en el caso de tener una rutina de inicio que demora algo de tiempo, entonces estaríamos desaprovechando tiempo y recursos.

Single Instancia WPF con Mutex

En el código de ejemplo podemos ver que se crea una instancia de Mutex, donde la aplicación pasa a ser su propietaria. La próxima vez que llama a la aplicación se intenta crear nuevamente la clase Mutex y si la misma ya existía no se puede volver a crear, ese resultado se guarda en la variable bNewInstance, si se verifica que no es una nueva instancia la que se está creando pasa a cerrarse.

Single Instance WPF con SingleInstance

La otra forma que tenemos de crear una aplicación single instance más flexible es utilizando un Manager actúe como wrapper para la aplicación WPF. Dentro del wrapper lo que primero verificaremos es que la aplicación no existe antes de crearla y si existe, en vez de crearla y destruirla, devolvemos la ya existente. Para aplicar esta solución, vamos a utilizar el soporte integrado que ofrece WindowsForm para este tipo de tareas.

La forma de hacer esto es crear una aplicación en forma manual, que haga de wrapper para la aplicación WPF. Cuando se llama a la aplicación (invocando Main()) se crea la clase wrapper que va a ser las veces de manager de aplicación y desde acá se instancia a la clase que crea la aplicación WPF.

Fig. 1

Al empezar a programar el wrapper, lo primero que vamos a tener que hacer es agregar la referencia al Assembly Microsoft.VisualBasic.dll para poder heredar de esta clase nuestro wrapper ya que tendremos que utilizar algunos miembros de la misma:

  • SingleInstance: Es una propiedad que nos habilitará la aplicación para que sea una aplicación SingleInstance.
  • OnStartup(): Deberemos sobrescribir el método para que cuando se ejecuta se cree nuestra aplicación WPF.

Adicionalmente podríamos sobrescribir el método OnStartupNextInstance() permitiendo así, que si se intenta crear una nueva instancia, se haga algo sobre la instancia actual, por ejemplo, traerla al frente o abrir un nuevo formulario o documento.

En resumidas cuentas, en el código crearemos el constructor de la clase wrapper y le definiremos que nuestra aplicación va a ser una aplicación SingleInstance, seteando la propiedad SingleInstance en true. También sobrescribiremos el método OnStartup() para crear ahí nuestra aplicación WPF.

Por otro lado creamos una nueva clase para nuestra aplicación WPF que en este caso se llama WPFApplication y haremos que herede de System.Windows.Application.

Sobrescribiremos el método OnStartup, también en esta y desde acá vamos a crear la instancia de la ventana que será la ventana principal y le indicaremos al Application que esta va a ser su MainWindows. Acto seguido mostramos la ventana, y ya tenemos nuestra aplicación single instance casi lista para funcionar.


El último paso antes de ejecutar nuestra aplicación, es crear una clase Startup que va a ser la clase inicial y configuramos la aplicación para que se inicie con esta clase.

Para esto en las propiedades del proyecto deberemos configurar el Startup Object con el nombre de esta clase. (Fig. 2)

Fig. 2

Una vez configurado el Startup Object estamos en condiciones de compilar nuestra aplicación y probar crear varias instancias.

Debido a que estamos capturando el evento Exit y mostrando un message box que nos indica que se está disparando el evento Exit, podremos comparar fácilmente la aplicación que utiliza Mutex contra este modelo de aplicación ejecutando las 2 aplicaciones intentando crear múltiples instancias de la misma. Veremos que en la aplicación con Mutex, estos eventos se disparan cuando se intenta crear una nueva instancia, debido a que la instancia se crea y se cierra en el momento. En cambio con esta forma de hacerlo, que recordemos es la que recomienda utilizar el equipo de desarrollo de WPF, veremos que esto no sucede ya que nunca se crean dos instancias, siempre se trabaja sobre una.

Para finalizar con este artículo veremos finalmente como interactuar con documentos dentro de la misma aplicación.

Interacción entre formularios de la misma aplicación

La forma más simple que tenemos de interacción entre las ventanas de la aplicación es a través del objeto Application. El objeto Application conserva la colección de ventanas que tiene abiertas. Esta colección se mantiene actualizada en forma automática y nos es de utilidad para funciones básicas, como controlar la cantidad de ventanas abiertas (Fig. 3), o para realizar interacción entre las mismas, estamos limitados a trabajar con objetos Windows o derivados que debemos conocer para poder hacer los casteos necesarios y poder acceder a los miembros.

Fig. 3

La otra opción es crear dentro de nuestra clase Application, colecciones de tipos de personalizados, que aparte de darnos más flexibilidad al conocer con qué tipo estamos trabajando, nos permite conocer la posición exacta de una ventana en nuestra colección (esto no lo podemos hacer con Application.current.windows), nos permite ordenarla, y podremos hacer cualquier otra acción que este permitida dentro de una colección.

Por ejemplo, si vamos tener una ventana con los datos de un cliente, podemos crear una colección de este tipo. Cuando interactuemos con cada una de estas ventanas, podremos acceder a sus propiedades y métodos, sin tener que hacer casteos o sin tener que preocuparnos por si existe el método o propiedad, ya que al ser todas las ventanas del mismo tipo, todas cuentan con los miembros que corresponden a ese tipo.

En el ejemplo (Fig. 4), veremos cómo en un listbox, se va a mostrar el nombre de cada cliente que se agrega en cada ventana.

Fig. 4

Concusión

A lo largo de este artículo, vimos como manejar los eventos necesarios a nivel aplicación y ventana, como así también las formas de cerrar una aplicación, cómo manejar colecciones de ventanas y cómo podemos trabajar con aplicaciones SingleInstance.

Con lo que aprendimos podremos crear aplicaciones más específicas y con un poco de imaginación y práctica, hasta podremos crear nuestro propio MDI Manager, ya que WPF no lo ofrece.

Read Full Post »

La forma más sencilla de crear una aplicación WPF, con el wizard de Visual Studio. Aunque esta es una forma sencilla, no nos enseña a comprender todo lo que hace para que nuestra aplicación funcione y justamente eso es lo que aprenderemos en este post.

Que hace Visual Studio cuando creamos una nueva aplicación WPF?

La comprensión de cómo en WPF se crea un aplicación nos va a permitir crear todo esa “plomería” en forma manual, permitiéndonos hacer incluso Custom Applications para poder realizar las tareas que necesitemos al iniciar nuestra aplicación.

Cuando el Wizard de Visual Studio crea una aplicación WPF crea dos partes, una que no es visible y que se similar a la siguiente:


///<summary>
App</summary>

[System.CodeDom.Compiler.GeneratedCodeAttribute(“PresentationBuildTasks”, “4.0.0.0”)]
public partial class App : System.Windows.Application {

///<summary>
InitializeComponent</summary>

[System.Diagnostics.DebuggerNonUserCodeAttribute()]

public void InitializeComponent()

{

#line 4 “..\..\..\App.xaml”
this.StartupUri = new System.Uri(“MainWindow.xaml”, System.UriKind.Relative);

#line default

#line hidden

}

///<summary>
/// Application Entry Point.

///</summary>

[System.STAThreadAttribute()]

[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main()

{

NormalApplication.App app = new NormalApplication.App();

app.InitializeComponent();

app.Run();

}

}

Para ver este código tenemos que abrir el archivo: App.g.cs en la carpeta obj\Debug dentro del directorio del proyecto.

La parte que normalmente vemos y podemos editar nosotros es la que se encuentra en el archivo App.xaml.cs

namespace NormalApplication

{
///<summary>

/// Interaction logic for App.xaml

///</summary>

public partial class App : Application{

}

}

Creando la aplicación a mano

Para crear la aplicación a mano, lo primero que tenemos que hacer es crear una clase a la que deberemos indicar desde las propiedades de la aplicación que es la que instanciará la ventana principal.

Si observamos en el siguiente código, lo primero que se hace es crear la aplicación, luego creamos la ventana que será la principal y finalmente llamamos al método Run del objeto application y le pasamos la ventana como parámetro.


public class StartUp

{

[STAThread] static void Main()

{
//Creo el objeto Application

var app = new Application();

//Creo la ventana

MainWindow w = new
MainWindow();

app.Run(w);

}

}

Para configurar cual es el método que va a lanzar nuestra aplicación tenemos que ir al as propiedades del proyecto y setear el startup object con el nombre de la clase que tiene el método Main, como se muestra en la figura:

Esto es todo lo que tenemos que hacer para hacer nuestro propio Startup. Si bien esto no parece tener mucha importancia a simple vista, más Es importante comprender como funciona.

Manejando los eventos de la página

Si tenemos necesidad de manejar los eventos de la página, entonces este ultima forma de crear las aplicaciones en forma manual, ya no sirve. Si nos fijamos en el Application creado por VisualStudio, podemos ver que la clase Main esta heredando de la clase Application, es decir que para poder crear una custom application que maneje los eventos de la página deberemos heredar de la clase Application.

El manejo de los eventos de las páginas para ambos casos es igual se hace en la clase principal, es decir la clase que inicializa la aplicación, ya sea que este esté separada en una o varias Partial Clases o que este toda en una sola clase.

Los eventos a nivel Application Class que podemos manejar son:

  • Startup: Se dispara inmediatamente después de llamarse al método Application.Run(). Este evento normalmente se usa cuando se hace una aplicación de consola, para revisar los parámetros que recibe la aplicación.
  • Exit: Se dispara en el momento en que la aplicación se cierra por cualquier motivo. Se dispara justo antes de que el método Application.Run() finalice. En este evento, ya no podemos evitar que la aplicación finalice. Este método lo podríamos usar para devolver un valor o lanzar alguna otra aplicación o volver a ejecutar la misma aplicación en forma recursiva, por ejemplo
  • SessionEnding: se dispara cuando finaliza una sesión de Windows por cualquier motivo. Para saber cuál es el motivo por el que se dispara el evento, podemos investigar la propiedad SessionEndingCancelEventArgs.ReasonSessionEnding. Desde este evento, si podemos detener el cierre de la aplicación, seteando la propiedad SessionEndingCancelEventArgs.Cancel
    en true; si no se setea esta propiedad, a continuación de este evento, se llama al Application.Shutdown().
  • Activated: Sucede cuando una de las ventanas en la aplicación se vuelve activa. Normalmente esto sucede cuando se pasa de un programa a otro, ademar de dispararse la primera vez que se abre una ventana de la aplicación.
  • Deactivated: Análogamente al evento anteriormente mencionado, este evento se dispara cuando la aplicación pierde el foco.
  • DispatcherUnhandledException: Se dispara cuando ocurre una excepción que no fue manejada en cualquier parte de la aplicación, una aplicación normal de este evento, sería el de loguear errores.

Para manejar estos eventos tenemos dos maneras posibles: podemos atachear un manejador de eventos o podemos sobrescribir el método correspondiente. Por otro lado tenemos que indicarle a la aplicación que método va a ser el que maneja a un evento, para esto basta con attachearlo usando un atributo en el App.xaml.

En el App.xaml quedaría algo asi:

<Application x:Class=”WindowsPresentationFundationApplication.App”

xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;

xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;

StartupUri=”Window1.xaml”

Startup=”OnStartup”>

</Application>

En el codebehind bastaría con sobreescribir el método OnStartup, quedaría de la siguiente manera:

public partial class App : Application

{

private bool unsavedData = false;

public bool UnsavedData

{

get { return unsavedData; }

set { unsavedData = value; }

}

protected override void OnStartup(StartupEventArgs e)

{

base.OnStartup(e);

UnsavedData = true;

}

}

Si prestamos atención, notaremos que no hay un evento atacheado de la forma normal en que se hace, sino que estamos sobrescribiendo un método ya existente, esto es porque estos métodos ya son parte de la clase base Application, es por esto que es una buena idea, siempre llamar al método de la clase base, por si este hace alguna tarea adicional que desconozcamos.

Conclusión

En este artículo aprendimos como se crea la clase Application y la importancia que la misma tiene. También vimos cuales son los eventos a nivel aplicación y como indicarle a nuestra aplicación que esos eventos están disponibles y que los tiene que usar y finalmente vimos que tenemos dos formas de manejar los eventos, o sobrescribimos los de la clase base, o lo manejamos como cualquier otro evento en C#.

En el siguiente artículo, trataremos tareas más especificas que se hacen a partir del manejo de estos eventos, como por ejemplo, que no se pueda abrir dos veces la misma aplicación, o trabajar con varias ventanas que interactúan dentro de nuestra aplicación.

Read Full Post »

Esta es una discusión que a menudo suelo escuchar. Hay quieren opinan que existen dos tipos de aplicaciones que se pueden realizar con WPF y hay quienes opinan que son tres tipos distintos. Lo cierto es que depende del punto de vista.

Si vamos a tomar como tipo de aplicación las que nos propone Visual Studio entonces tenemos 2 tipos de aplicación (fig.1).

  • WPF Application
  • WPF Browser Application (o XBAP Application)

Fig. 1

 

Pero no necesariamente esto significa que sean esos dos los tipos de aplicación que se pueden crear, ya que si lo vemos desde otro punto de vista, podemos ver que tenemos estos otros dos tipos de aplicaciones:

  • Windows-Based Applications
  • Page-Based Applications

Viéndolo de este modo, tenemos las Windows-Based application que son las aplicaciones Windows convencionales, y dentro de las Page-Based Applications, podemos decir que tenemos dos tipos de aplicaciones, diferenciadas por su contenedor.

Por un lado tenemos las aplicaciones que corren sobre el contenedor NavigationWindow, estas se muestran dentro de una ventana de Windows, a la que automáticamente se le generan los botones de “Forward” y “Backward” para ir navegando las páginas por las que ya pasamos. (fig. 2).

Fig. 2

Y por otro lado tenemos las XBAP Applications o Browser Based Applications. Estas corren directamente sobre el workspace del browser, ya sea Internet Explorer o Firefox que son los dos browsers soportados. La ventaja de este tipo de aplicación es que si tenemos que hace un deploy a muchas workstations que estén distribuidas, es más sencillo hacerlo vía browser. La desventaja es que tiene limitaciones de seguridad y no son soportadas todas las funcionalidades de WPF. Una XBAP Application se vería como se muestra en la fig. 3

Fig. 3

 

Diferencias entre las aplicaciones Windows-Based y XBAP

La principal y más importante diferencia entre los dos tipos de aplicación es que las aplicaciones XBAP proveen una instalación completamente transparente al usuario.

Este tipo de aplicaciones corre desde el caché del browser y la única forma de ejecutarla es llamando nuevamente la URL desde el browser. Cuando se hace esta llamada, se verifica contra el servidor si hay una nueva versión del archivo .XBAP, en caso de que la haya se baja la nueva versión al caché y se ejecuta.

Como contrapartida, todas las aplicaciones XBAP corren dentro de un sandbox con permisos Partial Trust, es decir que solo pueden correr algunas librerías de .NET lo que hace que la aplicación sea un poco más restrictiva.

 

Restricciones de las aplicaciones XBAP

Como mencioné anteriormente las aplicaciones XBAP se ven restringidas en cuanto a las librerías que pueden usar y el nivel de acceso que pueden tener dentro del sistema de usuario final. Como corren con permisos parciales tienen el mismo nivel de permisos que cualquier aplicación que corra dentro de la zona de internet. Si bien la gran mayoría de la funcionalidad de WPF corre dentro de este sandbox, aún existen algunas limitaciones, a saber:

  • No pueden correr como si fueran aplicaciones Stand Alone.
  • No pueden utilizar cuadros de dialogo standard.
  • No pueden utilizar Interop con controles de Windows o ActiveX
  • No soporta el Drag and Drop.
  • No soporta Efectos Shader

Por otra parte se puede perfectamente usar.

  • Controles de Interface de usuario.
  • Controles de entrada de texto.
  • Flow Documents y los lectores asociados.
  • Documentos XPS.
  • Dibujos 2D.
  • 3D.
  • Animaciones.
  • Audio.
  • Video.
  • Paginas.
  • Cuadros de Dialogo.
  • OpenFileDialog.
  • Drag and Drop (solo dentro de la aplicación)
  • Llamadas a servicios de WCF.
  • Llamadas a Web Services.

 

Cuadro comparativo entre las características de las aplicaciones Standalone y XBAP.

Característica

Standalone

XBAP

Instalación

Instalación en requerida en la máquina del usuario

La instalación se hace de forma automática y es transparente al usuario.

Menú de inicio

Tiene acceso directo

No aparece ningún acceso, solo se accede vía URL

Panel de Control

Se permite desinstalación

No hace falta desinstalar.

Métodos de Instalación

XCopy, MSI, ClickOnce

Automático vía ClickOnce.

CAS

Corre con permisos Full a menos que lo modifique el administrador.

Corre con los permisos de la zona de internet

Restricciones SandBox

No existen, salvo que el administrador cambie los permisos.

Existen.

Proceso

Corre dentro de su propia ventana.

Corre dentro del PresentationHost.exe

Actualizaciones Automáticas

No se actualizan automáticamente, salvo que el desarrollador utilice ClickOnce para el deploy o que haya programado una rutina de auto actualización.

Siempre se utiliza la última versión distribuida al servidor.

Acceso Desconectado

Permitido.

No se puede abrir la aplicación a menos que se pueda navegar a la URL de la aplicación.

Requerimientos

.NET 3.0 o superior en la computadora del usuario final.

.NET 3.0 o superior en la computadora del usuario final. Internet Explorer 6 o superior o Firefox 2.0 o superior.

 

Conclusión

Existen dos tipos de aplicación, las que se basan en páginas y las que se basan en Windows. Las que se basan en páginas son un poco más limitadas en cuanto al nivel de acceso que tienen dentro de la computadora del usuario final, pero son mucho más sencillas de distribuir, sobre todo si se tienen muchos usuarios distribuidos y la aplicación se actualiza periódicamente.

Dentro de las aplicaciones basadas en páginas, tenemos 2 categorías identificadas por el tipo de host. Están las que corren sobre su propia ventana y las que necesitan un browser para ser hosteadas. Esta sub clasificación es que muchas veces genera la discusión de si son 2 o 3 tipos de aplicación ya que muchos consideran que los tipos de aplicación son las que corren en el browser, las basadas en páginas que corren dentro de su propia ventana y las aplicaciones Standalone.

Read Full Post »