Q-flow 3.4 Diseño de formularios personalizados±o de formulario… · Si se agregaron los...

20
i Urudata Software Canelones 1370 • Piso 2 CP11200 Montevideo, Uruguay Teléfono: (598) 2900 76 68 • Fax: 2900 78 56 Código del manual: Qf340014ESP Versión: 1.0 Se aplica a: Q-flow 3.4 Última revisión: 28/11/2013 Q-flow 3.4 Diseño de formularios personalizados

Transcript of Q-flow 3.4 Diseño de formularios personalizados±o de formulario… · Si se agregaron los...

i

Urudata Software Canelones 1370 • Piso 2 CP11200

Montevideo, Uruguay Teléfono: (598) 2900 76 68 • Fax: 2900 78 56

Código del manual: Qf340014ESP Versión: 1.0 Se aplica a: Q-flow 3.4 Última revisión: 28/11/2013

Q-flow 3.4

Diseño de formularios

personalizados

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

2

Tabla de Contenido

Introducción ................................................................................................................. 3

Diseño de formularios personalizados ...................................................................... 3

Creación de una solución vacía .............................................................................................................. 3

Inclusión del sitio web de Q-flow en la solución ................................................................................... 4

Configuración de Visual Studio para utilizar la biblioteca de controles ............................................ 4

Creación de un formulario personalizado.............................................................................................. 5

Controles de los formularios de Q-flow ................................................................................................. 6

Validaciones del lado del cliente ............................................................................................................ 8

Scripts disponibles a través del objeto Host ........................................................................................... 8

Scripts clásicos de Q-flow ..................................................................................................................... 12

Validaciones del lado del servidor ........................................................................................................ 15

FlowInteraction ...................................................................................................................................... 16

Eventos de adjuntos.............................................................................................................................. 18

Dos métodos para hacer validaciones .................................................................................................. 18

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

3

Introducción

El propósito de este manual es explicar cómo diseñar formularios personalizados. Los formularios personalizados son páginas de ASP.NET que pueden ser utilizadas para sustituir los formularios estándar de Q-flow. El concepto de formulario personalizado se explica en el manual del diseñador de procesos del negocio. Allí también se explica cómo, una vez pronto un formulario, se debe proceder para asociarlo a un template o a un paso de un template. Este manual se ocupa solamente de explicar cómo construir un formulario personalizado.

Lectores de este manual deberían estar familiarizados con los aspectos más importantes de Q-flow y con el diseño de procesos en ese producto. Además, deberían tener conocimientos de programación en ASP.NET 2.0.

Diseño de formularios personalizados

Los formularios personalizados son páginas ASP.NET (aspx). El desarrollo de formularios personalizados requiere conocimientos de programación, ASP.NET y de la herramienta utilizada para diseñarlos (en general, Visual Studio o Visual Studio Web Developer Express Edition, que es gratuito).

La herramienta utilizada debe ser compatible con la versión 3.5 del .NET Framework, por lo que, en caso de utilizar Visual Studio, debe utilizar la versión 2008 o alguna versión posterior.

Para desarrollar un formulario personalizado, es conveniente que el desarrollador tenga instalado en su equipo el sitio web de Q-flow, para así poder probar fácilmente los formularios en su ambiente de desarrollo. Para obtener información acerca de cómo instalar el sitio web de Q-flow, consulte el manual de instalación y configuración (Qf340002ESP-Instalación y Configuración).

Una vez instalado el sitio web de Q-flow, cree una solución vacía en Visual Studio y agregue el sitio web de Q-flow como proyecto de la solución.

Q-flow ofrece una biblioteca de controles para utilizar en los formularios personalizados. Estos controles son los que permiten utilizar en los formularios los elementos que Q-flow incluye en los formularios estándar y tener disponibles todas las funcionalidades de los formularios estándar de Q-flow. La biblioteca de controles está implementada en el archivo Qflow.Web.CustomForms.dll.

Las siguientes instrucciones suponen que el desarrollador tiene instalado el sitio web de Q-flow en su equipo de desarrollo.

Creación de una solución vacía

1. Abra Visual Studio. 2. Haga clic en Archivo, Nuevo, Proyecto. 3. En la ventana que le permite elegir el tipo de proyecto que desea crear, seleccione, dentro de la

categoría “Otros Tipos de Proyectos”, la opción “Soluciones de Visual Studio”. 4. Seleccione la opción “Solución en Blanco”. Si no tiene la opción de seleccionar “Solución en

Blanco”, cree un proyecto de otro tipo (por ejemplo, Aplicación Web ASP .NET) y una vez creada la solución, quite el proyecto creado.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

4

Inclusión del sitio web de Q-flow en la solución

1. Haga clic con el botón derecho del ratón sobre el ícono que representa la solución en el explorador de soluciones.

2. Seleccione “Agregar”, “Sitio web existente….”. 3. Seleccione la carpeta en la que se encuentra el sitio de Q-flow. La carpeta en la que el sitio se

instala por defecto es C:\Inetpub\wwwroot\QflowWebSite. 4. Si Visual Studio le advierte de que el proyecto utiliza una versión previa del .Net Framework, y

le pregunta si desea actualizarlo para que use el .Net Framework 4.0, conteste que no.

Configuración de Visual Studio para utilizar la

biblioteca de controles

Antes de comenzar a diseñar los formularios, es conveniente agregar los controles de Q-flow a la barra de herramientas de Visual Studio. Para hacerlo, proceda de la siguiente forma:

1. En Visual Studio, seleccione en el menú “Herramientas” la opción “Elegir elementos del cuadro de herramientas…”. Visual Studio abrirá una ventana como la que se muestra en la Figura 1.

Figura 1 Ventana que permite agregar controles a la barra de herramientas de Visual Studio

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

5

2. Haga clic en “Examinar…” para buscar el archivo Qflow.Web.CustomForms.dll. Navegue hasta la carpeta “bin”, del sitio web, donde se encuentra el archivo, y hágale doble clic. Esto agregará los controles de Q-flow a la lista (Figura 2).

3. Haga clic en “Aceptar”. Esto agregará los controles de Q-flow. La próxima vez que abra un proyecto web en Visual Studio, los controles de Q-flow aparecerán en la barra de herramientas.

Figura 2 Los controles de los formularios de Q-flow acaban de ser agregados

Creación de un formulario personalizado

Para crear un formulario personalizado:

1. Agregue una nueva página aspx en la carpeta CustomForms (por ejemplo, MyForm.aspx). Si está trabajando en Windows Vista o alguna versión posterior de Windows, tendrá que ejecutar Visual Studio como administrador para poder agregar una página a esa carpeta, ya que ésta seguramente normalmente se encuentra dentro de wwwroot, que es una carpeta protegida.

2. Abra el archivo que contiene el código de la página y modifíquelo para que la clase de la página sea una clase derivada de alguna de las clases de los formularios de Q-flow. Cuál debe ser la clase base de su formulario depende del tipo de formulario que esté desarrollando. La tabla que se muestra abajo indica el nombre de la clase base correspondiente a cada tipo de formulario. Las tres clases base posibles pertenecen al namespace Qflow.Web.CustomForms.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

6

Tipo de formulario Clase base

Inicio StartFlowBase

Respuesta TaskResponseBase

Workflow FlowFormBase

3. Registre en su formulario los controles de Q-flow. Si se agregaron los controles de Q-flow a la barra de herramientas, Visual Studio los registrará automáticamente la primera vez que arrastre uno de ellos hacia la vista del diseño o de edición del código ASP.NET del formulario. Los controles están en el assembly Qflow.Web.CustomForms y pertenecen al namespace Qflow.Web.CustomForms.Controls. Ejemplo:

<%@ Register Assembly="Qflow.Web.CustomForms"

Namespace="Qflow.Web.CustomForms.Controls" TagPrefix="qflow" %>

4. Desarrolle su formulario. 5. Utilice el diseñador de procesos de negocio para asociar el formulario que creó al template o

paso que desee. Esta operación se describe en detalle en el manual del diseñador de procesos de negocio (Qf340003ESP-Diseñador de Procesos del Negocio).

Controles de los formularios de Q-flow

Todos los controles tienen las propiedades de la clase WebControl y otras propiedades adicionales. Además, todos los controles, salvo el control “Submit”, tienen la propiedad DisplayMode. El valor de esta propiedad indica cómo Q-flow va a mostrar el control en el sitio web. Las opciones son cuatro:

ControlWithLabel: Q-flow mostrará el control junto a una etiqueta que indicará su nombre.

Control: Q-flow sólo mostrará el control, sin mostrar ninguna etiqueta.

Label: Q-flow sólo mostrará la etiqueta.

Text: Q-flow mostrará sólo el valor del dato representado por el control.

Los controles para formularios de Q-flow son los siguientes:

Attachments: Muestra los archivos adjuntos de un workflow. Si los archivos adjuntos son modificables en el paso asociado al formulario, permite agregar y borrar archivos adjuntos.

o CellPadding: tamaño del relleno de las celdas de la tabla principal o CellSpacing: espacio entre celdas de la tabla principal o CssAddInstance: estilo del botón de agregar instancia o CssAlternatingRow: estilo de las filas pares o CssHeader: estilo de la primera fila (la que contiene los encabezados) o CssRemoveInstance: estilo del botón de quitar instancia o CssRow: estilo de las filas impares o CssTable: estilo de la tabla principal

Data: representa un dato de aplicación. Cuando Q-flow muestre el formulario en el sitio web, reemplazará este control por el control asociado al dominio al que pertenece el dato. El dato será editable o no, según lo que esté definido en el alcance del dato para el formulario. El valor de la propiedad DataName es el nombre del dato representado por el control. Cuando Q-flow muestre el formulario, utilizará ese nombre para saber qué dato mostrar en el lugar donde está el control. La propiedad ReadOnly permite especificar que el dato es solamente para lectura (no se puede modificar) independientemente del alcance del dato (lo normal es determinar si el dato es editable o no mediante el alcance del dato para el formulario). En estos casos se

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

7

supone que el dato será modificado por medio de código. Para mostrar datos que pertenecen a bloques de líneas utilice el control Line.

o En el formulario del workflow no tiene sentido utilizarlo. o En un formulario de inicio, es el botón que permite iniciar el workflow. o En un formulario de respuesta, es el botón que permite responder la tarea.

FlowDescription: Este control muestra la descripción del workflow actual, o permite ingresarla, si se trata de un formulario de inicio de workflow.

FlowFlag: muestra la bandera del workflow.

FlowImportance: muestra la importancia del workflow.

FlowName: Este control muestra el nombre del workflow actual, o permite ingresarlo, si se trata de un formulario de inicio de workflow.

FlowProgress: muestra el porcentaje de avance del workflow (no confundir con el porcentaje de avance de una tarea; el porcentaje de avance del workflow se puede modificar en pasos de hito).

FlowStartDate: muestra fecha y hora de inicio del workflow.

FlowStarterUser: muestra el nombre del usuario que inició el workflow.

FlowStatus: muestra el estado actual del workflow.

Line: Este control define un bloque de líneas. Utilice la propiedad LineBlock para indicar cuál es el bloque de líneas que se desea mostrar en el formulario. Q-flow mostrará en el lugar del control todos los datos del bloque de líneas especificado. La propiedad ReadOnly permite especificar que los datos del bloque son solamente para lectura (no se pueden modificar), independientemente del alcance definido para ellos. En estos casos se supone que los datos serán modificados por medio de código. El control tiene, además, las siguientes propiedades, que permiten modificar la apariencia del control mediante estilos:

Role: representa un rol del template. Cuando Q-flow muestre el formulario en el sitio web, reemplazará este control por un control que permite elegir el usuario que desempeñará ese rol, o por un control que muestra qué usuario desempeña ese rol, dependiendo de si el rol es o no editable en el paso al que está asociado el formulario. El valor de la propiedad RoleName es el nombre del rol. Cuando Q-flow muestre el formulario en el sitio web, utilizará ese nombre para saber qué rol mostrar en el lugar donde está el control. La propiedad ReadOnly permite especificar que el rol es solamente para lectura (no se puede modificar) independientemente del alcance del rol. En estos casos se supone que el rol será modificado por medio de código.

Submit: Es un botón. Adquiere diferente significado según el tipo de formulario donde es usado:

TaskDescription: Este control muestra la descripción de una tarea. Sirve en los formularios de respuesta a tareas.

TaskName: Este control muestra el nombre de una tarea. Sirve en los formularios de respuesta a tareas.

TaskResponse: Muestra una lista con las opciones de respuesta posibles, y permite seleccionar una respuesta.

TaskSubject: Este control muestra el asunto de una tarea. Sirve en los formularios de respuesta a tareas.

TemplateDescription: Muestra la descripción del template al que pertenece el workflow al que está asociado el formulario.

TemplateName: Muestra el nombre del template al que pertenece el workflow al que está asociado el formulario.

TemplateVersionDescription: Muestra la descripción de la versión del template que está siendo utilizada.

TemplateVersionName: Este control muestra el nombre de la versión del template que está siendo utilizada.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

8

Validaciones del lado del cliente

Q-flow provee mecanismos para interactuar con sus controles de forma que se pueda realizar validaciones del lado del cliente. Los controles de Q-flow utilizan internamente controles más sencillos (como por ejemplo, una caja de texto), y permiten acceder a esos controles internos para interactuar con ellos y realizar validaciones sobre sus contenidos. Un desarrollador puede utilizar estos mecanismos en sus formularios personalizados.

Q-flow provee dos conjuntos de scripts para hacer validaciones del lado del cliente:

Scripts disponibles a través del objeto Host

Scripts clásicos de Q-flow

Los scripts disponibles a través del objeto Host tienen la ventaja de que son más fáciles de usar. Utilizan objetos tipados y vienen con documentación que permite utilizar la funcionalidad de autocompletar (IntelliSense) de Visual Studio. Sin embargo, algunas funciones, como las que permiten acceder a información del workflow (progreso del workflow, nombre del workflow, etc), sólo están disponibles en los scripts clásicos de Q-flow.

Scripts disponibles a través del objeto Host

Estos scripts están disponibles en los formularios personalizados de Q-flow, y exponen un objeto llamado Host que tiene funciones para manipular y acceder a datos de aplicación, roles, dominios y otros elementos del workflow. Antes de empezar a trabajar con estos scripts, conviene incluir su documentación en el formulario, para poder trabajar con la funcionalidad de autocompletar (IntelliSense) de Visual Studio. A continuación se explica cómo hacer esto.

Inc lus ión de documentac ión para In te l l iSense

Para facilitar la utilización de los scripts, Q-flow provee un archivo de documentación que describe las funciones de esos scripts y que permite que Visual Studio utilice su IntelliSense (funcionalidad de autocompletar) para ayudar a los desarrolladores a programar con ellos. Para incluir esa documentación, haga lo siguiente:

1. Abra el formulario en modo código. 2. Encuentre los scripts de Q-flow en explorador de soluciones de Visual Studio. El archivo está

dentro de la carpeta Scripts en el sitio de Q-flow, y se llama ScriptHost-vsdoc.js. 3. Seleccione el archivo de los scripts y arrástrelo al formulario, preferentemente dentro del

elemento <head>. El resultado debería ser el código que se muestra abajo. También puede copiar el tercer renglón dentro del elemento <head>.

<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <script src="../Scripts/ScriptHost-vsdoc.js" type="text/javascript"></script>

Una vez incluido el script, podrá escribir, por ejemplo “Host.”, y Visual Studio le mostrará las funciones que puede llamar. Si selecciona uno, le mostrará la descripción de esa función.

Funciones de l obje to Host y de obje tos auxi l iares

Host

El objeto Host tiene las siguientes funciones:

GetData(String nombre):Data: devuelve un objeto que representa un dato de aplicación.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

9

GetDataByElementId(String elementId):Data: devuelve un objeto que representa el dato de aplicación al que pertenece el elemento cuyo identificador se pasó por parámetro. Si no encuentra un dato de aplicación que cumpla con esa condición, devuelve el valor nulo. Por ejemplo, si se le pasa el identificador de una caja de texto (control “input” de tipo texto) que se usa para mostrar el valor del dato “Nombre del cliente”, devuelve un objeto que representa ese dato.

GetDataSourceItemDescription(String key, String domainId, Object parameters, Function onsuccess, Function onerror):String: dado un dominio asociado a un origen de datos y especifiado mediante su identificador en el parámetro domainId, devuelve el valor (también llamado “descripción”) correspondiente a la clave indicada por el parámetro key. Si el dominio tiene parámetros, éstos se pasan a la función mediante el parámetro parameters. Este método es asincrónico. El código que lo invoca sigue su ejecución sin esperarlo. Los dos últimos parámetros permiten capturar los resultados. El primero es el nombre de la función que se llamará si el método se ejecuta correctamente. El segundo es el nombre de la función que se llamará en caso de que se produzca un error. La función que se especifique para ser llamada en caso de que el método se ejecute correctamente debe tener un parámetro. Cuando esa función sea invocada, ese parámetro contendrá el valor (de tipo String) devuelto por el método. Vea más adelante el ejemplo de uso de esta función, en esta misma sección.

