首页 > 编程开发 > Objective-C编程 >
-
学习WF系列(6) 工作流模拟投票项目
1. 摘要
在这次文章中,我们主要来学习关于Replicator活动的相关用法,然后我们会通过一个模拟的投票程序去熟悉Replicator活动。
另外,我们在本节会试着将工作流与具体的实现方式解耦。
2. Replicator
在前文中,我们学过了if-else活动,学过了while活动,按照我们之前学习语言基本语法的学习思路一样,我们还缺少了一个循环中的重要语言元素——For。
但是,在这篇文章中,Replicator其实并不等同于For,按照我的理解,我更倾向于把Replicator看成一个foreach语义的活动。至于为什么,我会在下文的例子中去说明。
在高级语言中,用for,foreach可以实现的功能,我们同样都可以用while来实现,但是在WF中,Replicator提供了比While丰富得多的事件。因此,在MSDN中,有这样的一条建议:如果出现需要对许多不同实体重复同一函数的任务,那么我们应优先使用Replicator,而不是while。
什么叫对许多不同实体重复同一函数呢?我们来看下面的一个小例子:
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
list1.Add(3);
foreach (int intItem in list1)
{
DoSomething(intItem);
}
在上面的代码中,我们就相当与把list1中的每一个元素,即intItem作为实体,去重复DoSomething的工作。
我们在这里不去说太多的理论,毕竟我们现在处于入门阶段吧,让我们在例子中一点点去熟悉他的相关知识。
3. 模拟投票
我们现在有这样一个需求,需求很简单,就是我们构建一个投票的小Winform项目,然后在工作流端处理,并且要求返回一个字符串,对我们的每一项都进行一个字符串处理。并且要求选中用户选中了几项。
在这再说明一次,如果就是单纯地去实现这个功能很简单,我们这里主要是介绍Replicator的用法而已。
学习Replicator的关键在于熟悉Replicator的生命周期,我按时间顺序去写:
A. Initialized:在ReplicatorActivity开始执行时发生
B. ChildInitialized:在初始化子活动实例时发生
C. Code_Excute:在内部活动发生时发生(这条不属于Replicator,在这里只是希望有助于对方理解整个周期)
D. ChildCompleted:在子活动结束时发生
E. Completed: 在整个ReplicatorActicity结束时发生
好,我们为了加速我们的理解,会在下面的例子中强行用到以下的步骤:
首先,我们来明确一下上面程序的工作流程:
A. 传入一个数组。
B. 初始化工作流内部数组
C. 遍历整个数组,并且将数组转化为一个字符串
D. 当遍历每一项时都把总数加一。
E. 当遍历到每一项结束时,我们为了加深我们的理解,都给用户一个提示窗口。
F. 当遍历结束时,我们为了加深我们的理解,会用户一个提示。
好,我们来一步一步去写代码:
首先拖进一个Replicator 和 一个Code活动:
我们先在工作流中声明一个数组,用来把宿主中的数组传递进工作流:
private ArrayList array;
public ArrayList ArrayL
{
set { array = value; }
}
然后我们想,我们既然遍历,就需要指定一个我们要遍历的对象 ,这个工作应该在初始化Replicator活动时执行:
private void Activity_Initialized(object sender, EventArgs e)
{
this.replicatorActivity1.InitialChildData = array;
}
然后我们要在每一次遍历之前知道我们这次遍历的是什么实体:
private void Activity_ChildInitialized(object sender, ReplicatorChildEventArgs e)
{
vote = e.InstanceData as String;
}
剩下的工作就很简单了,让我们看一看整个项目的代码:
Workflow.cs:
public sealed partial class Workflow1 : SequentialWorkflowActivity
{
private string vote;
private int countAll = 0;
private string result = String.Empty;
private ArrayList array;
public ArrayList ArrayL
{
set { array = value; }
}
public string Result
{
get { return result; }
}
public int CountAll
{
get { return countAll; }
}
public Workflow1()
{
InitializeComponent();
}
private void Activity_Initialized(object sender, EventArgs e)
{
this.replicatorActivity1.InitialChildData = array;
}
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
countAll++;
}
private void Activity_ChildInitialized(object sender, ReplicatorChildEventArgs e)
{
vote = e.InstanceData as String;
vote = "您给" + vote + "投了票";
}
private void Activity_ChildCompleted(object sender, ReplicatorChildEventArgs e)
{
result = result + "n" + vote;
MessageBox.Show("Complete"); //实际中不应有
}
private void Activity_Completed(object sender, EventArgs e)
{
MessageBox.Show("All Complete"); //实际中不应有
}
}
Form1.cs:
public partial class Form1 : Form
{
private WorkflowRuntime workflowRuntime;
private WorkflowInstance instance;
private AutoResetEvent waitHandle;
private string result = String.Empty;
int count;
public Form1()
{
InitializeComponent();
workflowRuntime = new WorkflowRuntime();
workflowRuntime.StartRuntime();
waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender1, WorkflowCompletedEventArgs e1)
{
result = e1.OutputParameters["Result"].ToString();
count = Convert.ToInt32(e1.OutputParameters["CountAll"]);
waitHandle.Set();
};
}
private void button1_Click(object sender, EventArgs e)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
ArrayList list = new ArrayList();
for (int i = 0; i < this.checkedListBox1.CheckedItems.Count;i++ )
{
list.Add(this.checkedListBox1.CheckedItems[i].ToString());
}
dic.Add("ArrayL", list);
instance = workflowRuntime.CreateWorkflow(typeof(Workflow1), dic);
instance.Start();
waitHandle.WaitOne();
MessageBox.Show(result);
MessageBox.Show(count.ToString());
}
}
运行结果如下:
4. 项目总结
在实际的项目中,我们的工作流不应该涉及到具体操作,我们可以这样理解,工作流是一个设计人员用的工具,他不应该与我们的具体实现扯上关系。
比如说我们在之前的项目中弹出对话框,这点就不可取。因为这个工作流可能用于Web,可能用与Winform,甚至可能用于PDA,一旦你在工作流中写入了MessageBox.Show()这样的代码,直接就束缚了你的工作流的重用性。
另外,我们在实际中不会向以前项目一样把工作流和宿主程序写在同一个工程中,而应该:
这就要求我们在宿主程序中要引入工作流的dll。
好,这一次就写到这,希望大家回去多加练习。
下文预告:《一步一步学WF系列(七)——改善登陆程序,取代if-else》
系列文章:
学WF系列(一)——Hello world开始
学WF系列(二)——工作流与Visual Studio
学WF系列(三)
学WF系列(四)——工作流模拟登陆
学WF系列(五)——工作流模拟猜数游戏
在这再说明一次,如果就是单纯地去实现这个功能很简单,我们这里主要是介绍Replicator的用法而已。
学习Replicator的关键在于熟悉Replicator的生命周期,我按时间顺序去写:
A. Initialized:在ReplicatorActivity开始执行时发生
B. ChildInitialized:在初始化子活动实例时发生
C. Code_Excute:在内部活动发生时发生(这条不属于Replicator,在这里只是希望有助于对方理解整个周期)
D. ChildCompleted:在子活动结束时发生
E. Completed: 在整个ReplicatorActicity结束时发生
好,我们为了加速我们的理解,会在下面的例子中强行用到以下的步骤:
首先,我们来明确一下上面程序的工作流程:
A. 传入一个数组。
B. 初始化工作流内部数组
C. 遍历整个数组,并且将数组转化为一个字符串
D. 当遍历每一项时都把总数加一。
E. 当遍历到每一项结束时,我们为了加深我们的理解,都给用户一个提示窗口。
F. 当遍历结束时,我们为了加深我们的理解,会用户一个提示。
好,我们来一步一步去写代码:
首先拖进一个Replicator 和 一个Code活动:
我们先在工作流中声明一个数组,用来把宿主中的数组传递进工作流:
private ArrayList array;
public ArrayList ArrayL
{
set { array = value; }
}
然后我们想,我们既然遍历,就需要指定一个我们要遍历的对象 ,这个工作应该在初始化Replicator活动时执行: