0% encontró este documento útil (0 votos)
68 vistas68 páginas

Manual

Este documento introduce los conceptos básicos de Windows Workflow Foundation 4.0. Explica la arquitectura de WF 4.0 y los tipos de actividades, flujos de trabajo y cómo probarlos. También cubre temas como lógica if/else, manejo de errores, actividades personalizadas y diseñadores alojados.

Cargado por

Javier
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
68 vistas68 páginas

Manual

Este documento introduce los conceptos básicos de Windows Workflow Foundation 4.0. Explica la arquitectura de WF 4.0 y los tipos de actividades, flujos de trabajo y cómo probarlos. También cubre temas como lógica if/else, manejo de errores, actividades personalizadas y diseñadores alojados.

Cargado por

Javier
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 68

Windows Workflow

Foundation 4.0
Visual Studio 2010

En este manual se pretende introducir al lector en los


conceptos de Windows Workflow para poder mejorar y
optimizar al máximo las aplicaciones, realizando un
desarrollo fácil y dirigido a través del modelado de
procesos de negocio.

Alhambra-eidos
08/02/2011
Windows Workflow Foundation 4.0 2011

TABLA DE CONTENIDOS
INTRODUCCIÓN ............................................................................................................................. 4
CONCEPTOS PREVIOS ................................................................................................................ 6
ARQUITECTURA DE WINDOWS WORKFLOW 4.0 ...................................................................... 7
ACTIVIDADES ................................................................................................................................. 9
Ejemplo- Hola Mundo ............................................................................................................. 10
Actualizar el método Main para usar el nuevo nombre ..................................................... 13
La Actividad CodeActivity ........................................................................................................ 14
FLUJOS DINÁMICOS CON XAML .................................................................................................. 16
PROBAR LOS FLUJOS DE TRABAJO .............................................................................................. 19
Crear una prueba unitaria ....................................................................................................... 19
WORKFLOWAPPLICATION ........................................................................................................... 24
Devolver el Flujodetrabajo como un argumento .................................................................... 25
Modificar la prueba para utilizar WorkflowApplication.......................................................... 28
Delegados, no eventos ........................................................................................................ 29
Verificar el resultado ........................................................................................................... 29
Lógica If/Else ............................................................................................................................... 31
Manejo de Errores....................................................................................................................... 35
Añadir la actividad try/Catch al flujo de trabajo ..................................................................... 35
ACTIVIDADES PERSONALIZADAS ................................................................................................. 39
Crear una actividad básica ...................................................................................................... 39
Propiedades de dependencia .................................................................................................. 40
Creación de actividades compuestas ...................................................................................... 43
Actividades asíncronas y basadas en eventos......................................................................... 46
Control de errores ................................................................................................................... 48
Experiencia de diseño ............................................................................................................. 49
Validación ................................................................................................................................ 52
DISEÑADORES ALOJADOS............................................................................................................ 54
EJEMPLOS .................................................................................................................................... 58
Para crear la actividad ReadInt ............................................................................................... 58
Para crear la actividad Prompt ................................................................................................ 59
Para crear el proyecto de flujo de trabajo .............................................................................. 60

2
Windows Workflow Foundation 4.0 2011

Para crear las variables y argumentos de flujo de trabajo .......................................................... 61


Para agregar actividades de flujo de trabajo .......................................................................... 61
Para ejecutar el proyecto ........................................................................................................ 63
Para crear un diseñador de actividad personalizado con un área de colocación mediante
WorkflowItemPresenter ......................................................................................................... 65

3
Windows Workflow Foundation 4.0 2011

INTRODUCCIÓN
Un flujo de trabajo es un conjunto de unidades elementales llamadas actividades que están
almacenadas como un modelo que describe un proceso real. Los flujos de trabajo proporcionan
una manera de describir el orden de ejecución y las relaciones de dependencia entre las partes de
trabajo de ejecución tanto corta o prolongada. Este trabajo pasa a través del modelo desde un
principio hasta un final y las actividades pueden ser ejecutadas por personas o por funciones de
sistema.

Las instancias de flujo de trabajo en ejecución se crean y se mantienen por medio de un motor
de tiempo de ejecución con el que el proceso de host interactúa a través de alguna de las
siguientes clases:

Una clase WorkflowInvoker, se encarga de invocar el flujo de trabajo como si se tratara


de un método.

Una clase WorkflowApplication se encarga del control explícito de la ejecución de una


única instancia de flujo de trabajo.

Una clase WorkflowServiceHost se encarga de las interacciones basadas en mensajes en


escenarios en la que hay varias instancias.

Cada una de estas clases ajusta el tiempo de ejecución de la


actividad principal, representado como una clase
ActivityInstance, la cuál es la responsable de la ejecución de la
actividad. Destacar que puede haber varios objetos
ActivityInstance ejecutándose simultáneamente dentro de un
dominio de aplicación.

Los tres objetos de interacción de host anteriores se crean a partir


de un árbol de actividades al que se hace referencia como
programa de flujo de trabajo. Con estos tipos o con un host
personalizado que ajuste la clase ActivityInstance, los flujos de
trabajo se pueden ejecutar dentro de cualquier proceso de
Windows, incluidos las aplicaciones de consola, las aplicaciones
basadas en formularios, los Servicios de Windows, los sitios web
de ASP.NET y los servicios de Windows Communication
Foundation (WCF).

Como se observa en la imagen, se necita un proceso que aloje el


flujo de trabajo, esto es un aspecto fundamental a tener en cuenta.
Dentro del flujo de trabajo tendremos diversas actividades que
cada una de ellas se encargará de realizar alguna lógica concreta.

4
Windows Workflow Foundation 4.0 2011

Antes de seguir, es importante conocer todos los componentes involucrados en un flujo de


trabajo y cómo interactúan entre ellos. Para verlo de una forma más clara, la siguiente imagen
muestra un ejemplo que comentaremos a continuación.

4
2

1. Siguiendo la imagen anterior vemos un método Invoke, este pertenece a la clase


WorkflowInvoker se usa para invocar varias instancias de flujo de trabajo.
WorkflowInvoker se usa para los flujos de trabajo ligeros que no necesitan la
administración del host. Los flujos de trabajo que necesitan la administración del host se
deben ejecutar usando el método Run.

2. Antes de invocar una nueva instancia de flujo de trabajo no es necesario esperar a que se
complete la instancia de flujo de trabajo en ejecución. El motor de tiempo de ejecución
permite ejecutar varias instancias de flujo de trabajo simultáneamente. Los flujos de trabajo
invocados son los descritos a continuación:

Una actividad Sequence que contiene una actividad secundaria WriteLine. Las
actividades de tipo Sequence permiten añadir dentro diferentes actividades para
que estas se vean obligadas a ejecutarse de forma secuencial, en concreto, la
actividad WriteLine permite interactuar con la consola para escribir. Un objeto
Variable (más tarde lo veremos) de la actividad principal está vinculado a un
objeto InArgument de la actividad secundaria.

3. Una actividad personalizada llamada ReadLine, esta actividad permite leer de consola. Un
argumento OutArgument de la actividad ReadLine se devuelve al método Invoke de
llamada.

5
Windows Workflow Foundation 4.0 2011

Antes de continuar, repasemos, lo visto. Lo primero que necesitamos para iniciar un flujo de
trabajo es el método Invoke, en cuanto se inicia el flujo de trabajo, en este caso concreto una
actividad de tipo secuencia, que as u vez ésta compuesta por varias actividades que son el
escribir en la consola el valor de una variable. Pero resulta que podemos iniciar varios flujos de
trabajo, en este caso un segundo con una actividad de tipo ReadLine que permite leer de
consola.

4. Una actividad personalizada que se deriva de la clase abstracta CodeActivity. La clase


CodeActivity puede tener acceso a las características de tiempo de ejecución usando
CodeActivityContext, que está disponible como parámetro del método Execute.

CONCEPTOS PREVIOS

En la introducción, ya se ha descrito un caso real de invocación de flujos de trabajo, pero hemos


hablado de varios elementos como variables de flujo, argumentos, etc., que deberían ser
aclarados antes de continuar, para entender la máximo el funcionamiento e implementación de
los workflows.

Un flujo de trabajo es una colección estructurada de acciones que modela un proceso y


cada acción en el flujo de trabajo se modela como una actividad.

Un host interactúa con un flujo de trabajo usando WorkflowInvoker para invocar un


flujo de trabajo como si fuera un método, WorkflowInstance para el control explícito
sobre la ejecución de una instancia de flujo de trabajo, WorkflowServiceHost para las
interacciones basadas en mensajes en escenarios de varias instancias.

Dado que los pasos del flujo de trabajo se definen como una jerarquía de actividades, se
puede decir que la actividad de nivel superior en la jerarquía define el propio flujo de
trabajo.

Este modelo de jerarquía ocupa el lugar de las clases StateMachineWorkflow y


SequentialWorkflow explícitas de versiones anteriores.

Las actividades se desarrollan como colecciones de otras actividades o se crean de


manera personalizada usando CodeActivity o usando la clase NativeActivity, que
expone todo el tiempo de ejecución del flujo de trabajo a disposición dell autor de la
actividad.

Bien, teniendo esto en cuenta. ¿Cómo se comunican las actividades? ¿Cómo pueden compartir
información de cada actividad? ¿Cómo se almacenan las actividades? Las actividades se
almacenan y comparten datos entre ellas a través de los siguientes tipos:

Variable: Almacena los datos en una actividad

Argumento: Mueve los datos dentro y fuera de una de una actividad.

Expresión: Una actividad con un valor devuelto elevado usado en enlaces de


argumento.

6
Windows Workflow Foundation 4.0 2011

ARQUITECTURA DE WINDOWS WORKFLOW 4.0

Windows Workflow Foundation (WF) genera el nivel de abstracción para desarrollar


aplicaciones interactivas de ejecución prolongada. Las unidades de trabajo se encapsulan como
actividades. Las actividades se ejecutan en un entorno que proporcione los medios para el
control de flujo, el control de excepciones, la propagación de errores, la persistencia de los datos
de estado, la carga y descarga de flujos de trabajo en progreso de la memoria, el seguimiento y
el flujo de la transacción.

Si anteriormente ha podido trabajar con la versión anterior de Windows Workflow Foundation,


sabrá que los flujos de trabajo se componían de varias actividades, pero ahora un flujo de
trabajo consta de una única actividad que a su vez puede contener en su interior otras
actividades, y con ellos se conforma el flujo. Por ello antes de proseguir veamos cuál es la
arquitectura de una actividad.

Las actividades se desarrollan como tipos CLR que derivan de Activity, CodeActivity o
NativeActivity, o sus variantes que devuelven un valor, Activity, CodeActivity o
NativeActivity. Desarrollar actividades que derivan de Activity le permite al usuario ensamblar
objetos WorkflowElement existentes previamente para crear las unidades de trabajo que se
ejecutan en el entorno del flujo de trabajo.

CodeActivity, por otro lado, permite crear la lógica de ejecución en código administrado
usando principalmente CodeActivityContext para el acceso a los argumentos de actividad.
Desarrollar actividades que derivan de NativeActivity permite a los usuarios tener acceso al
tiempo de ejecución a través de ActivityExecutionContext para funciones como la
programación de elementos secundarios, la creación de marcadores, invocar el trabajo
asincrónico, el registro de transacciones y mucho más.

La creación de actividades que derivan de Activity son declarativas. Estas actividades se pueden
crear en XAML.

ActivityExecutionContext es la interfaz del autor de la actividad en el tiempo de ejecución del


flujo de trabajo y proporciona acceso a la amplia variedad de características del tiempo de
ejecución. En el siguiente ejemplo, se define una actividad que usa el contexto de ejecución para
crear un marcador.

La instancia de una actividad empieza en el estado En ejecución. A menos que se encuentren


excepciones, permanece en este estado hasta que todas las actividades secundarias se hayan
terminado de ejecutar y se hayan completado otros trabajos pendientes.

Una vez llegado este punto, cambia al estado Cerrado. El elemento primario de una instancia de
actividad puede pedir a un elemento secundario que cancele; si el elemento secundario puede
cancelarse, se completa en el estado Cancelado.

7
Windows Workflow Foundation 4.0 2011

Si una excepción se produce durante la ejecución, el tiempo en ejecución coloca


WorkflowElement en el estado de error y propaga la excepción a la cadena primaria de
actividades.

A continuación se describen tres estados de realización de una actividad:

Cerrado: la actividad ha completado su trabajo y ha salido.

Cancelado: la actividad ha abandonado su trabajo sin contratiempos y ha salido. En este


estado, el trabajo no se revierte de manera explícita.

Con errores: la actividad ha encontrado un error y ha salido sin completar su trabajo.

8
Windows Workflow Foundation 4.0 2011

ACTIVIDADES
Las actividades representan bloques de compilación mínimos para los flujos de trabajo. La clase
Activity es la clase base para todas las actividades posibles.

Activity define un conjunto de propiedades y eventos, como cualquier clase, junto con la lógica
de ejecución que define el comportamiento del tiempo de ejecución de la actividad. Se puede
asociar a una Activity un conjunto de componentes adicionales a. Estos incluyen, pero sin
limitarse a, un validador, un generador de código, serializadores personalizados y un diseñador.

Todas las actividades comparten un conjunto común de propiedades definido en la clase base
Activity. Cada Activity puede declarar sus propias propiedades adicionales según sus
necesidades extendiendo esta clase. Dado que Activity deriva de DependencyObject, las
propiedades se pueden definir como propiedades CLR estándar y como propiedades de
dependencia.

La lógica de ejecución de Activity cumple un contrato que existe entre Activity y el tiempo de
ejecución del flujo de trabajo. Usted debe documentar la lógica de ejecución de Activity en un
sentido funcional, para que un programador del flujo de trabajo que utiliza Activity conozca
cómo se comporta. La propia lógica de ejecución se oculta del programador del flujo de trabajo
que incluye la actividad en un flujo de trabajo, porque la lógica de ejecución forma parte de un
contrato que existe estrictamente entre el flujo de trabajo en tiempo de ejecución y Activity.

En el siguiente ejemplo se define una actividad cuyo propósito es enviar un correo electrónico.
La actividad define una propiedad, Subject, que usa una propiedad de dependencia en su
implementación. Se pueden definir otras propiedades de una manera similar. El método Execute
se invalida para proporcionar la lógica para enviar el correo electrónico.

public class EnviarCorreo : Activity


{
public static readonly DependencyProperty SubjectProperty =
DependencyProperty.Register("Subject", typeof(string),
typeof(EnviarCorreo));

public string Subject


{
get { return base.GetValue(SubjectProperty) as string; }
set { base.SetValue(SubjectProperty, value); }
}

//Define otras propieddaes


Execute(ActivityExecutionContext executionContext)
{
// Logica necesaria para enviar un correo electronic
//...return ActivityExecutionStatus.Closed;
}
}

9
Windows Workflow Foundation 4.0 2011

Ejemplo- Hola Mundo

Ya se ha comentado anteriormente que los flujos de trabajo son una manera de ejecutar procesos
de negocio. Veamos un ejemplo sencillo paso a paso e como implementar una actividad:

En este ejemplo se creará un flujo muy simple que será básicamente lo equivalente al siguiente
código:

private static void SayHello()


{
Console.WriteLine("Hello Workflow 4");
}

1. Iniciar Visual Studio 2010 desde Inicio | Todos los programas | Microsoft Visual Studio
2010.

2. Crear un nuevo proyecto en Visual Studio 2010 de tipo Aplicación de consola.

3. Desde el proceso de negocio antes citado, podemos simplificarlo añadiendo una


actividad WriteLine para implementar el proceso. Desde el cuadro de herramientas,
arrastrar una actividad WriteLine al área de diseño.

10
Windows Workflow Foundation 4.0 2011

4. Establecer la propiedad WritLineText a “Hola Mundo”

Ya estaría el flujo de trabajo. La actividad WriteLine es una actividad simple que mostrará un
mensaje en la consola. La propiedad Text es una expresión que podría resultar de llamar a una
función o evaluar la propiedad de un objeto. En este caso, la expresión es un literal string por lo
debemos entrecomillar el string.

El resultado de la ejecución del flujo de trabajo es la siguiente:

Nota: Para ello ejecutar sin depurar.

Imaginemos que una vez que hemos implementado nuestro flujo de trabajo decidimos
refactorizarlo. A pesar de que tengas una aplicación trabajando, quizá quieras llevar a cabo
algunas mejoras. Veamos a través del ejemplo anterior como hacerlo.

Para este caso concreto, se especifico como Workflow1 el cual no es muy descriptivo,
cambiaremos el nombre del flujo de trabajo a HolaMundo y veremos como el cambio de
nombre afecta al resto del programa.

11
Windows Workflow Foundation 4.0 2011

Para cambiar el nombre del flujo de trabajo definido en Workflow1.Xaml, Abrirlo con el
diseñador de flujos de trabajo y hacer clic en un área vacía del área de diseñador. Esto permite
cambiar las propiedades del flujo de trabajo.

Clic Aquí

En la ventana de propiedades, establezca en la propiedad Nombre con el valor


Ejemplo1.HolaMundo o DecirHola.

Mientras no se necesite, es buena idea para el nombre del archivo xaml hacer que coincida con
el nombre de la actividad contenida en el. En el explorador de soluciones, podemos de igual
manera hacer clic con el botón derecho en el xaml y selecciona la opción renombrar para que
coincida.

Nota: tener cuidado cuando se renombran archivos xaml por que a diferencia de los archivos
que contienen clases, en estos no se renombran de forma automática el nombre de la clase
asociada sino que hay que hacerlo de forma manual.

Hagamos el ejemplo, para ver qué ocurriría si renombramos:

12
Windows Workflow Foundation 4.0 2011

¿Por qué falla la aplicación al compilar cuando cambiamos el nombre de un flujo de trabajo?

El flujo de trabajo es normalmente una clase declarada en XAML que hereda de


System.Activities.Activity. El nombre del flujo de trabajo es el nombre de la clase. Cuando
establecemos la propiedad Nombre, cambiamos el nombre de la clase.

<Activity mc:Ignorable="sap" x:Class="Ejemplo1.HolaMundo"


sap:VirtualizedContainerService.HintSize="251,240" …./>
</Activity>

Actualizar el método Main para usar el nuevo nombre

Windows Workflow Foundation 4.0 ejecuta un flujo de trabajo con el Workflow Runtime. La
manera más fácil de invocar el runtime es usando la clase WorkflowInvoker la cual es la que la
plantilla de aplicación de consola de flujo de trabajo usa.

1. Abrir la el archivo Program.cs y modificarlo usando el nombre nuevo “HolaMundo”.


Para hacer esto, localizar la llamada al método WorkflowInvoker.Invoke, y modificarlo
para usar HolaMundo en vez de Workflow1 como se muestra a continuación:

class Program
{
static void Main(string[] args)
{
WorkflowInvoker.Invoke(new HolaMundo());
}
}

¿Por qué Visual Studio me informa de que el tipo HolaMundo no está definido? Si cambias el
Main() después de compilar el flujo de trabajo con el nombre HolaMundo, se mostrará una
advertencia acerca del tipo HolaMundo de que no está definida. La razón es que Visual Studio
no es consciente de los tipos declarados en XAML hasta que se genera el ensamblado.

Ejecutamos la aplicación para comprobar que las modificaciones realizadas, dejan la aplicación
en un estado correcto:

13
Windows Workflow Foundation 4.0 2011

La Actividad CodeActivity

Como hemos comprobado en el ejemplo anterior Windows Workflow Foundation 4.0 consiste
en un diseñador que edita archivos .xamls y un runtime que invoca actividades. Al crear un flujo
de trabajo, creamos un nuevo tipo de actividad y debido a que las actividades son justamente
clases que heredan de System.Activities.Activity o una de sus subclases, puedes declarar flujos
de trabajo usando C#, VB o XAML.

CodeActivity es una clase abstracta que se usa para crear una actividad personalizada con
comportamiento imperativo definido con el método Execute(CodeActivityContext), que da
acceso a la resolución de variables, argumentos y extensiones.

El siguiente ejemplo, veremos cómo implementar el proceso de negocio “Saludar” mediante la


creación de una actividad en C#.

Como he comentado ya anteriormente, las actividades implementan un proceso de negocio.


Algunas actividades implementarán el proceso mediante la invocación a otras actividades. Por
ejemplo, la actividad HolaMundo no escribe texto en la consola pero en vez de ello usa la
actividad WriteLine para hacerlo, podría tener implementada la misma actividad HolaMundo en
C# mediante la herencia de System.Activities.Activity y crear una Implementación como
veremos.

public sealed class ActividadHolaMuno : Activity{

WriteLine writeLine = new WriteLine() {

Text = "Hola Mundo" };

public ActividadHolaMundo() {

Implementation = () => { return writeLine; };

}}

Este enfoque es útil si estas creando un flujo de trabajo fuera de otras actividades que realizan el
trabajo como en el ejemplo anterior con la actividad WriteLine. Sin embargo, a veces cuando
creamos una actividad implementamos la lógica de negocio en si misma o mediante la llamada a
otra clase que no es una actividad que hace el trabajo. Para hacer esto, heredaremos de una clase
base diferente System.Activities.CodeActivity y sobrescribiremos el método Execute.

1. Abrir Visual Studio 2010, desde Inicio | Todos los programas | Visual Studio 2010.

2. Pulsar con el botón derecho en el proyecto Ejemplo1, seleccionar Añadir/ Nuevo


elemento. Desde la plantillas Workflow seleccionar Code ACtivity y llamar al archivo
HolaMundoEnCodigo.

3. Eliminar la propiedad Text de la plantilla, ya que no la necesitamos.

4. CodeActivity es una clase abstracta. Esto significa que cuando heredemos de ella,
deberíamos sobrescribir el método Execute. Esto es el lugar donde realizaremos el
trabajo de la actividad. Reemplazar la implementación predeterminada de la clase son el
siguiente código:

14
Windows Workflow Foundation 4.0 2011

public sealed class HolaMundoEnCodigo : CodeActivity{


protected override void Execute(CodeActivityContext context)
{
Console.WriteLine("Hola Mundo ");
}
}

5. Ahora deberíamos actualizar el método Main para invocar HolaMundoEnCódigo. Para


ello cambiamos Program.cs para usar la clase HolaMundoEnCodigo. Para realizar este
cambio, localizar el código del método WorkflowInvoker.Invoke y reemplazarlo con el
siguiente código:

static void Main(string[] args){

WorkflowInvoker.Invoke(new HolaMundoEnCodigo());

6. Para verificar el buen funcionamiento del cambio realizado ejecutamos el flujo de


trabajo, y obtenemos el siguiente resultado:

¿Por qué crear una actividad de código?

Escribir la lógica de negocio en código no es nada nuevo. ¿Por qué hacer el esfuerzo de escribir
una clase especial que herede de CodeActivity? La razón es que hacer ahora la lógica de
negocio puede estar compuesta en otros procesos de negocio que usan el workflow runtime.
Como veremos más tarde también beneficia al modelo Multihilo y gestión de datos que
proporcionan una alta escalabilidad.

15
Windows Workflow Foundation 4.0 2011

FLUJOS DINÁMICOS CON XAML


Hasta este punto, hemos creado flujos de trabajo en archivos .xaml o .cs. Estos archivos son
compilados en tipos que están incluidos en el ensamblado del proyecto y se ejecuta mediante el
workflow runtime.

A pesar de que podría ser similar al formato de los archivos de origen no importa,. xaml ofrecen
algunas ventajas sobre creación de flujos de trabajo en C #.

El diseñador del flujo de trabajo trabaja con sólo con archivos .xaml, los flujos de
trabajo creados en C# no tienen soporte de diseño.
XAML puede ser cargado y ejecutado de forma dinámica sin compilarlo en el
ensamblado.

Los flujos de trabajo dinámicos proporcionan algunas posibilidades interesantes para los
programas que quieren generar lógicas de negocio o realizar decisión en tiempo de ejecución en
la que la lógica de negocio se carga y se ejecuta.

En el siguiente ejemplo veremos cómo llevar a cabo una actividad dinámica:

1. Lo que haremos será modificar el programa HolaMundo para cargar y ejecutar el


archivo HolaMundo.xaml. Después modificaremos el texto del HolaMundo.xaml y
observaremos el cambio en el mensaje en la siguiente ejecución de la aplicación.

a. En el explorador de soluciones seleccionar: Copiar siempre

b. Acción de compilación: Contenido

c. Copiar en el directorio de resultados: Copiar siempre

d. Establecer la herramienta personalizada en blanco

2. Modificar el método Main para cargar el archivo HolaMundo.xaml

Anteriormente el flujo de trabajo era compilado en un tipo. Para invocar un flujo de trabajo
desde un archivo xaml necesitamos usar ActivityXamlService para cargar el archivo xaml en

16
Windows Workflow Foundation 4.0 2011

memoria y crear una instancia de una actividad que WorkflowInvoker puede invocar. Tener en
cuenta que cualquier ensamblado que al que se haga referencia en el archivo xaml debe estar
disponible cuando el flujo de trabajo este invocado.

a. Añadir el siguiente espacio de nombres en las directivas de program.cs

using System.Activities.XamlIntegration;

b. Modificar Program.cs para usar ActivityXamlServices y también añadir una


llamada a Console.ReadKey, esto hará más fácil ver que está ocurriendo cuando
la aplicación se ejecute.

static void Main(string[] args){


WorkflowInvoker.Invoke(ActivityXamlServices.Load("HolaM
undo "));
Console.ReadKey(false);}

3. Comprobemos el funcionamiento de la aplicación.

En esta verificación, ejecutaremos la aplicación, después modificaremos el archivo


HolaMundo.xaml desplegado desde la carpeta bin/debug y veremos que el nuevo texto aparece
en la consola.

1. Ejecutar sin depurar. La aplicación debería mostrar en la consola el siguiente mensaje:


“Hola Mundo”.

2. Navegar al directorio Bin\Debug en la carpeta del proyecto y localizar el archivo


HolaMundo.xaml.

3. Pulsar con el botón derecho en HolaMundo.xaml y seleccionar Editar para abrir un


editor de texto plano.

4. En NotePad cambie la propiedad Text de la actividad WriteLine por “Hola Mundo


XAML” después guarde y cierre.

17
Windows Workflow Foundation 4.0 2011

5. En el explorador de Windows, ejecute HolaMundo.exe y debería aparecer Hola Mundo


XAML.

6. Volver a Visual Studio y restaurar HolaMundo.xaml a los valores predeterminados.


Necesitará estos valores para el resto de etiquetas.

a. Acción de compilación: XamlAppDef

b. Copiar al directorio de resultados: No Copiar

c. Herramienta personalizada: MSBuild:Compile

18
Windows Workflow Foundation 4.0 2011

PROBAR LOS FLUJOS DE TRABAJO


Bueno, una vez llegados a este punto, la aplicación no es muy interesante. Es solo trabajo con la
consola y no recibir ningún argumento de entrada. Muchas aplicaciones hacen un trabajo
significativo que tendrá que hacer frente a los argumentos de entrada y salida. Además, la
aplicación no es fácil de probar en su forma normal.

Vamos a ver a continuación como modificar la aplicación HolaMundo para utilizar argumentos
y prepararla para ser útil en otros entonos que no sean los de la consola para devolver algún
saludo en vez de escribir directamente en la consola con la actividad WriteLine .

Crear una prueba unitaria

1. Para empezar a crear una prueba unitaria para el flujo de trabajo y verificar su
comportamiento, pulse con el botón derecho en el explorador de soluciones en la
solución Ejemplo1 y seleccione Añadir / Nuevo proyecto y establezca las opciones de
proyecto.

a. Plantillas instaladas: seleccionar Pruebas

b. Plantilla: proyecto de prueba

c. Nombre: FlujoHolaMundo.Test

2. Pulse con el botón derecho en el proyecto FlujoHolaMundo.Test y después Añadir


Referencia. Usar la pestaña Proyectos, añadir una referencia de proyecto al proyecto
Ejemplo1. Repetir estos pasos, usando la pestaña .NET para añadir una referencia a la
biblioteca System.Activities.

19
Windows Workflow Foundation 4.0 2011

3. Pulsar con el botón derecho en uniTest1.cs, y seleccionar Renombrar y cambiar el


nombre a HolaMundoPrueba.cs.

Una vez tenemos el entorno necesario para crear la prueba unitaria, vamos a crearla.

1. Añadir el siguiente espacio de nombres a las directivas de HolaMundoPrueba.cs

using System.Activities;
using Ejempo1;

2. Crear una prueba para asegurarse de que el flujo de trabajo se comporta como
queremos. Para hacer esto, abrir HolaMundoPrueba.cs, localizar el método method1 y
renombrarlo a DeberiaDevolverConNombre.

3. La actividad HolaMundo no acepta argumentos todavía, pero escribiremos el código


para invocar que lo haga. Reemplace la implementación como se muestra a
continuación.

[TestMethod]public void DeberiaDevolverConNombre(){


IDictionary<string, object> output;
output = WorkflowInvoker.Invoke( new HolaMundo()
{
UserName = "Test"
});
Assert.AreEqual("Hola Mundo desde el Flujo de
trabajo", output["Saludar"]);}

¿Por qué pasar argumentos a una actividad?

Puedes crear la actividad e inicializar los argumentos (los cuales son propiedades públicas)
usando la inicialización de objeto o pasando un diccionario de tipo Dictionary<string, object>
de parámetros de entrada que mapean los nombres de los argumentos de entrada de la actividad.

¿Por qué conseguir datos de salida?

La variable de salida es un IDictionary<string,object> que contiene un mapa de las variables de


salida usando los nombres de las variables como clave.

2. Obtención de la aplicación para compilar.

La interfaz para la actividad parece que está bien, pero veremos qué tal y como está ahora no
compila.

20
Windows Workflow Foundation 4.0 2011

3. Abrir HolaMundo.xaml en el diseñador.


4. Abrir el panel de argumentos haciendo clic en Argumentos

5. Añadir los argumentos UserName y Saludo como se muestra a continuación

Los argumentos en Windows Workflow Foundation representan el flujo de datos de entrada y


salida de una actividad. Una Actividad tiene un conjunto de argumentos y conforman la firma
de la actividad, cada argumento tiene una dirección específica: entrada, salida, o entrada/salida.

6. Ejecutar la prueba

Es importante comprobar la prueba, para comprobar el funcionamiento de nuestro método. Así


que ejecutamos al prueba, pero resulta que no hemos introducido ningún valor de entrada por lo
que a su vez tampoco devuelve ningún valor en el argumento saludar.

21
Windows Workflow Foundation 4.0 2011

Bien comprobado que la prueba en sí funciona correctamente vamos a solucionar o modificar


HolaMundo para que pase la prueba.

1. Escribir texto en la consola con WriteLine no hace que pase la prueba por lo que
elimine la actividad WriteLine.

2. Necesita establecer el valor del argumento de salida Saludo, para hacer esto, arrastre
una actividad Assign del grupo primitivas en el cuadro de herramientas al área de
diseño.

3. Establezca la propiedad To a Saludo.

4. Puedes escribir la expresión saludar en el área de diseño pero si la expresión es


demasiado grande usar mejor la ventana de propiedades. Haga clic en el botón que hay
a la derecha de la propiedad Value para abrir el editor para la expresión.

22
Windows Workflow Foundation 4.0 2011

En el editor, puede escribir la expresión, incluyendo aquellas que usen líneas en VB. Establecer
la expresión a "Hola " + UserName + " desde Workflow 4":

5. Ahora ya se supone que la actividad pasa la prueba, para comprobarlo, ejecutamos de


nuevo la prueba.

23
Windows Workflow Foundation 4.0 2011

WORKFLOWAPPLICATION
Una vez llegados a este punto, hemos creado una actividad y la hemos invocado de la manera
más simple posible con la clase WorkflowInvoker. El método WorkflowInvoker.Invoke es
simple ya que es síncrono e invoca el flujo de trabajo en el mismo hilo que el llamante.

Otra manera de invocar un flujo de trabajo es con la clase WorkflowApplication. Esta clase
permite devolver un flujo de trabajo en un hilo aparte y suplir los delegados para ser invocado
cuando el flujo de trabajo se complete. Esto permite crear servidores Multihilo o programas
clientes Multihilo mucho más fácil que si no usáramos Windows workflow 4.0.

WorkflowApplication proporciona un modelo más enriquecido para ejecutar flujos de trabajo


que incluye notificación de eventos de ciclo de vida, control de ejecución, reanudación de
marcadores y persistencia. WorkflowServiceHost proporciona el soporte técnico para las
actividades de mensajería y se usa principalmente con servicios de flujo de trabajo.

Cuando se invoca un flujo de trabajo con WorkflowInvoker, el flujo de trabajo se ejecuta en el


subproceso que realiza la llamada y el método Invoke se bloquea hasta que el flujo de trabajo
haya finalizado, incluido el tiempo de inactividad. Para configurar un intervalo de tiempo de
espera en el que se debe completar el flujo de trabajo, use una de las sobrecargas Invoke que
tome un parámetro TimeSpan.

Nota: La excepción TimeoutException solo se produce si se agota el tiempo de espera y el flujo


de trabajo queda inactivo durante la ejecución. Un flujo de trabajo que tarda en completarse
más tiempo que el especificado finaliza correctamente si el flujo de trabajo no queda inactivo

WorkflowApplication actúa como un proxy seguro para subprocesos para WorkflowInstance,


que encapsula el tiempo de ejecución, y que proporciona métodos para crear y cargar las
instancias de flujo de trabajo, pausar y reanudar, terminar y notificar los eventos de ciclo de
vida. Para ejecutar un flujo de trabajo mediante WorkflowApplication, cree
WorkflowApplication, suscríbase a cualquier evento de ciclo de vida que desee, inicie el flujo
de trabajo y, a continuación, espere a que termine.

En el siguiente ejemplo, veremos cómo invocar un flujo de trabajo con la clase


WorkflowApplication modificando el ejemplo anterior. Antes de nada destacar de la necesidad
de dos requisitos para el flujo de trabajo:

Devolver un saludo personalizado.

Devolver un entero distinto de cero conteniendo el ID del hilo gestionado del flujo de
trabajo que será invocado.

Usar el enfoque de “escribir primero la prueba”, empezará mediante la escritura de una prueba
que verifique el nuevo requisito del ID del hilo del flujo de trabajo.

1. Abrir HolaMundo.cs y añadir las directivas de los siguientes espacios de nombres:

using System.Threading;
using System.Diagnostics;

24
Windows Workflow Foundation 4.0 2011

2. Añadir la prueba FlujoTrabajoTest como se muestra a continuación:

[TestMethod]public void FlujoDeTrabajoTest()


{
var output = WorkflowInvoker.Invoke( new HolaMundo()
{
UserName = "Test"
});
Assert.IsTrue(output.ContainsKey("Flujodetrabajo"),
"Hola Mundo deberia contener un OutArgument llamado
Flujodetrabajo");
// Don't know for sure what it is yet
var outarg = output["Flujodetrabajo"];
Assert.IsInstanceOfType(outarg, typeof(Int32),
"Flujo de trabajo deneria de ser de tipo Int32");
Assert.AreNotEqual(0, outarg, "Flujodetrabajo no
deberia ser cero");
Debug.WriteLine("Hilo Test es " +
Thread.CurrentThread.ManagedThreadId);
Debug.WriteLine("Workflow thread is " +
outarg.ToString());
}
}

Si ejecutamos el proyecto de prueba veremos que esta segunda prueba unitaria no se pasa.

Devolver el Flujodetrabajo como un argumento

Ahora que tenemos una prueba para verificar el comportamiento, necesitamos modificar el
flujo de trabajo para que pase la prueba.

1. Abrir HolaMundo.xaml y añadir un argumento de salida llamado Flujodetrabajo de tipo


Int32.

25
Windows Workflow Foundation 4.0 2011

Hasta ahora, el flujo de trabajo ha sido solo una actividad. En este punto necesitamos dos
actividades, una para asignar el saludo y otra para asignar el hilo del flujo de trabajo.
Necesitamos para modificar el flujo de trabajo usar una actividad que contenga dos actividades
y hay varias actividades que podríamos elegir para hacerlo, pero empezaremos con la más
simple, con la actividad de secuencia.

2. No podremos arrastrar una actividad de secuencia en el diseñador mientras no


eliminemos la actividad Assign que todavía está en el área de diseño.

3. Arrastrar ahora si la actividad de secuencia al área de diseño.

4. Ahora en el interior de la actividad de secuencia seleccionar Pegar del menú contextual,


para incluir la actividad Assign que teníamos

26
Windows Workflow Foundation 4.0 2011

5. Añadir el espacio de nombres System.Threading en el flujo de trabajo. Hacer clic en


Imports y añadirla. Para hacerlo primeramente escriba las primeras letras del espacio de
nombres y cuando aparezca el correcto presione enter.

¿Hay que añadir System.Threading a Imports?

No, es opcional si se encuentra en le proyecto. Si no importa el espacio de nombres tendrá que


especificar en la clase el nombre completo que es System.Threading.Thread.

6. Ahora necesitamos asignar el ID del hilo actual al argumento de salida Flujodetrabajo.


Arrastre una actividad dentro de la secuencia como se muestra a continuación:

27
Windows Workflow Foundation 4.0 2011

7. Generar la solución y ejecutar el proyecto de prueba y veremos que la pasará. Si quieres


ver el hilo devuelto mediante el flujo de trabajo, haz doble clic en la prueba. La salida
del depurador aparecerá en la venta de resultados de prueba. El ID del hilo actual varia
en la máquina pero destacar que el ID del hilo para la prueba es el mismo que el ID del
hilo del flujo de trabajo ya que WorkflowInvoker invoca el flujo de trabajo de forma
asíncrona en la llamada del hilo.

Modificar la prueba para utilizar WorkflowApplication

La prueba es buen pero tiene un inconveniente, Verifica que el flujodetrabajo devuelto no es


cero pero no verifica que devuelve el ID del hilo actual que está en el flujo de trabajo. Por
ejemplo, la prueba pasa si el flujo de trabajo devuelve siempre 1 en el argumento de salida.

Si queremos verificar el ID del hilo actual necesitamos usar WorkflowApplication para ejecutar
el flujo de trabajo. En el siguiente ejemplo modificaremos la prueba para capturar el hilo del

28
Windows Workflow Foundation 4.0 2011

flujo de trabajo mediante la obtención en la llamada WorkflowApplication.Completed y


después comparar el valor devuelto desde el flujo de trabajo.

1. Modificar el método FlujoDeTrabajoTest con el siguiente código:

[TestMethod]public void FlujoDeTrabajoTest()


{
AutoResetEvent sync = new AutoResetEvent(false);
Int32 actionThreadID = 0;
IDictionary<string, object> output = null;
WorkflowApplication workflowApp = new
WorkflowApplication(
new HolaMundo()
{
UserName = "Test"
});

workflowApp.Completed = (e) => { output = e.Outputs;


actionThreadID = Thread.CurrentThread.ManagedThreadId;

sync.Set();
};
workflowApp.Run();

sync.WaitOne(TimeSpan.FromSeconds(1));
Assert.IsNotNull(output, "salida no establecida,
workflow ha tenido timed out");
Assert.IsTrue(output.ContainsKey("flujodetrabajo"),
"HolaMundo deberia contener un OutArgument llamado
flujodetrabajo");
var outarg = output["flujodetrabajo"];
Assert.IsInstanceOfType(outarg, typeof(Int32),
"flujodetrabajo deberia ser de tipo Int32");
Assert.AreEqual(actionThreadID, (int)outarg,
"flujodetrabajo deberia ser igual a actionThreadID");
Debug.WriteLine("Hilo Test es " +
Thread.CurrentThread.ManagedThreadId);
Debug.WriteLine("Hilo Workflow es " +
outarg.ToString());
}

Delegados, no eventos

WorkfloApplication.Completed y otras propiedades de workflowApplication no son eventos


pero si son delegados. Manejarlos requiere proporcionar un método, método anónimo o una
expresión lambda.

Verificar el resultado

Ahora ya si estamos listos para comprobar si nuestra actividad pasa la prueba con los nuevos
requisitos.

29
Windows Workflow Foundation 4.0 2011

Haga doble clic sobre FlujoDeTrabajoTest y compruebe los resultados

Perfecto!!!! El flujo de trabajo y la prueba de ejecutan en hilos diferentes.

30
Windows Workflow Foundation 4.0 2011

Lógica If/Else
En el ejemplo anterior, creamos una aplicación de flujo de trabajo con un mensaje
personalizado. En este ejemplo, veremos cómo usar la lógica If/Else en el flujo de trabajo para
mostrar un mensaje diferente dependiendo de una condición personalizada.

Al igual que antes, primeramente escribiremos la prueba para el nuevo requisito. Si el nombre
tiene un número impar de letras la primera palabra será Saludos y sino Hola.

private static string HolaMundo(string userName)


{
string FirstWord = null;
if (userName.Length % 2 == 0)
FirstWord = "Hola";
Else
FirstWord = "Saludos";
return FirstWord + ", " + userName +" desde Workflow 4";
}

1. Crear una prueba unitaria que verifique el Nuevo requisito. Para ello añadir el siguiente
código al proyecto de prueba.

[TestMethod]public void SaludoDependiente()


{
var output = WorkflowInvoker.Invoke( new HolaMundo() {
UserName = "Impar" });
string greeting = output["Saludo"].ToString();
Assert.AreEqual("Saludos impar desde Workflow 4",
greeting);
}

2. Si ahora ejecutamos la prueba, es evidente que fallará:

Así que para solucionarlo, vamos a implementar el requisito en el flujo de trabajo.

1. Seleccione la actividad de secuencia y haga clic en la figura.

2. Clic en el botón Variables. Se mostrará un panel que mostrará a su ves todas las
variables disponibles para la actividad de secuencia.

3. Clic en crear una variable

31
Windows Workflow Foundation 4.0 2011

4. Escribir FirstWord en el cuadro Nombre.

Las variables en Windows Workflow Foundation representan el almacenamiento de datos. Los


argumentos, por otra parte representan el flujo de datos de entrada y salida de una actividad. Las
variables tienen un ámbito al igual que en C#. Si abre el panel de variables sin seleccionar una
actividad no será capaz de añadir una variable. La actividad seleccionada en el diseñador
proporciona el ámbito para las variables. En este caso, FirstWord pertenece al ámbito de la
actividad de la secuencia.

5. Ahora necesita comprobar que el argumento UserName tiene un número impar de


caracteres. Para hacerlo, añadir una actividad If desde el grupo flujo de control en el
cuadro de herramientas en la actividad secuencia por encima de la actividad Assign.

Como se observa, aparece en la actividad el icono de Warning lo que nos indica que la actividad
no es válida. La actividad If está todavía sin especificar una condición.

6. Modificar una expresión por la condición if. Para hacer esto, doble clic en la actividad
If para abrirla y escribir la siguiente expresión en el cuadro condición. Comprobará el
nombre si es impar o no.

32
Windows Workflow Foundation 4.0 2011

Las expresiones son sentencias programadas que puedes ser literales, o sentencias condicionales
una expresión que concatene varios strings o llamas a métodos o invocar otra actividad. Las
expresiones son escritas mediante la sintaxis de Visual Basic incluso si la aplicación está en C#.
Esto significa que la capacitación no importa, la comparación es realizada usando un símbolo
equals en vez de “==”, y los operadores booleanos son la palabras “And” y “Or”.

7. Actualizar el valor de la variable FirstWord , la cual debería ser “Hola”. Arrastrar una
actividad Assign desde el grupo primitivas en el cuadro de herramientas en el área del
Then. Después, escribir FirstWord en el cuadro To y “Saludos” en el valor.

8. Modificar la actividad Assign final para personalizar el mensaje de saludo que se


mostrará basado en el valor FirstWord. Para realizar esto, en la actividad Assign
localizar a continuación del If y reemplazar el contenido el valor del cuadro Value con
la siguiente expresión: FirstWord + “ ”+UserName + “ desde Wokflow 4”

33
Windows Workflow Foundation 4.0 2011

9. Ya sólo queda verificar el correcto comportamiento del flujo de trabajo, para ello
ejecutamos la prueba.

34
Windows Workflow Foundation 4.0 2011

Manejo de Errores
Puede haber notado un error potencial en la aplicación y eso que es muy sencilla. ¿Qué sucede
si no pasamos un nombre de usuario para el flujo de trabajo? En el siguiente ejemplo, agregará
un control de errores para el flujo de trabajo utilizando el Try / Catch, <T> .

Destacar que si pasamos un string vacio la aplicación debería funcionar correctamente. La única
manera de obtener null en el nombre pasado en el flujo de trabajo es para crearlo sin
suministrarlo en el argumento UserName.

Para hacer esto, añadir un nuevo método como prueba unitaria, como se muestra a continuación:

[TestMethod]public void ShouldHandleNullUserName()


{
// Invocar sin argumentos
WorkflowInvoker.Invoke(new HolaMundo());}

Ahora ejecutar la prueba:

Cuyo resultado es:

La ejecución fallará y se lanzará la siguiente excepción:

HelloWorkflow.Tests.SayHelloFixture.ShouldHandleNullUserName threw
exception: System.NullReferenceException: Object reference not set to
an instance of an object

Añadir la actividad try/Catch al flujo de trabajo

Para manejar el error, podríamos validar el argumento nombre antes de usarlo o simplemente
podríamos capturar la excepción y tratarla.

1. Abrir HolaMundo.xaml en el diseñador y arrastrar una actividad TryCatch desde el


cuadro de herramientas:

35
Windows Workflow Foundation 4.0 2011

La actividad Try/Catch puede ser usada por flujos de trabajo para manejar excepciones
que son lanzadas durante la ejecución de un flujo de trabajo. Estas excepciones pueden
ser manejadas o propagadas usando la actividad Throw. Las actividades en la sección
Finally son ejecutadas cuando incluso la sección try se completa o se entra en la sección
Catch.

2. Ahora necesitamos mover la actividad If en el bloque Try.

Colapsar la actividad If para hacer el diagrama más pequeño

Arrastrar la actividad If en el bloque Try

36
Windows Workflow Foundation 4.0 2011

3. Ahora necesitamos capturar una excepción de tipo NullReeferenceException. Para


hacer esto, siguiendo los siguientes pasos:

a. Clic en Añadir nueva captura

b. Seleccionar System.NullReferenceexception desde el cuadro combo y


presionar enter.

4. En la sección catch de la actividad necesitamos decidir cómo manejar el erro. En este


caso, estas reemplazando la excepción con un ArgumenNulleception y lanzarlo. Esto
permite al llamante conocer que la excepción fue lanzada por el argumento UserName.
Arrastrar una actividad Throw dede el cuadro de herramientas y soltarla en el área de
los Catches.

37
Windows Workflow Foundation 4.0 2011

5. Establecer la expresión de la actividad Throw. Para hacer esto, seleccionar la actividad


Throw y en la ventana de propiedades escribir la siguiente expresión en el cuadro
Exception. New ArgumentException(“UserName”)

6. Ahora necesitamos fijar la prueba para que espere ésta excepción. Para hacer esto, abrir
HolaMundo.cs y añadir la anotación ExceptedException en el método que funciona
como prueba unitaria como se muestra a continuación.

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ShouldHandleNullUserName()
{
// Invocar sin argumentos WorkflowInvoker.Invoke(new HolaMundo());
}

7. Si ejecutamos la prueba comprobaremos que pasa correctamente:

38
Windows Workflow Foundation 4.0 2011

ACTIVIDADES PERSONALIZADAS
Las actividades personalizadas de los flujos de trabajo constituyen uno de los aspectos más
importantes de Windows Workflow Foundation, por lo que se deben tener en cuenta varias
características a la hora de crearlas. Vamos a ver los componentes básicos necesarios para crear
actividades personalizadas para el dominio empresarial, entre otras las responsabilidades en
tiempo de ejecución, la experiencia en tiempo de diseño o el desarrollo de actividades
asincrónicas.

Al crear una actividad personalizada, la primera decisión que se debe tomar consiste en optar
por una actividad simple o compuesta. Las actividades simples, o básicas si se prefiere, son
aquéllas cuya lógica y ejecución se encuentran encapsuladas en el propio código de la actividad.
Son actividades simples, por ejemplo, la actividad SendEmail del SDK de Windows o una
actividad que permita consultar datos de una base de datos.

Las actividades compuestas, en cambio, logran su objetivo basándose en la ejecución de


actividades secundarias. Las actividades compuestas se pueden crear de dos modos según los
requisitos particulares. Una opción consiste en crear una actividad compuesta que permita a los
usuarios agregar actividades secundarias en tiempo de diseño y que controle la ejecución de
éstas. Por ejemplo, la actividad While incluida en Windows Workflow Foundation permite
agregar una actividad secundaria y, a continuación, configurar la funcionalidad de bucle de la
actividad While. La actividad compuesta While se centra en administrar la ejecución de sus
actividades secundarias. La otra opción disponible para crear actividades compuestas consiste
en crear una actividad que se derive de una actividad compuesta existente (la actividad
Sequence, por ejemplo) y agregar un grupo de actividades como secundarias para definir un
componente reutilizable.

Las actividades reutilizables, por ejemplo, las de aprobación del administrador, constan de
varios pasos de fácil modelado con una recopilación de actividades existentes. La actividad de
aprobación del administrador puede servirse de actividades como IfElse con el fin de determinar
si el administrador se encuentra en línea. En caso afirmativo, se le podría enviar un mensaje
instantáneo; de lo contrario, bastaría con enviar un mensaje de correo electrónico. Esta actividad
encapsula toda la compleja lógica de una solicitud de aprobación, la cual forma parte de un flujo
de trabajo y constituye en sí misma un flujo.

El mero hecho de incluir esta actividad compuesta en sus flujos de trabajo libra a los usuarios de
esta actividad de tener que hacerse cargo de las complejidades y los detalles de ponerse en
contacto con el administrador.

Crear una actividad básica

En muchos aspectos, crear una actividad es parecido a crear un componente. Al fin y al cabo,
una actividad es un componente reutilizable que aprovecha las características de Windows
Workflow Foundation, el marco en el que se ejecuta. Así pues, el primer paso para crear una
actividad consiste en determinar su función y su interfaz. En el caso de este ejemplo es crear la
actividad CreateUser, la función es crear un usuario mediante el modelo de proveedores de
suscripciones de ASP.NET. Por consiguiente, la interfaz requerirá las propiedades necesarias
para crear un usuario: dirección de correo electrónico, nombre de usuario, contraseña y otros
datos similares.

39
Windows Workflow Foundation 4.0 2011

El código que controla la función de una actividad básica se escribe en el método Execute
sustituido de la clase base Activity. Aparte del código específico de la función de la actividad, el
desarrollador también tiene la responsabilidad de devolver el estado de la actividad. En el caso
de las actividades básicas, casi siempre será el estado Closed. Al devolver Closed, se indica al
tiempo de ejecución del flujo de trabajo que la actividad finalizó su ejecución, por lo que es
libre de programar la siguiente actividad del flujo de trabajo.

En la siguiente imagen se muestra una actividad básica empleada para crear un usuario del
sistema de suscripciones de ASP.NET.

public string Password


{
get { return _password; } set {_password = value; }
}

public string Email


{
get { return _email; } set{ _email = value; }
}

public string UserName


{
get { return _username; } set{ _username = value; }
}

public string MembershipProviderName


{
get { return _provider; } set{ _provider = value; }
}

protected override ActivityExecutionStatus Execute(


ActivityExecutionContext executionContext)
{
MembershipProvider mp =
String.IsNullOrEmpty(MembershipProviderName)
? Membership.Provider :
Membership.Providers[MembershipProviderName];

MembershipCreateStatus status;
mp.CreateUser(UserName, Password, Email, null, null, true,
Guid.NewGuid(), out status);

return ActivityExecutionStatus.Closed;
}

Propiedades de dependencia

Es importante proporcionar propiedades de las actividades a fin de ofrecer a sus usuarios la


capacidad de configurar su ejecución. Con los campos o las propiedades estándar de
Microsoft .NET, tal como se mostraba en el ejemplo anterior, el usuario de la actividad debe
escribir código en el flujo de trabajo si desea establecer los valores de las propiedades.

40
Windows Workflow Foundation 4.0 2011

Ahora bien, imaginemos que desea contar con un mecanismo que permita crear flujos de trabajo
a personal no especializado. No conviene que todos los usuarios tengan que escribir código sólo
para conectar los datos de una actividad a otra. Gracias al modelo declarativo de Windows
Workflow Foundation, es más fácil actuar en esta situación, y una de las características que
sustenta este modelo son las propiedades de dependencia.

Las propiedades de dependencia son un medio para almacenar los valores de una actividad o del
estado de un flujo de trabajo. A diferencia de las propiedades estándar en las que el valor se
almacena dentro de la propia clase, el valor de las propiedades de dependencia se almacena en
un diccionario de cuyo mantenimiento se ocupa la clase base DependencyObject.

Las propiedades de dependencia habilitan varias características fundamentales, de las cuales la


más importante quizá sea el enlace de actividades. Como su propio nombre indica, el enlace de
actividades es el acto de enlazar una propiedad de una actividad con una propiedad de otra
actividad o del flujo de trabajo en sí. Al enlazar una propiedad de dependencia con otra
propiedad, el valor se propaga de manera automática. Por ejemplo, si un flujo de trabajo tiene
definida una propiedad para el nombre de usuario que se debe crear y se define una propiedad
UserName como propiedad de dependencia, ésta se puede enlazar a dicha propiedad del flujo de
trabajo. Cuando se inicia el flujo de trabajo con parámetros, el valor pasado al flujo de trabajo se
pasa automáticamente a la actividad por mediación del enlace. En la siguiente imagen se
muestra esta relación.

Este tipo de enlace traslada al campo de los componentes el mismo modelo eficaz del que
depende una gran mayoría de desarrolladores de interfaces de usuario. El enlace de actividades
simplifica en gran medida la conexión de componentes. Además, confiere plausibilidad a la idea
de que quienes no sean programadores sean capaces de crear flujos de trabajo. Si las actividades
cuentan con un diseño adecuado, los usuarios han de poder arrastrarlas a una superficie de
diseño con objeto de conectar los valores entre ellas sin necesidad de escribir código.

La definición de propiedades de dependencia en una actividad es un proceso que consta de dos


pasos. El primer paso consiste en especificar y registrar la definición de propiedad de
dependencia; para ello, debe crear una variable estática y establecer su valor llamando al
método Register de la clase DependencyProperty.

41
Windows Workflow Foundation 4.0 2011

El segundo paso consiste en definir una propiedad estándar de .NET con métodos get y set; en
este caso, se confía en la clase base para almacenar o recuperar el valor de la propiedad de
dependencia. Recuerde que existe un fragmento de código para Visual Studio® 2005 que
facilita este proceso. El código que hay a continuación muestra la propiedad UserName
redefinida como propiedad de dependencia.

public static DependencyProperty UserNameProperty =


System.Workflow.ComponentModel.DependencyProperty.Register(
"UserName", typeof(string), typeof(CreateUserActivity));

[Description("El Nuevo username para este usuario)]


[Category("User")]
[Browsable(true)]
[DesignerSerializationVisibility(
DesignerSerializationVisibility.Visible)]
public string UserName
{
get
{
return (string)base.GetValue(
CreateUserActivity.UserNameProperty);
}
set
{
base.SetValue(CreateUserActivity.UserNameProperty, value);
}
}

El enlace de actividades es una característica que se habilita mediante el uso de propiedades de


dependencia. Otras características son la promoción de propiedades, la notificación de cambios
y la administración de estados. Como los valores de las propiedades se almacenan en un
diccionario central, resulta más sencillo serializar el estado de las actividades. De modo similar,
este almacenamiento central facilita la supervisión de cambios de dichas propiedades, ya que
permite que otras actividades o el código del flujo de trabajo registren los cambios y adopten las
medidas oportunas cuando se modifique un valor en particular.

Una pregunta que se suele plantear es cuándo debe usar el desarrollador propiedades de
dependencia en lugar de propiedades estándar de .NET. La respuesta es muy sencilla: las
propiedades de dependencia se deben emplear en cualquier propiedad que se beneficie de estar
enlazada a otra propiedad u otro campo. Lo cierto es que no se puede esgrimir ningún
argumento bueno para no usar las propiedades de dependencia, y menos con lo fáciles que son
de escribir gracias a los fragmentos de código disponibles.

42
Windows Workflow Foundation 4.0 2011

Creación de actividades compuestas

Aunque las actividades básicas aportan un gran valor, con frecuencia es más conveniente
suministrar componentes reutilizables de mayor complejidad. Uno de esos tipos de
componentes es la actividad reutilizable que incluye varias actividades básicas para representar
una interacción reutilizable o un flujo de lógica; por ejemplo, sería una actividad compuesta la
que, en primer lugar, solicite la aprobación de un administrador antes de crear un usuario. En
esta actividad única estarían contenidas la solicitud de permiso, la recepción de la respuesta y,
por último, la creación del usuario. El objetivo a la hora de crear esta actividad compuesta ha de
ser facilitar que el usuario la agregue a un flujo de trabajo sin tener que preocuparse por las
complejidades inherentes al proceso de aprobación ni por su funcionamiento.

Este tipo simple de actividad compuesta reutiliza el control de ejecución de una clase base
existente, a menudo la clase SequenceActivity. Se trata de una situación tan habitual que Visual
Studio Extensions para Windows Workflow Foundation incorpora un diseñador visual idóneo
para este tipo de actividad. Basta con agregar una actividad nueva al proyecto en marcha para
disponer del punto de partida de una actividad compuesta derivada de la actividad Sequence y
de la capacidad precisa para definir los pasos del componente reutilizable del mismo modo que
se define un flujo de trabajo. Gracias al explorador de propiedades, se puede cambiar la clase
base a cualquier otra actividad compuesta a fin de alterar la semántica de la ejecución sin dejar
de usar el diseñador para crear la lógica de la actividad. En la siguiente imagen se muestra el
diseñador de actividades que se emplea para crear una actividad compuesta a partir de la
actividad Sequence.

Al encapsular actividades, surge la necesidad de exponer las propiedades de las actividades incluidas
ante los usuarios de la actividad compuesta. En el ejemplo anterior, el usuario de la actividad
compuesta debe ser capaz de suministrar valores para el nombre de usuario, la dirección de correo
electrónico, las funciones y otros datos precisos para la correcta configuración de las actividades
incluidas. Visual Studio Extensions hace que resulte sencillo gracias al mecanismo denominado
promoción de propiedades. En el diseñador de actividades compuestas, al hacer clic con el botón
secundario en una actividad, emerge un menú contextual con una opción que permite promover las
propiedades susceptibles de enlace de la actividad. Si elige esa opción, se declaran las propiedades
de dependencia en la actividad compuesta y se enlazan las propiedades de la actividad incluida a

43
Windows Workflow Foundation 4.0 2011

dichos valores. Con este procedimiento, se exponen las propiedades que se deben establecer sin que
sea necesario escribir el código que conecta los valores. Puede determinar cómo se llamará la
propiedad expuesta para los usuarios mientras mantiene ocultos los detalles de implementación de
la actividad.

En caso de que desee ejercer un mayor control sobre la ejecución de las actividades secundarias,
puede escribir una actividad derivada de CompositeActivity y, en el método Execute, controlar la
programación de dicha ejecución. Consideremos, por ejemplo, la actividad Switch. La actividad IfElse
disponible como parte de .NET Framework 3.0 proporciona el modelo para ejecutar con condiciones
una única rama de actividades. La actividad Parallel ofrece un modelo de ejecución de todas las
ramas. Sólo la actividad ConditionedActivityGroup (CAG) aporta un modelo de ejecución condicional
de varias ramas. Switch es más sencilla que CAG y, además, supone un mecanismo adecuado para
mostrar cómo se controla la ejecución de las actividades secundarias para posibilitar la ejecución
condicional de varias ramas de actividades en función de decisiones de código o reglas.

En la siguiente imagen se ilustra el empleo de la actividad Switch en un flujo de trabajo. Fíjese en que
cada una de las ramas de la actividad cuenta con una propiedad denominada Condition que se
puede establecer en una condición de regla declarativa o una condición de código de la misma
forma que las condiciones de una rama de una actividad IfElse. Sólo cuando hay presente una
condición y ésta se evalúa en True, se ejecuta esa rama de la actividad

Por su propia naturaleza de actividad compuesta, la actividad Switch se compone de diversas


secuencias que se ejecutan de acuerdo con una serie de condiciones. Por lo tanto, el primer paso
consiste en crear otra actividad (la actividad SwitchBranch) que se derive de la actividad
Sequence y que defina una propiedad ActivityCondition llamada Condition. Es precisamente
esta propiedad la que permitirá al usuario establecer la condición de código o regla declarativa
que indicará si se debe ejecutar la secuencia. SwitchBranch aprovecha al máximo su actividad
base, por lo que no proporciona ninguna lógica de ejecución propia.

44
Windows Workflow Foundation 4.0 2011

En el método Execute de la actividad Switch, se comprueban las actividades secundarias


habilitadas y se evalúa la condición. En caso de que haya establecida una condición y de que su
evaluación dé como resultado True, hay que programar la ejecución de dicha rama secundaria.
Para ello, se llama al método ExecuteActivity en ActivityExecutionContext. Aquí radican las
mayores diferencias en cuanto a la administración que practican las actividades compuestas con
respecto a sus secundarias. No importa si se desean programar en paralelo, por orden o por
orden inverso: el método Execute es el punto donde se identifican la programación y las
secundarias que se deben ejecutar o no.

El método Execute de la actividad Switch devuelve el estado Executing al tiempo de ejecución.


Así indica que la actividad está programada, pero que sus secundarias no terminaron aún su
trabajo. Al hacerlo, evita que el tiempo de ejecución ejecute la siguiente actividad hasta que se
notifique que la presente se ha completado. Una vez finalizadas todas las actividades
secundarias, la actividad Switch debe poder notificar su finalización al tiempo de ejecución. Así
pues, antes de programar la ejecución de la rama secundaria, la actividad Switch se registra para
recibir notificación de los cambios en la actividad y, de ese modo, saber cuándo se cierra la
actividad secundaria. A continuación se muestra el código empleado para registrar dicha
notificación. Observe que la notificación de cambios aprovecha la capacidad de supervisión de
cambios en las propiedades de dependencia antes citada. La actividad ha de implementar la
interfaz IActivityEventListener si desea recibir la notificación de los cambios.

foreach(Activity child in EnabledActivities)


{
SwitchBranchActivity childBranch = child as
SwitchBranchActivity;
if (childBranch != null &&
childBranch.Condition.Evaluate(childBranch,
executionContext))
{
childBranch.Closed += OnChildClosed;
executionContext.ExecuteActivity(childBranch);
}
}
return ActivityExecutionStatus.Executing;

En el método OnEvent de la interfaz IActivityEventListener, cuando se notifique el cierre de


una rama secundaria a la actividad, se comprueba si hay alguna secundaria más en ejecución. Si
no la hay, la actividad puede solicitar su cierre a ActivityExecutionContext. En realidad, es lo
mismo que devolver Closed desde el método Execute para dar a conocer al tiempo de ejecución
que la actividad finalizó todas sus tareas. En este punto también son notables las diferencias,
puesto que cada actividad debe determinar las condiciones específicas en las que finaliza su
trabajo.

Es probable que cada actividad compuesta aplique una semántica distinta al modo de ejecutar
las actividades secundarias en función de las necesidades del creador y del caso concretos. El
ejemplo pretende ofrecer una visión general de los asuntos principales que preocupan a los
desarrolladores cuando crean actividades compuestas.

45
Windows Workflow Foundation 4.0 2011

Actividades asíncronas y basadas en eventos

Si bien algunas actividades pueden finalizar su trabajo de una forma sincrónica simple, existen
numerosos casos en los que la actividad inicia alguna tarea y, luego, queda a la espera de una
respuesta. De igual modo, es posible que deba crear una actividad capaz de escuchar si se
provoca algún evento externo como, por ejemplo, la creación de un archivo o un evento del
Instrumental de administración de Windows (WMI). Dada la manera en que se administran los
flujos de trabajo en tiempo de ejecución, no conviene crear directamente en las actividades
controladores de eventos para los recursos externos. Por ejemplo, supongamos que se registra
un controlador de eventos en la actividad relativo a los cambios producidos en el sistema de
archivos. Si el flujo de trabajo entra en un estado inactivo serializado, el controlador también
acabaría en estado de inactividad. Entonces los eventos provocados no llegarían a conocimiento
de la actividad.

Windows Workflow Foundation ofrece una infraestructura que ayuda a paliar este problema de
comunicación con los flujos de trabajo que se mantiene en la actualidad. El modelo de
comunicación se basa en un sistema de colas: por lo general, las actividades se registran para
recibir mensajes en una cola y los servicios de la aplicación host envían mensajes a colas. Las
actividades personalizadas pueden valerse de este modelo tanto para controlar los eventos
externos como para comunicar la finalización de la ejecución de actividades asincrónicas. Así la
actividad se puede ejecutar hasta un punto determinado y, a continuación, esperar un estímulo
para continuar la ejecución. En la siguiente imagen se representa el modelo de comunicación
entre el código de la aplicación host y el código o las actividades del flujo de trabajo.

Con objeto de permitir que la actividad escuche los mensajes que llegan a una cola, es preciso
asegurarse primero de que dicha cola exista. En caso de que no exista, hay que crearla. Esto se
suele llevar a cabo en los métodos Initialize o Execute de la actividad según el momento en que
la cola deba estar disponible para recibir mensajes. WorkflowQueuingService proporciona los
métodos necesarios para crear, buscar o eliminar colas de flujos de trabajo. Por último, la
actividad se debe registrar para recibir estas notificaciones efectuando el registro para el evento
QueueItemAvailable en la propia cola del flujo de trabajo. En cuanto compruebe la existencia
de la cola y se registre para recibir eventos, se enviará una notificación a la actividad siempre
que haya elementos disponibles en la cola para que se puedan obtener y procesar.

Cuando crea una actividad asincrónica, sigue los pasos que acabo de describir con el fin de
preparar la actividad para la recepción de mensajes de la cola; una vez finalizada la actividad
asincrónica, pone en cola un mensaje que notifique la actividad y, si lo desea, que envíe los
datos resultantes. Como el método OnEvent recibe una referencia a ActivityExecutionContext
por mediación del parámetro Sender, es posible cerrar la actividad llamando al método
CloseActivity.

46
Windows Workflow Foundation 4.0 2011

Consideremos, por ejemplo, la actividad simple ReadLine. En este ejemplo, la actividad


ReadLine crea una cola en su método Execute, se registra para la notificación de datos que
lleguen a la cola e implementa la interfaz IActivityEventListener para recibir dicha notificación.
La aplicación host o un servicio de tiempo de ejecución personalizado recupera la entrada del
usuario de la consola y la pone en la cola de la actividad. Se incluye una actividad ReadLine de
ejemplo en el código de este artículo que utiliza ese patrón para su implementación.

Si desea crear una actividad que pueda actuar como receptor de eventos (por ejemplo, la
actividad HandleExternalEvent), también debe implementar la interfaz IEventActivity. Esta
interfaz define las responsabilidades principales de la actividad para escuchar eventos:

public interface IEventActivity


{
void Subscribe(ActivityExecutionContext parentContext,
IActivityEventListener<QueueEventArgs> parentEventHandler);
void Unsubscribe(ActivityExecutionContext parentContext,
IActivityEventListener<QueueEventArgs> parentEventHandler);

IComparable QueueName { get; }


}

La propiedad QueueName ha de devolver un valor IComparable que identifique de forma


exclusiva la actividad al poner en cola los mensajes. Ese mismo nombre de cola empleará el
código que pone en cola el mensaje para el tiempo de ejecución del flujo de trabajo.

Esta interfaz permite indicar a la actividad que se suscriba a eventos antes de ejecutarse y
comunica a la actividad cuándo debe anular la suscripción. En los métodos de suscripción y
anulación de la suscripción, la actividad tiene la responsabilidad de garantizar la creación de una
cola con QueueName, así como su eliminación al final del procesamiento. Asimismo, ésta es la
ocasión apropiada para que la actividad registre información con los servicios locales que
ejecutarán la lógica en su nombre y que responderán poniendo en cola un mensaje.

Un servicio local es una clase que se define y agrega al tiempo de ejecución del flujo de trabajo
desde el host y del que se sirven el código del host, el flujo de trabajo o las actividades. El
servicio local puede mantener controladores de eventos u otras escuchas siempre que la
aplicación host se encuentre en ejecución; además, asegura que el flujo de trabajo obtenga los
datos apropiados poniendo en cola el mensaje. La información que se pasa al servicio local debe
incluir el nombre de la cola, el Id. de la instancia del flujo de trabajo y cualquier otro dato que
necesite el servicio para iniciar el trabajo o devolver un resultado a la actividad.

El código de ejemplo incluye una actividad de evento personalizada que crea una cola y registra
un controlador para recibir una notificación siempre que llegue algún evento a ella. El programa
host se limita a poner en cola un elemento tras un breve período para mostrar este proceso; si se
tratara de una situación real, es probable que el servicio local tuviera que responsabilizarse de
enviar los datos. En cualquier caso, de este modo demostramos que también el host puede poner
en cola datos para las actividades.

En general, al trabajar con actividades basadas en eventos, es preciso comprobar si existen


elementos en la cola del método Execute y, si no hay, suscribirse para determinar la
disponibilidad de elementos en cola y devolver el estado Executing. Entonces, en el controlador

47
Windows Workflow Foundation 4.0 2011

de llegada de elementos, se leen los datos, se quitan de la cola y, de manera opcional, se


provocan los eventos oportunos en la actividad antes de cerrarla.

Mediante la implementación de estas interfaces, la actividad disfruta de la oportunidad de


participar en el flujo de trabajo de distintos modos. Por ejemplo, en un flujo de trabajo de
máquinas de estado, la actividad puede convertirse en el evento que aparece como primera
actividad en una secuencia controlada por eventos; así, el evento puede desencadenar una
secuencia de actividades y, en potencia, una transición a otro estado.

Control de errores

Otra de las responsabilidades que asume el desarrollador de actividades consiste en controlar las
condiciones de error. La administración de errores en las actividades comparte muchas de sus
características con la realizada con cualquier otro código, salvo en que han de tenerse en cuenta
ciertas consideraciones especiales a la hora de administrar la ejecución de las actividades. Si se
produce algún error en la lógica de ejecución de las actividades, hay que generar una excepción
con tantos detalles como sean necesarios, tal como se haría con cualquier otro componente. Así
se indica el error al tiempo de ejecución, el cual puede interactuar con las actividades del flujo
de trabajo con el fin de darles ocasión de cerrarse correctamente o bien puede invocar
controladores especiales de errores.

Cada vez que se genera una excepción desde el código de ejecución de las actividades, se llama
al método HandleFault de la actividad. En el método HandleFault, debe escribir el código
preciso para liberar los recursos adecuados o para cancelar las operaciones asincrónicas
iniciadas por la actividad. Recuerde que la excepción se seguirá generando en el tiempo de
ejecución; este método sirve tan sólo para permitir la limpieza de la actividad. Además, las
actividades compuestas también admiten FaultHandlers, un método secundario adjunto especial
que se ejecuta como utilidad posterior al procesamiento después de que la actividad compuesta
haya pasado del estado de error al estado cerrado. La vista FaultHandlers sirve para modelar el
control de los errores generados desde las actividades secundarias. De este modo, al poner en
relación las excepciones con problemas concretos, se facilita la creación de un código mejor
para el control de errores en el flujo de trabajo.

El comportamiento predeterminado del método HandleFault definido en la clase Activity


consiste en llamar al método Cancel de la actividad. Aunque así se centraliza la limpieza de los
recursos, puede no resultar apropiado en todos los casos, ya que cabe la posibilidad, según el
tipo de excepción generada, de que la actividad cuente con recursos específicos que limpiar. Si
no existe ninguna necesidad específica para controlar los recursos, realice otro registro o
limpieza; luego, el comportamiento predeterminado será suficiente en la mayoría de los casos.

Fíjese en que, en general, la cancelación se usa para implementar una semántica en la que una
actividad compuesta genera varias secundarias por lo que, a fin de cumplir la semántica
deseada, debe cancelar la ejecución continua de algunas secundarias. La cancelación es positiva
y se emplea para permitir la ejecución hacia delante; los errores, en cambio, son negativos y se
utilizan para implementar casos excepcionales. La cancelación y el control de errores son dos
conceptos totalmente diferentes. La implementación de la clase base de HandleFault sólo llama
al método Cancel cuando hay que habilitar escenarios muy básicos.

48
Windows Workflow Foundation 4.0 2011

Experiencia de diseño

Muchas actividades están diseñadas para su uso en interfaces de usuario basadas en modelos,
como el diseñador incluido con Visual Studio Extensions para Windows Workflow Foundation.
La experiencia de diseño de las actividades es, en muchos aspectos, más importante que los
principios fundamentales de ejecución, ya que resulta más visible para los demás
desarrolladores y para los usuarios de la actividad. Windows Workflow Foundation facilita la
visualización de flujos de trabajo en distintas situaciones, no sólo durante el desarrollo, por lo
que reviste vital importancia que la actividad no sólo respete la semántica de sus
responsabilidades sino que, además, resulte lógica y útil para los usuarios.

Varios componentes contribuyen a la experiencia en tiempo de diseño, incluidas la apariencia,


la interactividad y la validación. Estos componentes de diseño constituyen en sí mismos clases
de .NET y se asocian a la actividad mediante atributos personalizados aplicados a la clase
Activity. La primera de estas clases la integra el diseñador de actividades, que ofrece el punto de
entrada principal para que el desarrollador cree una experiencia enriquecida en tiempo de
diseño. La clase del diseñador permite participar en la experiencia de diseño por medio de la
interacción con el explorador de propiedades, los menús contextuales, los clics y los
movimientos del mouse, y el dibujo en sí de la actividad en la superficie de diseño.

Para crear un diseñador, debe crear una clase derivada de la clase base ActivityDesigner o de
cualesquiera de los diseñadores de actividades compuestas disponibles en las bibliotecas de
Windows Workflow Foundation. Éste sería el resultado al aplicar SwitchActivityDesigner a la
actividad Switch:

[Designer(typeof(SwitchActivityDesigner),
typeof(IDesigner))]
public partial class SwitchActivity :
System.Workflow.ComponentModel.CompositeActivity,
IActivityEventListener<ActivityExecutionStatusChangedEventArgs>
...

Centremos en las características clave que se usan más a menudo en los diseñadores
personalizados. La primera de ellas es la capacidad para proporcionar elementos de menús
contextuales personalizados en la actividad que permitan al usuario iniciar una acción. Si omite
la propiedad Verbs en el diseñador, puede especificar la colección de elementos de menú que se
debe agregar al menú contextual de la actividad para que el usuario pueda hacer clic con el
botón secundario o hacer emerger de cualquier otra forma el menú contextual con intención de
iniciar la acción. Por cada verbo agregado a la colección, hay que suministrar un controlador de
eventos al que llamar cuando el usuario elija el elemento del menú. Además, debe pasar un
miembro de la enumeración DesignerVerbGroup que indique cómo se agrupa ese verbo
concreto en el menú contextual. Así puede agrupar actividades relacionadas según su propósito,
por ejemplo, las opciones de edición o las de configuración.

En el código de ejemplo de la actividad Switch, se agrega un verbo que facilita al usuario el


restablecimiento de todas las condiciones en las ramas secundarias. El código para agregar el
verbo al menú contextual es el siguiente:

49
Windows Workflow Foundation 4.0 2011

protected override ActivityDesignerVerbCollection Verbs


{
get
{
ActivityDesignerVerbCollection newVerbs =
new ActivityDesignerVerbCollection();
newVerbs.AddRange(base.Verbs);
newVerbs.Add(new ActivityDesignerVerb(this,
DesignerVerbGroup.Edit, "Clear all conditions",
new EventHandler(ClearConditions)));
return newVerbs;
}
}

Aparte de la propiedad Verbs, también cabe la posibilidad de omitir la propiedad


DesignerVerbs, la cual permite agregar verbos al menú contextual de errores para indicar
problemas con la configuración activa. Por último, se puede omitir la propiedad
SmartTagVerbs, que permite agregar elementos a la etiqueta inteligente que aparece bajo el
nombre de casi todas las actividades compuestas. En general, este elemento sólo se emplea si es
necesario presentar una vista alternativa de la superficie de diseño similar a las vistas de
cancelación y control de errores.

Probablemente, la necesidad más común de tiempo de diseño que acucia a los desarrolladores
de actividades radica en la capacidad para personalizar la apariencia de las actividades en la
superficie de diseño. Windows Workflow Foundation ofrece varios mecanismos para crear la
apariencia deseada: desde opciones muy simples para establecer sólo algunas propiedades, hasta
las más avanzadas que permiten pintar la actividad directamente con un objeto
System.Drawing.Graphics.

Con respecto a la apariencia de una actividad de un flujo de trabajo, lo primero que se debe
comprender es que forma parte de lo que se denomina tema de diseñador. Un tema no es ni más
ni menos que una definición del aspecto que ha de tener cada actividad en el diseñador. Si tiene
instalado el producto Visual Studio Extensions para Windows Workflow Foundation y elige
Opciones en el menú Herramientas, verá una categoría para Workflow Designer con dos temas
instalados. Haga clic en el botón de creación para conocer las propiedades disponibles para cada
actividad. Los temas se pueden guardar y aplicar en el diseñador de Visual Studio pero, además,
también se pueden usar si se realoja el control del diseñador de flujos de trabajo en la aplicación
oportuna. Los temas permiten cambiar por completo la superficie de diseño de flujos de trabajo,
así como cada una de las actividades alojadas en el diseñador.

El tema predeterminado del diseñador inspecciona la actividad y le ofrece la opción de


suministrar información del tema. Al desarrollar actividades, realiza esta tarea asociando un
valor predeterminado de DesignerTheme al diseñador personalizado por medio de un atributo.
En el constructor del tema del diseñador se pueden establecer valores para propiedades tales
como BackColorStart, BackColorEnd, ForeColor o BackgroundStyle a fin de cambiar los
colores de la actividad y de su texto. CreateUserActivityDesignerTheme proporciona un
ejemplo de actualización de dichos valores:

50
Windows Workflow Foundation 4.0 2011

public class CreateUserActivityDesignerTheme : ActivityDesignerTheme


{
public CreateUserActivityDesignerTheme(WorkflowTheme theme)
: base(theme)
{
this.BackColorEnd = Color.BurlyWood;
this.BackColorStart = Color.Bisque;
this.BackgroundStyle = LinearGradientMode.ForwardDiagonal;
this.ForeColor = Color.Black;
}
}

Además del color y del diseño, se puede especificar que se dibuje un icono en una posición
predeterminada de la actividad. El icono se encuentra conectado a la actividad con el atributo
ToolboxBitmap y especifica un archivo de imagen (suele ser un archivo PNG o de mapa de bits
de 16×16) integrado en el ensamblado a modo de recurso incrustado. Además de utilizarse en el
diseñador, esta misma imagen aparece al agregar la actividad al cuadro de herramientas ya sea
de forma manual o con un instalador, algo lógico por otra parte teniendo en cuenta el nombre
del atributo. En la siguiente imagen se muestra la actividad CreateUser dentro de un flujo de
trabajo junto con el tema y el icono.

Durante la creación de diseñadores para actividades compuestas, no sólo debe preocuparse por
los temas y los iconos, sino también por trazar conectores entre los diseñadores de actividades
secundarias o por cambiar de cualquier otro modo la apariencia de inclusión de esas
secundarias. No obstante, existen varias clases base que ofrecen la funcionalidad precisa para

51
Windows Workflow Foundation 4.0 2011

reducir la cantidad de código que se debe escribir. En el caso de la actividad Switch, por
ejemplo, el diseñador se deriva de ParallelActivityDesigner, que permite dibujar las actividades
secundarias como ramas y crea un verbo de diseñador para agregar ramas nuevas.

Así, el diseñador de la actividad Switch sólo tiene que omitir un método y devolver una
actividad SwitchBranchActivity nueva. Este diseñador demuestra su utilidad en las actividades
compuestas con ramas; se emplea de forma interna en las actividades IfElse, Parallel y Listen.

La clase base para la clase ParallelActivityDesigner es StructuredCompositeActivityDesigner, la


cual proporciona gran parte de la lógica de procesamiento para los conectores si la actividad
compuesta contiene otros diseñadores.

Otra clase del mismo nivel de StructuredCompositeActivityDesigner es


FreeFormActivityDesigner. Este diseñador constituye la base para el diseñador de flujos de
trabajo StateMachine y se emplea cuando las actividades secundarias se pueden colocar
libremente (sin restricciones estructurales) en la actividad compuesta. Básese en uno de estos
dos diseñadores para crear el suyo propio.

Un último apunte: al crear una actividad compuesta con el diseñador de actividades, se muestra
en la superficie de diseño con una representación de todas sus actividades secundarias; por lo
tanto, el usuario de la actividad compuesta verá todas las actividades internas de la
implementación. Si es lo que pretende, no pasa nada pero, de lo contrario, puede solucionarlo
creando un diseñador para la actividad compuesta que se derive de la clase base
ActivityDesigner. Así se ofrece la representación sencilla de una única actividad. Si lo desea,
adjunte un tema de diseñador o atributos ToolboxBitmap para personalizar más el aspecto y
ofrecer una auténtica encapsulación.

Validación
A la hora de crear una actividad o cualquier otro componente, resulta recomendable validar la
entrada del usuario y asegurarse de que la configuración del componente es correcta. De hecho,
lo ideal sería poder impedir que el usuario configure de manera incorrecta el componente.
Existen dos áreas clave de desarrollo donde se ofrece la oportunidad de garantizar la correcta
configuración de la actividad: en el diseñador y con un validador personalizado de la actividad.

Cuando se crea un diseñador personalizado para la actividad, se controlan tanto las actividades
susceptibles de adición a la actividad compuesta como las actividades a las que se puede agregar
la actividad en cuestión. Es importante apuntar que este comportamiento sólo se aplica al
entorno de diseño, es decir, que no desempeña papel alguno al agregar la actividad mediante
código.

Si se omite el método CanBeParentedTo en la clase base ActivityDesigner, es posible filtrar los


tipos de actividades a los que puede agregarse la actividad. Cuando se devuelve False de este
método, los usuarios ven una barra diagonal de color rojo (el símbolo internacional para No), lo
que indica que no se puede agregar la actividad. La actividad SwitchBranch, por ejemplo, sólo
puede tener como actividad primaria SwitchActivity; de este modo se garantiza su ejecución en
el contexto correcto.

La omisión del método CanInsertActivities de la clase CompositeActivityDesigner permite


controlar si una actividad o un conjunto de actividades en particular se puede agregar a la

52
Windows Workflow Foundation 4.0 2011

actividad compuesta. El uso de los dos métodos anteriores facilita el control de la estructura de
actividades relacionada con su actividad. Esto reviste especial importancia al comprobar las
necesidades de ejecución de la actividad, porque ofrece la oportunidad de garantizar que su
configuración permite la correcta ejecución.

La otra opción para validar la actividad consiste en crear un validador personalizado. La tarea de
la clase del validador consiste en inspeccionar la actividad y comprobar que su configuración
respete los requisitos. Esta validación tiene lugar aunque se emplee un diseñador para agregar la
actividad al flujo de trabajo. A estas alturas, es probable que ya haya adivinado cómo se crea un
validador: debe crear una clase que se derive de ActivityValidator y, a continuación, omitir el
método Validate; luego, ha de usar ActivityValidatorAttribute para conectar el validador con la
actividad.

Aunque la actividad CreateUser cuenta con una propiedad para alojar la contraseña, no es
recomendable configurar la contraseña de forma estática en el flujo de trabajo. En lugar de
hacerlo así, el componente de validación garantiza que la propiedad de contraseña esté enlazada
a algún otro valor para que su valor se establezca en tiempo de ejecución. En la figura 9 se
muestra cómo prueba la propiedad la clase CreateUserValidator a fin de asegurar que esté
enlazada.

public override ValidationErrorCollection Validate(


ValidationManager manager, object obj)
{
ValidationErrorCollection baseErrors = base.Validate(manager, obj);

CreateUserActivity user = obj as CreateUserActivity;


if (user == null) throw new ArgumentException(
"Activity must be a CreateUser activity");

// make sure we are in a valid designer experience


if (user.Parent != null &&
!user.IsBindingSet(CreateUserActivity.PasswordProperty))
{
baseErrors.Add(new ValidationError(
"Password must be set using activity binding",
1, false, "Password"));
baseErrors.Add(new ValidationError("Oops", 2, true));
}
return baseErrors;
}

El código valida en primer lugar que la actividad sea del tipo correcto y que se esté validando en
otro contexto que no sea el de su propia compilación. Si no comprueba si la propiedad primaria
tiene un valor Null, no podrá crear siquiera la actividad, ya que el validador se ejecutará al
compilarla. ValidationErrorCollection sirve para devolver la recopilación de errores al
diseñador de manera que se agreguen los mensajes de error adecuados a la interfaz de usuario.

53
Windows Workflow Foundation 4.0 2011

DISEÑADORES ALOJADOS
¿Qué pasa si quieres crear una actividad personalizada? Muchos de los productos permiten a los
usuarios finales personalizar flujos de trabajo. Windows Workflow Foundation 4 le permite
acoger el diseñador en una aplicación con bastante facilidad.

1. Añadir una nueva aplicación WPF, para ello seguir los siguientes pasos:

a. Botón derecho en la solución con la que hemos estado trabajando hasta ahora y
añadir un nuevo proyecto

b. Desde la ventana de plantillas, seleccionar una aplicación WPF

c. Establecer el proyecto como proyecto de inicio

d. Añadir las siguientes referencias

i. System.Activities.Presentation

ii. System.Activities.Core.Presentation

iii. System.Activities

e. Desde la pestaña proyectos, añadir los siguientes ensamblados:

i. HolaMundo.Ativities

ii. HolaMundo.Activities.Designers

f. Abrir MainWindow.xaml y modificarlo como se muestra a continuación:

<Window x:Class="HelloDesigner.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation
"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="1000">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Name="grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
<TextBox Grid.Row="1" Name="textXAML"
VerticalScrollBarVisibility="Visible" />

54
Windows Workflow Foundation 4.0 2011

</Grid>
</Window>

g. Abrir MainWindow.xaml.cs y añadir las siguientes directivas:

using System.Activities.Presentation;
using System.Activities.Statements;
using System.Activities.Presentation.Toolbox;
using System.Activities.Core.Presentation;
using System.Activities.Presentation.Metadata;
using System.ComponentModel;
using HelloWorkflow.Activities;
using HelloWorkflow.Activities.Designers;

h. Añadir un campo de tipo WorkflowDesigner

public partial class MainWindow : Window{ WorkflowDesigner workflowDes


igner = new WorkflowDesigner();

i. Crear una nueva función RegisterMetadata como se muestra. Esta función


permite al diseñador almacenar los metadatos.

private void RegisterMetadata()


{
DesignerMetadata metaData = new DesignerMetadata();
metaData.Register();
AttributeTableBuilder builder = new AttributeTableBuilder
();
MetadataStore.AddAttributeTable(builder.CreateTable());
}

Cuando usted aloja el diseñador puede controlar la caja de herramientas. Elige lo que los
controles van a aparecer, las categorías que aparecen en el e incluso los nombres de los
controles. Agregue la función CreateToolboxControl según las indicaciones.

private ToolboxControl CreateToolboxControl()


{
return new ToolboxControl()
{
Categories = {
new ToolboxCategory("Hello Workflow")
{
Tools =
{
new ToolboxItemWrapper( "System.Activities.Statements.Ass
ign", "System.Activities, Version=4.0.0.0, Culture=neutral, Publ
icKeyToken=31bf3856ad364e35", null, "Assign"), new ToolboxItemW
rapper( "System.Activities.Statements.Sequence", "System.Activit
ies, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad
364e35", null, "Sequence"), new ToolboxItemWrapper( "System.Acti
vities.Statements.TryCatch", "System.Activities, Version=4.0.0.0
, Culture=neutral, PublicKeyToken=31bf3856ad364e35", null, "Try

55
Windows Workflow Foundation 4.0 2011

It"), new ToolboxItemWrapper( "HolaMundo.Activities.PrePostSeque


nce", "HolaMundo.Activities", null, "PrePostSequence"), new Tool
boxItemWrapper( "HolaMundo.Activities.DiagnosticTrace", "HolamUn
do.Activities", null, "Diagnostic Trace") } } } };
}

j. La venta del diseñador muestra el XAML del flujo de trabajo. Para hacer esto
necesitamos una función que maneje el evento ModelChanged. Añadir la
función ShowWorkflowXAML como se muestra.

private void ShowWorkflowXAML(object sender, EventArgs e)


{
workflowDesigner.Flush();
textXAML.Text = workflowDesigner.Text;
}

k. Cree la función AddDesigner como se muestra. Esta función se agrega el


diseñador a la ventana.

private void AddDesigner()


{
this.workflowDesigner = new WorkflowDesigner();
Grid.SetColumn(this.workflowDesigner.View, 1);
workflowDesigner.ModelChanged += ShowWorkflowXAML;
this.workflowDesigner.Load(new Sequence());
grid1.Children.Add(this.workflowDesigner.View);
Grid.SetColumn(workflowDesigner.PropertyInspectorView, 2);
grid1.Children.Add(workflowDesigner.PropertyInspectorView);
ToolboxControl tc = CreateToolboxControl();
Grid.SetColumn(tc, 0);
grid1.Children.Add(tc);
ShowWorkflowXAML(null, null);
}

l. Modificar el constructor para llamar las funciones que hemos añadido:

public MainWindow()
{
InitializeComponent();
RegisterMetadata();
AddDesigner();
}

Si ejecutamos el proyecto tendremos lo siguiente:

56
Windows Workflow Foundation 4.0 2011

57
Windows Workflow Foundation 4.0 2011

EJEMPLOS

1. Abra Visual Studio 2010 y elija Nuevo, Proyecto en el menú Archivo.

2. Expanda el nodo Otros tipos de proyectos en la lista Plantillas instaladas y seleccione


Soluciones de Visual Studio.

3. Seleccione Solución en blanco en la lista Soluciones de Visual Studio. Asegúrese de


que se haya seleccionado .NET Framework 4 en la lista desplegable correspondiente a
la versión de .NET Framework. Escriba GettingStartedTutorial en el cuadro Nombre
y, a continuación, haga clic en Aceptar.

4. Haga clic con el botón secundario en GettingStartedTutorial en el Explorador de


soluciones y elija Agregar, Nuevo proyecto

5. En la lista Plantillas instaladas, seleccione Visual C#, Flujo de trabajo o Visual


Basic, Flujo de trabajo. Asegúrese de que se haya seleccionado .NET Framework 4
en la lista desplegable correspondiente a la versión de .NET Framework. Seleccione
Biblioteca de actividad en la lista Flujo de trabajo. Mantenga la configuración
predeterminada y, a continuación, haga clic en Aceptar.

6. Haga clic con el botón secundario en Activity1.xaml en el Explorador de soluciones y


seleccione Eliminar. Haga clic en Aceptar para confirmar

Para crear la actividad ReadInt

1. Elija Agregar nuevo elemento en el menú Proyecto.

2. En la lista Plantillas instaladas, seleccione Flujo de trabajo. Seleccione Actividad de


código en la lista Flujo de trabajo.

3. Escriba ReadInt en el cuadro Nombre y, a continuación, haga clic en Agregar.

4. Reemplace la definición de ReadInt existente con la siguiente.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
namespace ActivityLibrary1
{

public sealed class ReadInt : NativeActivity<int>


{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }

protected override void Execute(NativeActivityContext context)


{
string name = BookmarkName.Get(context);
if (name == string.Empty)
{

58
Windows Workflow Foundation 4.0 2011

throw new ArgumentException("BookmarkName cannot be an Empty string.",


"BookmarkName");
}
context.CreateBookmark(name, new BookmarkCallback(OnReadComplete));
}

protected override bool CanInduceIdle { get { return true; } }

void OnReadComplete(NativeActivityContext context, Bookmark bookmark,


object state) { this.Result.Set(context, Convert.ToInt32(state)); }
}
}

Para crear la actividad Prompt

1. Presione F6 para compilar el proyecto. De esta forma se habilita la actividad ReadInt en


este proyecto que se va a usar para compilar la actividad personalizada desde este paso.

2. Elija Agregar nuevo elemento en el menú Proyecto.

3. En la lista Plantillas instaladas, seleccione Flujo de trabajo. Seleccione Actividad en


la lista Flujo de trabajo.

4. Escriba Prompt en el cuadro Nombre y, a continuación, haga clic en Agregar.

5. Haga doble clic en Prompt.xaml en el Explorador de soluciones para que se muestre


en el diseñador, si aún no aparece.

6. Haga clic en el botón Argumentos, situado en la parte inferior izquierda del diseñador
de actividad, para mostrar el recuadro Argumentos.

7. Haga clic en Crear argumento.

8. Escriba BookmarkName en el cuadro Nombre, seleccione Entrada en la lista


desplegable Dirección, elija Cadena en la lista desplegable Tipo de argumento y
después presione ENTRAR para guardar el argumento.

9. Haga clic en Crear argumento.

10. Escriba Result en el cuadro Nombre que se encuentra debajo del argumento
BookmarkName recién agregado, seleccione Salida en la lista desplegable Dirección,
seleccione Int32 en la lista desplegable Tipo de argumento y, a continuación, presione
ENTRAR.

11. Haga clic en Crear argumento.

12. Escriba Text en el cuadro Nombre, seleccione Entrada en la lista desplegable


Dirección, seleccione Cadena en la lista desplegable Tipo de argumento y, a
continuación, presione Entrar para guardar el argumento.

Estos tres argumentos se enlazan a los argumentos correspondientes de las actividades


WriteLine y ReadInt que se agregan a la actividad Prompt en los siguientes pasos.

13. Haga clic en el botón Argumentos en el lado inferior izquierdo del diseñador de
actividad para cerrar el recuadro Argumentos.

14. Arrastre una actividad Sequence de la sección Flujo de control del cuadro de
herramientas y colóquela en el diseñador de actividad

59
Windows Workflow Foundation 4.0 2011

15. Arrastre una actividad WriteLine de la sección Primitivas del cuadro de


herramientas y colóquela en la actividad Sequence.

16. Enlace el argumento Text de la actividad WriteLine al argumento Text de la actividad


Prompt escribiendo Text en el cuadro Escriba una expresión de VB en la ventana
Propiedades y, a continuación, presione dos veces la tecla TAB para descartar la
ventana de la lista de miembros de IntelliSense y guarde el valor de propiedad
moviendo la selección fuera de la propiedad. Esta propiedad también se puede
establecer escribiendo Text en el cuadro Escriba una expresión de VB en la propia
actividad.

17. Arrastre una actividad ReadInt de la sección ActivityLibrary1 del cuadro de


herramientas y colóquela en la actividad Sequence para que siga la actividad
WriteLine.

18. Enlace el argumento BookmarkName de la actividad ReadInt al argumento


BookmarkName de la actividad Prompt escribiendo BookmarkName en el cuadro
Escriba una expresión de VB a la derecha del argumento BookmarkName en la
Ventana Propiedades y, a continuación, presione dos veces la tecla TAB para cerrar la
ventana de lista de miembros de IntelliSense y guarde la propiedad.

19. Enlace el argumento Result de la actividad ReadInt al argumento Result de la


actividad Prompt escribiendo Result en el cuadro Escriba una expresión de VB a la
derecha del argumento Result en la Ventana Propiedades y, a continuación, presione
la tecla TAB dos veces.

20. Presione F6 para compilar la solución.

Para crear el proyecto de flujo de trabajo

1. Abra la solución del tema anterior Cómo: Crear una actividad con Visual Studio 2010.

2. Haga clic con el botón secundario en la solución GettingStartedTutorial en el


Explorador de soluciones y seleccione Agregar, Nuevo proyecto.

3. En la lista Plantillas instaladas, seleccione Visual C#, Flujo de trabajo o Visual


Basic, Flujo de trabajo.

Asegúrese de que se haya seleccionado .NET Framework 4 en la lista desplegable


correspondiente a la versión de .NET Framework. Seleccione Aplicación de consola de flujos
de trabajo en la lista Flujo de trabajo. Mantenga la configuración predeterminada y haga clic
en Aceptar. Así se crea una aplicación de flujo de trabajo de inicio con soporte básico de
hospedaje de flujo de trabajo. En este tema, el flujo de trabajo se rellena con actividades. En el
tema siguiente, Cómo: Ejecutar un flujo de trabajo, se modifica y se usa el código de hospedaje
básico para ejecutar la aplicación de flujo de trabajo.

4. Haga clic con el botón secundario en el elemento WorkflowConsoleApplication1 que


se acaba de agregar en el Explorador de soluciones y seleccione Agregar referencia.
Seleccione ActivityLibrary1 en la pestaña Proyectos y haga clic en Aceptar.

5. Presione F6 para compilar la solución. De esta forma se agregan actividades


personalizadas de ActivityLibrary1 en el Cuadro de herramientas de manera que se
puedan usar en este flujo de trabajo.

60
Windows Workflow Foundation 4.0 2011

Para crear las variables y argumentos de flujo de trabajo


1. Haga doble clic en Workflow1.xaml en el Explorador de soluciones para que el flujo
de trabajo se muestre en el diseñador, si aún no aparece.

2. Arrastre una actividad Flowchart desde la sección Diagrama de flujo del cuadro de
herramientas y colóquela en la superficie de diseño de flujo de trabajo.

3. Haga clic en Argumentos en la parte inferior izquierda del diseñador de flujo de trabajo
para mostrar el recuadro Argumentos.

4. Haga clic en Crear argumento.

5. Escriba MaxNumber en el cuadro Nombre, seleccione En en la lista desplegable


Dirección, seleccione Int32 en la lista desplegable Tipo de argumento y, a
continuación, presione Entrar para guardar el argumento.

6. Haga clic en Crear argumento.

7. Escriba Turns en el cuadro Nombre que se encuentra debajo del argumento


MaxNumber recién agregado, seleccione Salida en la lista desplegable Dirección,
seleccione Int32 en la lista desplegable Tipo de argumento y, a continuación, presione
ENTRAR.

8. Haga clic en el botón Argumentos en el lado inferior izquierdo del diseñador de


actividad para cerrar el recuadro Argumentos.

9. Haga clic en Variables en el lado inferior izquierdo del diseñador de flujo de trabajo
para mostrar el recuadro Variables.

10. Haga clic en Crear variable.

Para agregar actividades de flujo de trabajo

1. Arrastre una actividad Assign de la sección Primitivas del cuadro de herramientas y


colóquela en el flujo de trabajo de modo que esté debajo del nodo de inicio
(representado por un círculo verde) en la parte superior del flujo de trabajo. Escriba
Target en el cuadro Para y la siguiente expresión en el cuadro Escriba una expresión
de VB.
New System.Random().Next(1, MaxNumber + 1)

2. Mantenga el mouse sobre el nodo de inicio en la parte superior del flujo de trabajo para
indicar que la actividad Assign es el punto de partida de Flowchart. Haga clic en uno
de los rectángulos que aparecen cuando el mouse se desplaza por el nodo de inicio y
arrástrelo a la parte de superior de la actividad Assign. Cuando el mouse está sobre la
actividad Assign, aparecen cuatro rectángulos. Arrástrelo para que la línea de conexión
de la parte inferior del nodo de inicio se conecte con el rectángulo superior de la
actividad Assign y, a continuación, suelte el botón del mouse.

3. Arrastre una actividad Prompt de la sección ActivityLibrary1 del cuadro de


herramientas y colóquela debajo de la actividad Assign del paso anterior.

61
Windows Workflow Foundation 4.0 2011

4. En la ventana Propiedades, escriba "EnterGuess", incluidas las comillas, en el cuadro


de valor de propiedad BookmarkName. Escriba Guess en el cuadro de valor de
propiedad Resultado y escriba la siguiente expresión en el cuadro de propiedad Texto.

"Please enter a number between 1 and " & MaxNumber

5. Conecte la actividad Assign a la actividad Prompt. Para ello, mantenga el mouse en la


actividad Assign y haga clic en el rectángulo en la parte inferior que aparece cuando se
coloca el mouse encima de la actividad. Arrastre el mouse hacia abajo hasta la parte
superior de la actividad Prompt. Cuando el mouse está sobre la actividad Prompt,
aparecen cuatro rectángulos. Arrastre el mouse para que la línea de conexión de la parte
inferior de la actividad Assign se conecte con el rectángulo superior de la actividad
Prompt y, a continuación, suelte el botón del mouse.

6. A rrastre una actividad Assign de la sección Primitivas del cuadro de herramientas y


colóquela para que esté debajo de la actividad Prompt.

7. Escriba Turns en el cuadro Para y Turns + 1 en el cuadro Escriba una expresión de


VB.

8. Siga los pasos anteriores para conectar la actividad Prompt a la actividad Assign.

9. Arrastre FlowDecision desde la sección Diagrama de flujo del cuadro de


herramientas y colóquela debajo de la actividad Assign. Conecte la actividad Assign a
la actividad FlowDecision y, a continuación, haga clic en la actividad FlowDecision
para seleccionarla. En la Ventana Propiedades, escriba la siguiente expresión en el
cuadro del valor de la propiedad Condición.
Guess = Target

10. A rrastre otra actividad FlowDecision del cuadro de herramientas y colóquela debajo
de la primera. Conecte las dos actividades al arrastrar desde el rectángulo con la etiqueta
Falso en la actividad FlowDecision superior con rectángulo en la parte superior de la
segunda actividad FlowDecision

11. Haga clic en la segunda actividad FlowDecision para seleccionarla. En la Ventana


Propiedades, escriba la siguiente expresión en el cuadro del valor de la propiedad
Condición.

Guess < Target

12. Arrastre dos actividades WriteLine de la sección Primitivas del cuadro de


herramientas y colóquelas para que estén una junto a otra debajo de las dos actividades
FlowDecision. Conecte la acción Verdadero de la actividad FlowDecision inferior a la
actividad WriteLine situada más a la izquierda y la acción Falso a la actividad
WriteLine situada más a la derecha.

13. Haga clic en la actividad WriteLine situada más a la izquierda para seleccionarla y
escriba la siguiente expresión en el cuadro de valor de la propiedad Texto en la
Ventana Propiedades.

"Your guess is too low."

14. Conecte WriteLine al lado izquierdo de la actividad Prompt que está sobre ella.

62
Windows Workflow Foundation 4.0 2011

15. Haga clic en la actividad WriteLine situada más a la derecha para seleccionarla y
escriba la siguiente expresión en el cuadro de valor de la propiedad Texto en la
Ventana Propiedades.

"Your guess is too high."

16. Conecte la actividad WriteLine al lado derecho de la actividad Prompt que está sobre
ella.

En el siguiente ejemplo se muestra el flujo de trabajo completado.

Para ejecutar el proyecto

Modificar Program con el siguiente código

using System;
using System.Linq;
using System.Activities;
using System.Activities.Statements;
using System.Threading;
using System.Collections.Generic;

namespace WorkflowConsoleApplication1
{

class Program
{

63
Windows Workflow Foundation 4.0 2011

static void Main(string[] args)


{

//Notifica que se ha producido un evento a un subproceso en espera


//En el contructor false, es paro no incializar el estado AutoResetEvent

syncEvent = new AutoResetEvent(false);


AutoResetEvent idleEvent = new AutoResetEvent(false);

//Creamos las entradas


var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } };

//Proporciona un Host para una instancia de un flujo de trabajo


WorkflowApplication wfApp = new WorkflowApplication(new Workflow1(),
inputs);

//Se llama cuando el flujo detrabajo se completa


wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{

//variable entera donde se almacena el resultado


int Turns = Convert.ToInt32(e.Outputs["Turns"]);
Console.WriteLine("Felicitaciones, Adivinó el número en el {0} turno.",
Turns);
syncEvent.Set();
};

//se llama cuando se cancela la instancia del flujo de trabajo


wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
{
Console.WriteLine(e.Reason); syncEvent.Set();
};

//cuando la instancia del flujo de trabajo encuentra una excepción


wfApp.OnUnhandledException =
delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Console.WriteLine(e.UnhandledException.ToString());
return UnhandledExceptionAction.Terminate;
};

//se llama cuando un flujo de trabajo se queda inactivo


wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
{
idleEvent.Set();
};

//Comienza o reanuda la ejecucción de un flujo de trabajo


wfApp.Run();

// bucle hasta que el flujo detrabajo se complete


WaitHandle[] handles = new WaitHandle[]
{
syncEvent, idleEvent
};
while (WaitHandle.WaitAny(handles) != 0)
{

// Reúna la entrada del usuario y reanudar el marcador.


bool validEntry = false; while (!validEntry)
{
int Guess;
if (!Int32.TryParse(Console.ReadLine(), out Guess))
{
Console.WriteLine("Por favor introdudza un entero.");
}
else

64
Windows Workflow Foundation 4.0 2011

{
validEntry = true; wfApp.ResumeBookmark("EnterGuess", Guess);
}
}
}
}
}
}

Cuando el flujo de trabajo se inicia:

1. A través de una actividad Asign se inicia la variable Target en un número aleatorio


llamando a Random entre 1 y el MaxNumber más 1.

2. A continuación pasamos a la actividad prompt.

3. Es una actividad de Secuencia que en su interior muestra el contenido del argumento


Text y a continuación llama a la actividad ReadInt

Para crear un diseñador de actividad personalizado con un área de


colocación mediante WorkflowItemPresenter

1. Inicie Visual Studio 2010.

2. En el menú Archivo, elija Nuevo y después seleccione Proyecto.

1. Aparece el cuadro de diálogo Nuevo proyecto.

2. En el recuadro Plantillas instaladas, seleccione Windows en la categoría de lenguaje


preferido.

3. En el recuadro Plantillas, seleccione Aplicación WPF.

4. En el cuadro Nombre, escriba UsingWorkflowItemPresenter.

5. En el cuadro Ubicación, escriba el directorio en el que desea guardar el proyecto o haga clic
en Examinar para navegar hasta él.

6. En el cuadro Solución, acepte el valor predeterminado.

7. Haga clic en Aceptar.

8. Haga clic con el botón secundario del mouse en el archivo MainWindows.xaml en el


Explorador de soluciones, seleccione Eliminar y confirme con Aceptar en el cuadro de
diálogo Microsoft Visual Studio.

9. Haga clic con el botón secundario del mouse en el proyecto UsingWorkflowItemPresenter en


el Explorador de soluciones, seleccione Agregar, a continuación Nuevo Elemento… para
tener acceso al cuadro de diálogo Agregar nuevo elemento y seleccione la categoría WPF
de la sección Plantillas instaladas de la izquierda.

10. Seleccione la plantilla Ventana (WPF), asígnele el nombre RehostingWFDesigner y haga


clic en Agregar.

65
Windows Workflow Foundation 4.0 2011

11. Abra el archivo RehostingWfDesigner.xaml y pegue el siguiente código en él para definir la


interfaz de usuario de la aplicación.

<Window
x:Class="Microsoft.Samples.UsingWorkflowItemPresenter.RehostingWfDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sapt="clr-
namespace:System.Activities.Presentation.Toolbox;assembly=System.Activities.Pr
esentation" xmlns:sys="clr-namespace:System;assembly=mscorlib" Title="Window1"
Height="600" Width="900"> <Window.Resources> <sys:String
x:Key="AssemblyName">System.Activities, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35</sys:String> </Window.Resources> <Grid>
<Grid.ColumnDefinitions> <ColumnDefinition Width="2*"/> <ColumnDefinition
Width="7*"/> <ColumnDefinition Width="3*"/> </Grid.ColumnDefinitions> <Border
Grid.Column="0"> <sapt:ToolboxControl Name="Toolbox"> <sapt:ToolboxCategory
CategoryName="Basic"> <sapt:ToolboxItemWrapper AssemblyName="{StaticResource
AssemblyName}" > <sapt:ToolboxItemWrapper.ToolName>
System.Activities.Statements.Sequence </sapt:ToolboxItemWrapper.ToolName>
</sapt:ToolboxItemWrapper>
<sapt:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}">
<sapt:ToolboxItemWrapper.ToolName> System.Activities.Statements.WriteLine
</sapt:ToolboxItemWrapper.ToolName> </sapt:ToolboxItemWrapper>
<sapt:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}">
<sapt:ToolboxItemWrapper.ToolName> System.Activities.Statements.If
</sapt:ToolboxItemWrapper.ToolName> </sapt:ToolboxItemWrapper>
<sapt:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}">
<sapt:ToolboxItemWrapper.ToolName> System.Activities.Statements.While
</sapt:ToolboxItemWrapper.ToolName> </sapt:ToolboxItemWrapper>
</sapt:ToolboxCategory> </sapt:ToolboxControl> </Border> <Border
Grid.Column="1" Name="DesignerBorder"/> <Border Grid.Column="2"
Name="PropertyBorder"/> </Grid> </Window>

Para asociar un diseñador de actividad a un tipo de actividad, debe registrar ese diseñador
de actividad con el almacén de metadatos. Para ello, agregue el método RegisterMetadata
a la clase RehostingWFDesigner. Dentro del ámbito del método RegisterMetadata, cree un
objeto AttributeTableBuilder y llame al método AddCustomAttributes para agregar los
atributos a él. Llame al método AddAttributeTable para agregar AttributeTable al almacén
de metadatos. El siguiente código contiene la lógica de rehospedaje para el diseñador.
Registra los metadatos, coloca SimpleNativeActivity en el cuadro de herramientas y crea el
flujo de trabajo. Coloque este código en el archivo RehostingWFDesigner.xaml.cs.

using System;
using System.Activities.Core.Presentation;
using System.Activities.Presentation;
using System.Activities.Presentation.Metadata;
using System.Activities.Presentation.Toolbox;
using System.Activities.Statements;
using System.ComponentModel;
using System.Windows; namespace UsingWorkflowItemPresenter
{
// Interaction logic for RehostingWFDesigner.xaml
public partial class RehostingWFDesigner
{
public RehostingWFDesigner()
{
InitializeComponent();
}

protected override void OnInitialized(EventArgs e)


{
base.OnInitialized(e);

66
Windows Workflow Foundation 4.0 2011

// register metadata (new DesignerMetadata()).Register();


RegisterCustomMetadata(); // add custom activity to toolbox
Toolbox.Categories.Add(new ToolboxCategory("Custom activities"));
Toolbox.Categories[1].Add(new
ToolboxItemWrapper(typeof(SimpleNativeActivity))); // create the workflow
designer WorkflowDesigner wd = new WorkflowDesigner(); wd.Load(new
Sequence()); DesignerBorder.Child = wd.View; PropertyBorder.Child =
d.PropertyInspectorView; }
void RegisterCustomMetadata() { AttributeTableBuilder builder = new
AttributeTableBuilder();
builder.AddCustomAttributes(typeof(SimpleNativeActivity), new
DesignerAttribute(typeof(SimpleNativeDesigner)));
MetadataStore.AddAttributeTable(builder.CreateTable()); } } }

1. Haga clic con el botón secundario en el directorio Referencias en el Explorador de soluciones


y seleccione Agregar referencia… para tener acceso al cuadro de diálogo Agregar
referencia.

2. Haga clic en la pestaña .NET, localice el ensamblado denominado


System.Activities.Core.Presentation, selecciónelo y haga clic en Aceptar.

3. Con el mismo procedimiento, agregue referencias a los siguientes ensamblados:

System.Data.DataSetExtensions.dll

System.Activities.Presentation.dll

System.ServiceModel.Activities.dll

1. Abra el archivo App.xaml y cambie el valor de StartUpUri a "RehostingWFDesigner.xaml".

2. Haga clic con el botón secundario del mouse en el proyecto UsingWorkflowItemPresenter en


el Explorador de soluciones, seleccione Agregar, a continuación Nuevo Elemento… para
tener acceso al cuadro de diálogo Agregar nuevo elemento y seleccione la categoría Flujo
de trabajo de la sección Plantillas instaladas de la izquierda.

3. Seleccione la plantilla Diseñador de actividad, asígnele el nombre SimpleNativeDesigner y


haga clic en Agregar.

4. Abra el archivo SimpleNativeDesigner.xaml y pegue el siguiente código en él. Observe que


este código utiliza ActivityDesigner como elemento raíz y muestra cómo se usa el enlace
para integrar WorkflowItemPresenter en el diseñador de forma que se pueda mostrar un
tipo secundario en el diseñador de actividad compuesto.

<sap:ActivityDesigner
x:Class="Microsoft.Samples.UsingWorkflowItemPresenter.SimpleNativeDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sap="clr-
namespace:System.Activities.Presentation;assembly=System.Activities.Presentati
on" xmlns:sapv="clr-
namespace:System.Activities.Presentation.View;assembly=System.Activities.Prese
ntation"> <sap:ActivityDesigner.Resources>
<DataTemplate x:Key="Collapsed"> <StackPanel> <TextBlock>This is the collapsed
view</TextBlock> </StackPanel> </DataTemplate> <DataTemplate x:Key="Expanded">
<StackPanel> <TextBlock>Custom Text</TextBlock> <sap:WorkflowItemPresenter
Item="{Binding Path=ModelItem.Body, Mode=TwoWay}" HintText="Please drop an
activity here" /> </StackPanel> </DataTemplate> <Style
x:Key="ExpandOrCollapsedStyle" TargetType="{x:Type ContentPresenter}"> <Setter
Property="ContentTemplate" Value="{DynamicResource Collapsed}"/>
<Style.Triggers> <DataTrigger Binding="{Binding Path=ShowExpanded}"
Value="true"> <Setter Property="ContentTemplate" Value="{DynamicResource
Expanded}"/> </DataTrigger> </Style.Triggers> </Style>

67
Windows Workflow Foundation 4.0 2011

</sap:ActivityDesigner.Resources> <Grid> <ContentPresenter


Style="{DynamicResource ExpandOrCollapsedStyle}" Content="{Binding}" />
</Grid> </sap:ActivityDesigner>

1. Haga clic con el botón secundario del mouse en el proyecto UsingWorkflowItemPresenter en


el Explorador de soluciones, seleccione Agregar, a continuación Nuevo Elemento… para
tener acceso al cuadro de diálogo Agregar nuevo elemento y seleccione la categoría Flujo de
trabajo de la sección Plantillas instaladas de la izquierda.

2. Seleccione la plantilla Actividad de código, asígnele el nombre SimpleNativeActivity y


haga clic en Agregar.

3. Implemente la clase SimpleNativeActivity registrando el siguiente código en el archivo


SimpleNativeActivity.cs.

using System.Activities;
namespace Microsoft.Samples.UsingWorkflowItemPresenter
{
public sealed class SimpleNativeActivity : NativeActivity {

public Activity Body { get; set; }


protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddChild(Body);
base.CacheMetadata(metadata);
}

protected override void Execute(NativeActivityContext context)


{
context.ScheduleActivity(Body);
}
}
}

24. En el menú Generar, seleccione Generar solución.

25. Seleccione Iniciar sin depurar en el menú Depurar para abrir la ventana del diseño
personalizado rehospedado.

68

También podría gustarte