viernes, 12 de junio de 2009

Drag and Drop muy fácil con un DataGrid Flex


En el siguiente ejemplo vamos a ver como lograr de manera muy fácil la funcionalidad de pasar datos de un DataGrid hacia otro o viceversa con solo realizar el típico "drag and drop" del ratón (presionando el botón izquierdo del ratón sobre el dato que queremos pasar y arrastrándolo hasta el DataGrid que deseamos que reciba la información). Para empezar la explicación vamos a ver en funcionamiento del ejemplo que vamos a realizar:


Existen varias maneras de realizar esta funcionalidad, algunas de ellas requieren mayor dedicación y programación por parte del desarrollador y otras no, así como también las que requieren mayor programación también permiten un mayor control. En este voy a explicar la mas sencilla y fácil de implementar.

Todos los objetos que heredan de la clase ListBase (El DataGrid tambien, por heredar de esta clase) tienen 5 propiedades muy interesantes que permiten contemplar la funcionalidad Drag and Drop de una manera muy sencilla:

  1. dragEnabled: Esta propiedad especifica si el componente ListBase debe permitir que al seleccionar un dato se pueda iniciar el arrastre de la informacion hacia cualquier componente ListBase (incluyendo este mismo componente).
  2. dropEnabled: Esta propiedad especifica si el componente ListBase debe permitir que un dato entrante pueda ser soltado sobre este componente.
  3. dragMoveEnabled: Esta propiedad dice si cuando un objeto es arrastrado a partir de este componente se debe borrar la copia original que inicio el arraste. Es decir, si esta propiedad es falsa y hacemos un drag and drop de algun item de la lista, se mantendrá una copia del item en el lugar donde si inicio el movimiento.
  4. allowMultipleSelection: Con esta propiedad podemos realizar el drag and drop de multiples objetos (items) en un mismo movimiento.
  5. allowDragSelection: Esta propiedad nos indica si es posible realizar una seleccion sobre un item para realizar el drag and drop. Si es falsa no podremos seleccionar ningun item para realizar un drag and drop sobr el.
Entonces si en este ejemplo nosotros queremos realizar un drag and drop de el datagrid superior al datagrid inferior entonces debemos colocarle al datagrid superior las siguientes propiedades dragEnabled, allowDragSelection y dragMoveEnables como true. Y en el caso del datagrid inferior debemos colocarle la propiedad dropEnabled como true.

Si nosotros quisieramos podríamos reemplazar uno de los DataGrid utilizados en este ejemplo por cualquier otro componente que herede de la clase ListBase (El List, TitleList, Tree, etc.).

Bueno eso es todo! Como pueden ver es demasiado simple y fácil de realizar. Cualquier pregunta, si es que las tienen, no duden en realizarlas en los comentarios del post que yo siempre estoy pendiente de responderlas! Para descargar el código fuente del ejemplo presiona click aquí.


Continuar leyendo...

martes, 9 de junio de 2009

Listado de metatags y sus utilidades en flex


En el articulo anterior "Utilizando metadata para describir las clases en Flex" explique un poco la definicion de Metadata y di un ejemplo de su tipico uso para expecificar a flex un poco más información acerca de los datos, clases y propiedades que estamos utilizando en nuestra aplicación. En este caso voy a describirles un ejemplo de uso y explicación para cada tipo de metatag que podemos definir en Flex, sin hablar mucho más vamos a empezar con los metatags:

[ArrayElementType]
Para decir que tipo de datos debe contener el ArrayCollection. Por ejemplo:

[ArrayElementType("String")]
public var newStringProperty:Array;

[Bindable]
Para utilizar una propiedad como “data binding expression”, este tipo de propiedades son utilizadas para que cuando flex detecte un cambio en ellas actualice todos los destinos que tienen esta propiedad asiganada con el típico “propertyChangeEvent”. Por ejemplo:

[Bindable]
public var personas:ArrayCollection


<mx:DataGrid id="dg" dataProvider="{ personas }">
</mx:DataGrid>


[DefaultProperty]
Se refiere a la propiedad por defecto del componente, esta “defaultProperty” es la que se setea en mxml cuando se coloca entre los tags del componente. Por ejemplo:
<mx:DataGrid id="dg" dataProvider="{ personas }">
<mx:columns>
<mx:DataGridColumn dataField="email" headerText="Email"/>
</mx:columns>
</mx:DataGrid>
En este caso la defaultProperty del mx.Control.Datagrid seria un objeto de tipo Columns, que a su vez tiene una defaultProperty de tipo ArrayCollection de DatagridColumn.

[Deprecated]
Se utiliza para que el compilador arroje un warning para notificar al usuario que esa propiedad o clase esta obsoleta.


[Embed]
Le dice al compilador que incluya el recurso dentro de la película al momento de compilarla. Un recurso puede ser una imagen, sonido, video, icono, etc. Por ejemplo:
[Embed(source="com/assets/icono.png")]
private var icon:Class;


[Event]
Se utiliza para definir un evento que una clase arroja. Por ejemplo, el mx.Control.Button tiene como definido el evento MouseEvent.Click como metadato. De esta manera cuando estamos definiendo un botón en mxml podemos colocar un handler al evento click.
<mx:Button click="buttonClickHandler()" />


[Exclude]
Se utiliza para decirle al compilador que no muestre esta propiedad al momento de autocompletar, es como decir que el atributo es privado, por ejemplo:
[Exclude(name="label", kind="property")]


[ExcludeClass]
Le dice al compilador que excluya la clase al momento de autocompletar, es como decir que la clase es privada.


[IconFile]
Es el icono que va a tener la clase (componente). Por ejemplo:

[IconFile("MyButton.png")]
public class MyButton extends Button
{
...
}

[Inspectable]
Se utiliza para decirle al compilador que posibles valores puede tener una propiedad, de que tipo debe ser (String, int, Number, etc.) cual debe ser su valor por defecto, etc. Para más información revisar el siguiente vinculo: http://livedocs.adobe.com/flex/201/html/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Book_Parts&file=metadata_141_11.html


[InstanceType]
Para decir de que tipo debe ser una propiedad, por ejemplo (Cobra sentido en el caso de que la propiedad haya sido definida como una interfaz o una clase muy general):


// Define a deferred property for the top component.
[InstanceType("mx.controls.Label")]
public var topRow:IDeferredInstance;

[NonCommittingChangeEvent]
Este metatag le especifica a flex que no debe lanzar validadiones de data (commits típicos del data binding) en el evento especificado, por ejemplo:


[Event(name="change", type="flash.events.Event")]
class MyText extends UIComponent {
...

[Bindable(event="valueCommit")]
[NonCommittingChangeEvent("change")]
function get text():String {
return getText();
}
function set text(t):void {
setText(t);
// Dispatch events.
}
}

En este caso cuando el texto (propiedad text) cambie no va a ocurrir ninguna validación, pero en el caso de que se presione la tecla Enter, ocurrirá un valueCommit y entonces si se dispararan las validaciones de datos.


[RemoteClass]
Especifica que esta clase va mapeada contra un objeto en el servidor, le dice a flex que debe incluir la información de la case al momento de serializarla. Por ejemplo:


[RemoteClass(alias="com.CctVO")]
public class Carrito {
public var id : String;
[Inspectable(defaultValue="sin pagar",enumeration="sin pagar,pagado")]
public var estado : String;
public var productos : ArrayCollection;

[Transient] public function get total() : int
{
return productos.length;
}
}

[Style]
Para especificar que una propiedad es de tipo “estilo” para un componente, por ejemplo:


[Style(name="verticalAlign", type="String", enumeration="bottom,middle,top", inherit="no")]
[Transient]

Le dice a flex que debe ignorar cierta propiedad a la hora de serializar una clase para ser mapeada contra un objeto del lado del servidor. Por ejemplo:


[RemoteClass(alias="com.CctVO")] public class Carrito { public var id : String; public var estado : String; public var productos : ArrayCollection; [Transient] public function get total() : int { return productos.length; }
}

En este caso no se debe enviar la propiedad “total” al servidor ya que es un atributo calculado en ejecución. Para ver mas información hacer click en el siguiente vinculo: http://livedocs.adobe.com/flex/3/html/help.html?content=metadata_3.html


Esos son todos los metatags que conozco hasta la fecha que se peuden definir a la hora de programar en flex, espero les sean de mucha utilidad asi como para mi lo han sido. Cualquier cosa les dejo un documento en PDF con las definiciones y ejemplo que he dado en este artículo.



Continuar leyendo...

jueves, 4 de junio de 2009

Utilizando metadata para describir las clases en Flex


Si nos vamos a la definicion de wikipedia la metadata no es mas que información "sobre la data", es decir, es describir como es la data. Una metadata esta compuesta de una serie de metatags, donde cada uno de estos ultimos se encarga de describir alguna caracteristica de la clase. En un metadato podemos decile al flex que tipos de datos debe contener un ArrayCollection, si una imagen se debe cargar dentro de la pelicula SWF o cargarla remotamente al momento de ejecutar la pelicula, cuales son los posibles valores que puede tomar una propiedad, etc. Supongamos, en el siguiente ejemplo, que tenemos la clase "Carrito" que viene definida de la siguiente manera:


package
{
import mx.collections.ArrayCollection;

public class Carrito
{
public var id : String;
public var estado : String;
public var productos : ArrayCollection;

public function get total() : int
{
return productos.length;
}
}
}

Esta clase no tiene ningun tipo de "Metatag"y lo unico que conocemos de ella es que tiene tres atributos, sus tipos y sus nombres. Ahora supongamos que queremos utilizar este objeto como un "objeto remoto" para utilizarlo en algun lenguaje del lado del servidor como java, php, c#, etc. Para eso debemos decirle al compilador que este objeto esta representado del lado de java y que debe conservar la informacion de la clase al momento de serializar, para eso utilizamos el metatag [RemoteClass]. Tambien debemos decirle entonces que la propiedad "total" no debe ser enviada al servidor por tratarse de una propiedad calculada (seria como por ejemplo la edad, al pasar la fecha de nacimiento se puede calcular la edad), para excluir propiedades de ser enviadas para el servidor utilizamos el metatag [Transient]. Ahora supongamos que queremos definir que el estado de el carrito solo puede ser o "pagado" o "sin pagar", para eso debemos utilizar el metatag [Inspectable] y definir dentro de el los dos valores que puede tomar ese atributo. Con estas tres definiciones la clase nos quedaría de la siguiente manera:

package
{
import mx.collections.ArrayCollection;

[RemoteClass(alias="com.CctVO")]
public class Carrito
{
public var id : String;

[Inspectable(defaultValue="sin pagar",enumeration="sin pagar,pagado")]
public var estado : String;

public var productos : ArrayCollection;

[Transient]
public function get total() : int
{
return productos.length;
}
}
}

Este es un gran ejemplo de metadatos que podemos utilizar en flex. Si quieres conocer mas sobre los metadatos de flex te invito a entrar e mi articulo "Listado de metatags y sus utilidades en flex" donde explico de manera mucho más detallada todas las posibles descripciones que podemos realizarle a una clase al momento de programarla. Cualquier pregunta no duden en escribirla en los comentarios de este articulo.

Continuar leyendo...

miércoles, 3 de junio de 2009

Extendiendo de el SuperTitleWindow para que todas tus ventanas tengan "Cerrar" y "Aceptar" de forma nativa


Desde que empece mi aprendizaje en el desarrollo de aplicaciones con flex, he tenido la necesidad de crear ventanas emergentes con un formato especifico, todas ellas debian tener un botón de "Aceptar" y "Cancelar" para cerrar la ventana en el momento que el usuario lo creyera conveniente. En este ejemplo vamos a ver como se puede estantadrizar el comportamiento de todas las ventanas emergentes de una aplicacion apartir de la clase SuperTitleWindow.cs que tiene inmerso el siguiente comportamiento:
  1. Metodo "Aceptar" cuando se presiona el botón "Enter" del teclado.
  2. Metodo "Cancelar" cuando se presiona el boton "ESC" del teclado.
  3. Metodo "Limpiar Pantalla" cuando se presionan las teclas "shift" + "Delete".
  4. Botón cerrar en la parte superior derecha de la ventana (al estilo windows).
  5. Obliga a definir el foco inicial del mouse dentro que se utilizara al desplegarse la ventana.
  6. Permite modificar la logica a realizar una vez despachado el evento "Cerrar" o "Aceptar".


Para que se entienda un poco mejor vamos a ver un ejemplo de ventana emergente que herede del SuperTitleWindow.

Si observamos el codigo de esta ventana vamos a notar unas pequeñas particularidades con respecto a las ventanas tradicionales:

Código LoginWindow.mxml

<?xml version="1.0" encoding="utf-8"?>
<SuperTitleWindow xmlns="popupClasses.*" xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="174" title="Inicio de sesión">
<mx:Script>
<![CDATA[
import popupClasses.SuperTitleWindowEvent;
import mx.managers.PopUpManager;

private function aceptarClick() : void
{
//Lanzo el evento SuperTitleWindowEvent.OK_WINDOW para que se llame el metodo okPressed
var ev : SuperTitleWindowEvent = new SuperTitleWindowEvent(SuperTitleWindowEvent.OK_WINDOW);
this.dispatchEvent(ev);
}

private function cancelarClick() : void
{
//Lanzo el evento SuperTitleWindowEvent.CANCEL_WINDOW para que se llame el metodo cancelPressed
var ev : SuperTitleWindowEvent = new SuperTitleWindowEvent(SuperTitleWindowEvent.CANCEL_WINDOW);
this.dispatchEvent(ev);
}

override protected function setWindowFocus():void
{
//Seteo el foco al text input llamado "usuario"
//si no implemento esta funcion entonces se disparará una excepcion
//de esta manera obligo a que siempre se defina el foco inicial de una ventana emergante
usuario.setFocus();
}

override protected function okPressed(event:SuperTitleWindowEvent):void
{
//aqui inserto mi logica de aceptar, por ejemplo una validacion.


PopUpManager.removePopUp(this);
}

override protected function cancelPressed(event:SuperTitleWindowEvent):void
{
super.cancelPressed(event);//Debo remover esto si deseo sobreescribir el comportamiento original al momento de presionar cacelar
}

override protected function clearPressed(event:SuperTitleWindowEvent):void
{
//aqui va la logica para limpiar la ventana, se despacha con las teclas "shift" + "delete"
super.clearPressed(event);
}
]]>
</mx:Script>
<mx:Label text="Ingrese el nombre de usuario y contraseña"/>
<mx:HBox width="100%">
<mx:Label text="Nombre de usuario:"/>
<mx:TextInput id="usuario"/>
</mx:HBox>
<mx:HBox width="100%">
<mx:Label text="Contraseña:"/>
<mx:TextInput/>
</mx:HBox>
<mx:ControlBar horizontalAlign="right">
<mx:Button label="Cancelar" click="cancelarClick()"/>
<mx:Button label="Aceptar" click="aceptarClick()"/>
</mx:ControlBar>
</SuperTitleWindow>

Lo primero que notamos es que la ventanta hereda de la clase SuperTitleWindow, luego notamos que existen 6 funciones principales que las podemos utilizar dependiendo de nuestros requerimientos:
  1. okPressed() : Esta funcion se dispará automaticamente cada vez que la ventana esta a punto de cerrarse, en esta funcion debo agregar la logica a realizar justo antes de cerrar.
  2. cancelPressed() : Ocurre igual que en la funcion okPressed pero en este caso cuando la ventana esta apunto de cerrarse.
  3. clearPressed() : Esta funcion se ejecuta automaticamente cada vez que la ventana debe limpiarse, se debe introducir aqui la logica de limpiado, por ejemplo en este caso la logica de limpiado es:
    usuario.text = "";
    clave.text = "";
  4. setWindowFocus(): esta funcion es oblicatoria de sobreescribir, esto se debe a que debemos colocar el foco del mouse sobre el primer campo del formulario que se encuentra en la ventana, de esta manera la aplicacion fluye sin la necesidad de la intervencion del mouse. Si no se sobreescribe esta funcion, entonces ocurre una excepción.
  5. cancelarClick() : Esta funcion la debemos crear en caso de que queramos colocar un botón de cancelar dentro de la ventana, esta funcion debe despachar al evento SuperTitleWindowEvent de tipo SCANCEL_WINDOW para que sea escuchado por el SuperTitleWindow y volvamos a caer en el metodo cancelPressed().
  6. aceptarClick() : Esta funcion la debemos crear en caso de que queramos colocar un botón de cancelar dentro de la ventana, esta funcion debe despachar al evento SuperTitleWindowEvent de tipo OK_WINDOW para que sea escuchado por el SuperTitleWindow y volvamos a caer en el metodo okPressed().
Por otro lado tenemos 3 propiedades configurables para la confirmacion de las tres acciones tipicas en la ventana Aceptar, Cancelar, Limpiar:
  1. askClear: Defino si debo o no debo confirmar la limpieza de la pantalla, por defecto es verdadera para obligar al prigramador a deshabilitarla en caso que lo crea pertienente.
  2. askOk: Defino si debo o no debo confirmar el cerrado de la pantalla, por defecto es verdadera para obligar al prigramador a deshabilitarla en caso que lo crea pertienente.
  3. askCancel: Defino si debo o no debo confirmar el cerrado de la pantalla, por defecto es verdadera para obligar al programador a deshabilitarla en caso que lo crea pertienente.
Al heredar de esta clase estamos teniendo varias ventajas, la primera de ellas es que estamos estandarizando la implementacion de las ventanas, de esta manera el usuario se acostumbrará a presionar el boton "Enter" para aceptar las ventanas emergentes y "ESC" para cancelarlas. Por otro lado se organiza y estructura el código, se fomenta la reutilizacion del codigo y si se necesita variar algun comportamiento se puede manipular a la SuperTitleWindow.cs en lugar de realizar modificaciones a todas las ventanas por separado.

Para descargar este ejemplo pueden hacer click en el siguiente vinculo, cualquier duda no duden en preguntarlo como comentario de este post, espero que les sea de tanta utilidad como a mi.



Continuar leyendo...