GetDomain(Guid domainId):Domain: devuelve un objeto que representa un dominio.

GetInstanceByElementId(String elementId): devuelve el índice del valor de un dato de aplicación multivaluado o de bloque de línea asociado al elemento cuyo identificador se provee mediante el parámetro elementId. Por ejemplo, si se le pasa el identificador del elemento que muestra el quinto elemento de un dato multivaluado, devuelve “4” (porque los índices empiezan en 0).

GetLine(String nombre):Line: devuelve un objeto que representa el bloque de líneas cuyo nombre se pasó por parámetro.

GetRole(String nombre):Role: devuelve un objeto que representa el rol cuyo nombre se pasó por parámetro.

GetSecurityMember(String memberId, Function onsuccess, Function onerror): método asincrónico que trae el miembro de seguridad (rol, grupo, nodo, etc) cuyo identificador coincida con el valor del parámetro memberId. Las funciones onsuccess y onerror son invocadas cuando termina la ejecución de la función en caso de éxito o de error respectivamente. La primera debe recibir un parámetro. Cuando esa función se ejecute, ese parámetro contendrá un objeto con las propiedades Id, Name, y MemberType, todas de tipo String, que contendrán el identificador, el nombre y el nombre del tipo de miembro de seguridad respectivamente.

Ejemplo de función asincrónica: GetDataSourceItemDescription

<script type="text/javascript"> function GotDescOk(description) { alert(description); //Muestra la descripción obtenida en una ventana de diálogo. } function GotDescErr(objError) { alert(objError._message); //Muestra el mensaje de error } window.onload = function () { Host.GetDataSourceItemDescription('1', 'd6cf9ec1-2d6a-4d47-8856-b74273e4a7cb', null, GotDescOk, GotDescErr);

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

10

} </script>

Este script supone que hay un dominio que obtiene datos, por ejemplo, de una base de datos. El identificador del dominio es “d6cf9ec1-2d6a-4d47-8856-b74273e4a7cb”. El script intenta obtener la descripción del registro cuya clave es “1”. En caso de que se pueda traer el dato, se ejecuta la función GotDescOk, que recibe por parámetro la descripción obtenida y la muestra en una ventana de diálogo. En caso de error, se ejecuta la función GetDescErr, que recibe por parámetro un objeto con datos del error. Nótese que como el dominio no tiene parámetros, la llamada pasa “null” en lugar de un objeto con parámetros.

Ejemplo: GetDataSourceItemDescription con un dominio que requiere parámetros

<script type="text/javascript"> function GotDescOk(description) { alert(description); //Muestra la descripción obtenida en una ventana de diálogo. } function GotDescErr(objError) { alert(objError._message); //Muestra el mensaje de serror } window.onload = function () { var p = []; p['NombreCargo'] = 'Programador'; Host.GetDataSourceItemDescription('1', 'a4053011-a86e-499f-865a-0a0893395cec', p, GotDescOk, GotDescErr); } </script>

Este script es similar al ejemplo anterior, pero utiliza un dominio que requiere un parámetro. El parámetro se llama “NombreCargo”, y se le asigna el valor “Programador” para que sea utilizado en la consulta del dominio.

Ejemplo: manejo del evento “instanceAdded” <script type="text/javascript"> function handleInstanceAdded() { alert('Instance added'); } window.onload = function () { var dato = Host.GetData("Funciones"); dato.instanceAdded = handleInstanceAdded; dato.AddInstance(); } </script>

Data

Los objetos de tipo Data representan datos de aplicación.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

11

Los objetos de tipo Data las siguientes funciones:

AddInstance(): crea un nuevo valor en un dato de aplicación multivaluado. Este método es asincrónico. Siguientes modificaciones al dato se deben realizar en la función que maneje el evento “instanceAdded”.

GetDescription(índice):String devuelve la descripción correspondiente al índice especificado por parámetro. Si el dato no define una descripción, devuelve la clave convertida a String utilizando la configuración de cultura del usuario. La descripción es texto correspondiente al dato, tal como se le muestra al usuario en el sitio web.

GetInstanceCount():Number: devuelve la cantidad de valores que tiene el dato.

GetValue(índice): devuelve el valor del dato. Tiene el mismo tipo que el dato. Si el dato es de tipo fecha, devuelve una fecha (objeto Date), por ejemplo. En el caso de datos pertenecientes a dominios asociados a orígenes de datos, el valor es la clave.

RemoveInstance(índice): borra el valor en el índice especificado. Este método es asincrónico. Siguientes modificaciones al dato se deben realizar en la función que maneje el evento “instanceRemoved”.

SetValue(valor, índice): asigna el valor indicado a la instancia indicada por el índice.

Los objetos de tipo Data tienen los siguientes atributos:

domain: objeto de tipo Domain que representa el dominio al que pertenece el dato.

groupName: nombre del grupo al que pertenece el dato. Si el dato no pertenece a ningún grupo, este atributo es el texto vacío.

id: identificador del dato.

instanceAdded: función que se llama cuando se agrega un valor al dato.

instanceRemoved: función que se llama cuando se elimina un valor del dato.

isArray: indica si el dato es multivaluado.

line: nombre del bloque de líneas al que pertenece el dato. Si el dato no pertenece a ningún bloque de líneas, este atributo es el texto vacío.

name: nombre del dato.

scope: alcance del dato. Es un texto con uno de los siguientes valores: “Required” (requerido), “Editable”, “ReadOnly” (sólo lectura), “Hidden” (oculto).

Domain

Los objetos de tipo Domain representan dominios. No tienen métodos.

Tienen los siguientes atributos:

id: identificador del dominio.

controlType: tipo del control que usa el dominio (por ejemplo, “Lookup”).

dataType: nombre del tipo de dato que usa el dominio (“Decimal”, “Boolean”, “String” o “DateTime”).

decimalCount: número de dígitos decimales que utiliza el dominio. Sólo válido para dominios de tipo Decimal.

valueChangedHandler: nombre de la función que será invocada cuando cambie el valor de un dato del dominio. Válido para dominios que usan controles de tipo selector (selector de ítems, lookup).

Line

Los objetos de tipo Line representan bloques de líneas.

Tienen los siguientes métodos:

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

12

AddInstance(): agrega una línea. Es asincrónico, como el método homónimo de los objetos Data. Para manipular los valores de un dato que pertenece a un bloque de líneas, utilice el objeto Data.

GetInstanceCount():Number devuelve la cantidad de líneas del bloque.

RemoveInstance(): elimina una línea. Es asincrónico, como el método homónimo de los objetos Data.

Los objetos de tipo Line tienen los siguientes atributos:

name: nombre del bloque.

controlId: identificador del control que muestra el bloque en el formulario.

instanceAdded: función que se llama cuando se agrega una línea al bloque.

instanceRemoved: función que se llama cuando se elimina una línea del bloque.

Role

Los objetos de tipo Role representan roles. Tienen los siguientes métodos:

AddInstance(): agrega un miembro vacío al rol. Este método es asincrónico. Siguientes modificaciones al rol se deben realizar en la función que maneje el evento “instanceAdded”.

GetInstanceCount():Number devuelve la cantidad de miembros que tiene el rol.

GetMemberId(índice):String: devuelve el identificador del miembro que está desempeñando el rol en el índice especificado.

GetMemberName(índice):String: devuelve el nombre del miembro que está desempeñando el rol en el índice especificado.

GetMemberType(índice):String: devuelve el nombre del tipo del miembro que está desempeñando el rol (el tipo indica si es un usuario, un grupo, una cola de trabajo) en el índice especificado.

RemoveInstance(índice): quita del rol el miembro correspondiente al índice especificado. Este método es asincrónico. Siguientes modificaciones al rol se deben realizar en la función que maneje el evento “instanceRemoved”.

Los objetos de tipo rol tienen los siguientes atributos:

id: identificador del rol.

name: nombre del rol.

instanceAdded: función que se llama cuando se agrega un miembro al rol.

instanceRemoved: función que se llama cuando se quita un miembro del rol.

Scripts clásicos de Q-flow

Las siguientes son otras funciones que permiten trabajar con datos de los workflows. Algunas tienen las mismas funcionalidades que funciones del objeto Host. En esos casos, se recomienda utilizar las funciones del objeto Host, que es más fácil de utilizar porque maneja objetos tipados y tiene documentación que se puede ver en el propio Visual Studio.

GetDataCount(dataName): obtiene la cantidad de líneas que tiene el dato cuyo nombre coincide con el valor del parámetro dataName.

GetDataElement(dataName, instance): obtiene el elemento HTML que representa el dato de aplicación cuyo nombre es igual al valor del parámetro dataName. Si el dato acepta valores múltiples, el parámetro instance indica qué línea obtener (0 corresponde a la primera línea). De lo contrario, es 0.

GetFlowDescriptionElement(): obtiene el elemento HTML que contiene la descripción del workflow.

GetFlowFlagElement(): obtiene el elemento HTML que contiene la bandera del workflow.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

13

GetFlowImportanceElement(): obtiene el element HTML que contiene la importancia del workflow.

GetFlowNameElement(): obtiene el elemento HTML que contiene el nombre del workflow.

GetFlowProgressElement(): obtiene el elemento HTML que contiene el progreso del workflow.

GetFlowStartDateElement(): obtiene el elemento HTML que contiene la fecha de inicio del workflow.

GetFlowStarterUserElement(): obtiene el elemento HTML que contiene el nombre del usuario que inició el workflow.

GetFlowStatusElement(): obtiene el elemento HTML que contiene el estado actual del workflow.

GetLineCount(lineBlock): obtiene la cantidad de líneas que tiene el bloque de líneas indicado por el parámetro lineBlock.

GetLineDataElement(lineBlock, dataName, instance): obtiene elemento HTML que representa el dato de nombre dataName del bloque de líneas lineBlock. El parámetro instance indica qué valor se debe traer (0 si es el primer valor o si el dato no admite valores múltiples).

GetRoleCount(roleName): obtiene la cantidad de líneas que tiene el rol cuyo nombre coincide con el valor del parámetro roleName.

GetRoleElement(roleName, instance): obtiene el elemento HTML que representa el rol cuyo nombre coincide con el valor del parámetro roleName. Si el rol acepta valores múltiples, el parámetro instance indica cuál de los valores obtener (0 corresponde al primer valor). De lo contrario, es 0.

GetSubmitElement(): obtiene el elemento HTML correspondiente al botón que permite contestar la tarea.

GetTaskDescriptionElement(): obtiene el elemento HTML que contiene la descripción de la tarea.

GetTaskNameElement(): obtiene el elemento HTML que contiene el nombre de la tarea.

GetTaskProgressElement(): obtiene el elemento HTML que contiene el porcentaje de avance de la tarea.

GetTaskResponseElement(): obtiene el elemento HTML que contiene la respuesta a la tarea.

GetTaskSubjectElement(): obtiene el elemento HTML que contiene el asunto de la tarea.

En algunos casos, es posible modificar los valores de esos elementos. Por ejemplo, si el template está configurado para que un determinado dato pueda ser modificado en el paso al que corresponde el formulario, entonces es posible modificar el valor de ese dato. En otras ocasiones, no es posible modificar el valor del dato. Por ejemplo, una vez iniciado un workflow, no es posible cambiar su nombre.

Ejemplo: Conteo de caracteres ingresados

Supongamos que un template tiene un dato de aplicación llamado “Comments” (comentarios), de tipo area de texto. Se desea que, a medida que el usuario ingresa el comentario, se indique el largo del texto ingresado hasta el momento. Para eso, basta con pegar el siguiente script en el formulario personalizado de ese paso:

<script type="text/javascript">

function formLoad() {

GetDataElement("Comments").onkeyup = updateCharacterCount;

}

function updateCharacterCount() {

var commentsElement = GetDataElement("Comments");

var characterCountElement = GetDataElement("CharacterCount");

var characterCount = commentsElement.value.length;

characterCountElement.value = characterCount;

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

14

}

</script>

La función “formLoad” es invocada automáticamente por Q-flow cuando el formulario termina de cargarse. Esto permite tener un lugar donde inicializar variables o asignar eventos como en este caso. Utilizamos el evento “onkeyup” del area de texto para recalcular el largo del texto ingresado usando la función “updateCharactersCount”. Esta función escribe en el dato “CharacterCount” el largo del texto ingresado como comentario.

Ejemplo: Cargar automáticamente el nombre del workflow

El siguiente script puede ser utilizado en un formulario personalizado de inicio para asignarle un texto al nombre del workflow. El ejemplo utiliza un dato de aplicación llamado “Invoice”, que contiene el número de orden de compra, el cual se incluye en el nombre del flow autogenerado.

<script type="text/javascript">

function loadFlowName() {

var automaticFlowName;

var flowNameElement;

var invoiceElement;

invoiceElement = GetDataElement("Invoice");

automaticFlowName = "Invoice nº " + invoiceElement.value;

flowNameElement = GetFlowNameElement();

flowNameElement.value = automaticFlowName;

}

</script>

Ejemplo: Roles

Un proceso de elaboración de informes tiene dos roles: Writers (readactores) y Reviewers (revisores). Ambos admiten múltiples valores. Ningún redactor puede estar entre los revisores. El formulario de inicio de un workflow basado en ese proceso debe impedir iniciar un workflow si algún redactor es también un revisor. El siguiente script valida que ello no ocurra:

<script type="text/javascript">

function validateWriters() {

var writersCount;

var roleElement;

writersCount = GetRoleCount("Writers");

for (var i = 0; i < writersCount; i++) {

roleElement = GetRoleElement("Writers", i);

if (isReviewer(roleElement)) {

alert("ERROR: Writer " + roleElement.nextSibling.value + "

is also a reviewer.");

return false;

}

}

return true;

}

function isReviewer(roleElement) {

var reviewersCount;

reviewersCount = GetRoleCount("Reviewers")

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

15

for (var i = 0; i < reviewersCount; i++) {

if (roleElement.value == GetRoleElement("Reviewers", i).value) {

return true;

}

}

return false;

}

</script>

La función “validateWriters” debe ejecutarse cuando el usuario hace clic en el botón “Iniciar”, que es el que hace que el formulario sea enviado al servidor. Esto puede conseguirse utilizando el control CustomValidator de ASP.NET.

El script recorre los miembros del rol “Writers” y verifica para cada uno de ellos si es también un miembro del rol “Reviewers”. Si encuentra alguno en esas condiciones, muestra un mensaje de error y devuelve falso. El mensaje de error muestra el nombre del usuario que es miembro de ambos roles.

Nótese que para obtener el nombre de un miembro de un rol se utiliza la expresión “roleElement.nextSibling.value”. Sin embargo, la función “EsRevisor” utiliza la expresión “roleElement.value” para comparar dos usuarios. Esto es porque roleElement.value contiene el identificador del usuario, mientras que roleElement.nextSibling.value contiene el nombre del usuario, que es lo que se muestra en el formulario.

Validaciones del lado del servidor

También es posible acceder a los controles de Q-flow desde el code behind del formulario. En particular los controles “Data”, “Line” y “Role” proveen funciones y propiedades que permiten leer y modificar sus valores.

Data o AddValue(string value): Agrega un nuevo valor (equivalente al del parámetro) al dato

(si admite muchos valores) o string GetValue(int index): Devuelve el valor de la instancia en la posición index. o int GetValueCount(): Devuelve cantidad de valores del dato. o RemoveValue(int index): Elimina el valor de la posición index. o SetValue(int index, string value): Asigna el valor indicado por el parámetro “value” a

la posición indicada por el parámetro “index”. o string Value: Propiedad que devuelve o asigna el primer valor del dato de aplicación o InstanceAdded: evento que se dispara cuando se agrega un valor al dato. o InstanceRemoved: evento que se dispara cuando se quita un valor del dato.

Line o AddLine(): Agrega una nueva línea vacía. o Propiedades de estilo para las tablas que muestran el bloque de líneas:

CellPadding CellSpacing CssAddInstance: estilo del botón que agrega una línea. CssAlternatingRow: estilo de las filas pares. CssHeader: estilo del cabezal. CssRemoveInstance: estilo del botón que elimina una línea. CssRow: estilo e las filas impares. CssTable: estilo de tabla.

o int GetLineCount(): Devuleve la cantidad de instancias de la línea.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

16

o string GetValue(string dataName, int lineNumber): Devuelve la instancia en la posición lineNumber del dato con nombre dataName de la linea.

o RemoveLine(int lineNumber): Remueve la instancia en la posición lineNumber de la línea.

o SetValue(string dataName, int lineNumber, string value): Asigna a la instancia lineNumber del dato dataName de la linea el valor value.

o InstanceAdded: evento que se dispara cuando se agrega una línea al bloque. o InstanceRemoved: evento que se dispara cuando se quita una línea del bloque.

Role o AddMember(Guid id, string name, TemplateRoleMemberType memberType):

Agrega un nuevo miembro al rol. El miembro se especifica mediante su identificador (“id”), nombre (“name”) y tipo (“memberType”). Asegúrese de que el “memberType” corresponda con el identificador especificado, de lo contrario, se producirá un error al enviar la información del formulario.

o GetMemberCount(): Devuelve la cantidad de miembros que tiene el rol. o GetMemberId(int index): Devuelve el identificador del miembro que está en la posición

“index” del rol. o GetMemberName(int index): Devuelve el nombre del miembro que está en la posición

“index” del rol. o GetMemberType(int index): Devuelve el tipo del miembro que está en la posición

“index” del rol. o InstanceAdded: evento que se dispara cuando se agrega un miembro al rol. o InstanceRemoved: evento que se dispara cuando se quita un miembro del rol.

FlowInteraction

También es posible interactuar con los controles a través de la propiedad FlowInteraction. Salvo para los controles mencionados anteriormente, esta es la única forma de interactuar con los controles de Q-flow. Las propiedades y métodos más destacados de FlowInteraction son los siguientes:

Attachments: Objeto, de la clase Attachments, que contiene los adjuntos accesibles desde el formulario.

Data: Colección de datos de aplicación accesibles desde el formulario.

DataLines: Colección de líneas accesibles desde el formulario.

Roles: Colección de roles accesibles desde el formulario.

SimpleData GetSimpleData(string dataName): Devuelve un dato dado su nombre.

DataLine GetDataLine(string lineBlock): Devuelve una línea dado su nombre.

FlowRole GetRole(string roleName): Devuelve un rol dado su nombre.

TemplateName: Nombre del template del flow.

TemplateDescription: Descripción del template del flow.

TemplateVersionName: Nombre de la versión del flow.

TemplateVersionDescription: Descripción de la versión del flow.

FlowName: Nombre del flow.

FlowDescription: Descripción del flow.

FlowImportance: Prioridad del flow.

FlowProgress: Progreso del flow.

FlowFlag: Bandera del flow.

FlowStatus: Estado del flow.

FlowStartDate: Fecha de inicio del flow.

FlowStarterUser: Usuario iniciador del flow.

TaskName: Nombre de la tarea.

TaskDescription: Descripción de la tarea.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

17

TaskSubject: Asunto de la tarea.

TaskResponse: Respuesta a la tarea.

TaskProgress: Progreso de la tarea.

Submit: Item de inicio de flow o respuesta de tarea.

Los métodos y propiedades mencionados trabajan con los llamados “ítems de interacción”, que son objetos de clases que encapsulan el comportamiento de los diferentes elementos del formulario, y se encargan de construir los controles ASP.NET que finalmente son desplegados en la página. A continuación se describen las clases que se utilizan, con sus métodos y propiedades más importantes.

Attachments

Un objeto de la clase Attachments representa la colección de archivos adjuntos de un workflow. El objeto FlowInteraction.Attachments es de la clase Attachments. Expone un conjunto de eventos (los mismos que se describen en “Eventos de adjuntos”). A continuación se describen los métodos y propiedades más importantes:

AddAttachment(string name, byte[] content): agrega un adjunto con el nombre especificado y el contenido especificado en el segundo parámetro.

CheckInAttachment(string name): protege (hace check-in de) un adjunto.

CheckOutAttachment(string name): desprotege (hace check-out de) un adjunto.

DeleteAttachment(string name): elimina un adjunto.

ModifyAttachment(string name, byte[] content): modifica el adjunto del nombre especificado, sustituyendo su contenido con el que es especificado en el segundo parámetro.

UndoCheckOutAttachment(string name): deshace la desprotección (el check-out) de un adjunto.

DataL ine

Los objetos de la clase DataLine representan bloques de línea. La colección FlowInteraction.DataLines es un diccionario que contiene objetos de este tipo (la clave cada uno es el nombre del bloque).

Data: colección de datos de aplicación pertenecientes a un bloque de línea.Scope: indica si el alcance permite agregar y borrar líneas.

LineBlock: nombre del bloque de líneas.

LineCount: cantidad de líneas del bloque de líneas.

Values: lista de valores del dato (sólo disponible para datos de aplicación y bloques de línea).

Flow Role

La colección FlowInteraction.Roles contiene objetos de esta clase. Las siguientes son sus propiedades más importantes, junto a un par de eventos:

Id: identificador del rol.

IsArray: indica si el rol admite múltiples valores.

InstanceAdded: evento que se dispara cuando se agrega un nuevo miembro al rol.

InstanceRemoved: evento que se dispara cuando se quita un miembro del rol.

Scope: alcance del rol (devuelve un valor del enumerado ItemScope).

Name: nombre del rol.

Members: lista de miembros de un rol.

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

18

SimpleData

Los objetos de la clase SimpleData representan datos de aplicación. La colección FlowInteraction.Data contiene objetos de esta clase. Las siguientes son sus propiedades más importantes y un par de eventos:

DomainId: identificador del dominio del dato.

Id: identificador del dato.

InstanceAdded: evento que se dispara cuando se agrega un valor al dato.

InstanceRemoved: eventos que se disparan cuando se elimina un valor del dato.

IsArray: indica si el dato admite múltiples valores.

Scope: alcance del dato (devuelve un valor del enumerado ItemScope).

Name: nombre del dato.

Type: tipo del dato de aplicación (es un tipo de .Net).

Value: Texto con el valor de un dato.

Values: lista de valores del dato.

Eventos de adjuntos

El control Attachments expone un conjunto de eventos relacionados con los archivos adjuntos:

AttachmentAdded: se dispara una vez que un usuario agregó un archivo adjunto.

AttachmentModified: se dispara una vez que un usuario guardó cambios en archivo adjunto.

AttachmentDeleted: se dispara una vez que un usuario eliminó un archivo adjunto.

AttachmentCheckedIn: se dispara después que el usuario hizo “check in” (proteger) de un archivo adjunto.

AttachmentCheckedOut: se dispara después que el usuario hizo “check out” (desproteger) de un archivo adjunto.

AttachmentCheckOutUndone: se dispara después que el usuario dehizo una operación de check-out (deshacer desprotección).

Ejemplo de manejo de l avento At tachmentAdded

protected void AttachmentAdded(object sender, AttachmentEventArgs e) { Page.ClientScript.RegisterStartupScript(typeof(CustomForms_TaskResponseCustom), "AttachmentAdded", "alert('Adjunto agregado: " + e.Name + "');", true); }

Dos métodos para hacer validaciones

Para realizar validaciones del lado del servidor previo al inicio del flow o respuesta de la tarea existen dos alternativas.

La primera es usar un control CustomValidator de ASP.NET, cuyo uso para rutinas de lado del cliente se explica en “Validaciones del lado del cliente”. También es posible utilizar dicho control para realizar validaciones del lado del servidor. Para ello es necesario asignarle con el atributo “OnServerValidate” una rutina de validación en el lado del servidor.

La segunda alternativa es sobreescribir el método de la clase base que procesa el inicio del flow o respuesta de la tarea. Según el tipo de formulario el método se llama de manera diferente:

Clase base Método

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

19

StartFlowBase OnStartFlow

TaskResponseBase OnSubmitResponse

Si se decide utilizar esta alternativa, es importante recordar llamar al método de la clase base si se desea definitivamente procesar el flow o la tarea.

Ejemplo: Calcular total a partir de subtotales

En un formulario de ingreso de orden de compra hay una línea con la información de los productos solicitados. Dentro de la línea, uno de los datos es el subtotal (dato “SubTotal”) que indica el costo de una instancia de la línea. Se desea poder calcular el total de la compra en el mismo formulario y desplegarlo en el dato “Total”. Esto se puede conseguir utilizando un botón para calcular el total accediendo a los controles del lado del servidor, como lo muestra el siguiente ejemplo:

Markup

<qflow:Line runat="server" ID="linOrder" LineBlock="Order" />

<qflow:Data runat="server" ID="datTotal" DataName="Total" />

<asp:Button runat="server" ID="btnCalculateTotal" Text="Calculate total"

OnClick="btnCalculateTotal_Click" />

Code behind

protected void btnCalculateTotal_Click(object sender, EventArgs e)

{

SimpleData datSubTotal =

FlowInteraction.GetDataLine("Order").GetSimpleData("SubTotal");

decimal total = 0;

foreach (var item in datSubTotal.Values)

{

decimal subTotal;

if (decimal.TryParse(item.Value, NumberStyles.Any,

CultureInfo.InvariantCulture, out subTotal))

{

total += subTotal;

}

}

datTotal.Value = total.ToString(CultureInfo.InvariantCulture);

}

En el manejador del click del botón, primero se obtiene el dato “SubTotal”. Como este dato se encuentra dentro de la línea “Order”, es necesario pedir el dato a la línea en lugar de pedirlo directamente a “FlowInteraction”.

Luego se recorren y suman los subtotales para determinar el total. Notar que los valores de los datos son repesentados como strings en cultura invariante, por lo que al momento de convertir de string a decimal y viceversa es necesario especificar que se desea utilizar dicha cultura (pasando como parámetro “CultureInfo.InvariantCulture”).

Finalmente el total calculado es asignado al dato “Total”, al cual se accede directamente utilizando el nombre del control definido en el markup.

Ejemplo: Validación de fechas

Este ejemplo valida que un par de datos de tipo fecha definan un rango de tiempo válido. Para ellos se comparan los valores de los datos “StartDate” (fecha de inicio) y “EndDate” (fecha de fin) para comprobar que fecha de inicio es menor o igual que fecha de fin. Esto se hace de la siguiente manera:

Q f 3 4 0 0 1 4 E S P D i s e ñ o d e f o r m u l a r i o s p e r s o n a l i z a d o s v 1 . 0

20

protected override void OnStartFlow(object sender, EventArgs e)

{

DateTime startDate =

DateTime.Parse(Interaction.GetSimpleData("StartDate").Value,

CultureInfo.InvariantCulture);

DateTime endDate =

DateTime.Parse(Interaction.GetSimpleData("EndDate").Value,

CultureInfo.InvariantCulture);

if (startDate.CompareTo(endDate) > 0)

Page.ClientScript.RegisterStartupScript(

this.GetType(),

"DateRangeValidation",

"alert('The entered date range is not valid')", true);

else

{

ConfirmationPage = "~/CustomForms/MyCustomConfirmationPage.aspx";

base.OnStartFlow(sender, e);

}

}

Para realizar la validación se sobreescribe el método “OnStartFlow”, heredado de la clase base “StartFlowBase”. Al igual que en el ejemplo anterior, los valores de los datos se convierten utilizando la cultura invariante, que es la que utiliza Q-flow para almacenarlos.

En caso de que el rango ingresado no sea válido, se registra una rutina javascript para desplegar una alerta y se omite la llamada al método “OnStartFlow” de la clase base. De esta manera el flow no es iniciado y el usuario puede observar el mensaje de error en el mismo formulario.

Si el rango es válido se procede a iniciar el flow llamando al método “OnStartFlow” de la clase base. Previamente se asigna una página de confirmación personalizada utilizando la propiedad “ConfirmationPage”. De esa manera, luego de iniciado el flow el usuario será redirigido a dicha página en lugar de la que utiliza Q-flow normalmente.