本文示例源代碼或素材下載下傳
學習完本章,你将掌握:
1.學會怎樣使用IfElse活動來執行條件表達式
2.學會怎樣使用While活動來執行循環
3.了解Replicator活動是怎樣來模拟for循環的,以及它的使用方法。
我們已經看到過怎樣執行工作流内部和外部的代碼,已經知道怎樣處理異常,暫停程序,在事情脫離控制時終止我們的工作流。但無疑對于任何一個計算機系統的主要組成部分來說,都應具有根據運作時的條件做出判斷以執行不同的任務的能力。在本章,我們将示範要求我們應付if/else場景及基本的循環的一些工作流活動。
條件及條件處理
現在,你可能不會感到奇怪,你發現WF提供了基于運作時的條件進行邏輯處理控制流的活動。畢竟,假如WF提供了活動去抛出并捕獲異常,那它為什麼就沒有相應的活動來根據工作流的執行情況進行檢測并根據它們擷取的結果作出決策呢?
我們将在本章中進行測試的活動包括IfElse活動、While活動和Replicator活動。IfElse活動的作用是測試一個條件并根據測試結果執行不同的工作流路徑。While活動用來執行一個While循環。而對于for循環,則是使用Replicator活動來完成。現在通過本章的示例應用程式開始我們的學習。
備注:在本章你将依靠CodeCondition來進行條件的處理,它(CodeCondition)的意思是你将寫下C#代碼來處理條件表達式。在12章(“政策和規則”)中,你将使用RuleCondition來對條件表達式的值進行處理,RuleCondition使用了WF的基于規則的處理方式。兩種方式都同樣有效。
Qustioner應用程式
本章的示例應用程式是一個Windows Form應用程式,它會請你回答三個問題,問題内容你能夠進行修改。(問題的内容儲存在應用程式的settings property中。)你也可指定這些問題是各自獨立還是互相關聯的。
當工作流開始執行時你要把這些問題和相關的情況傳入該工作流。互相關聯的問題隻有在前面的問題回答正确時才會被進一步提出。例如,假如有人問你:“談到的文檔你看過嗎?”,假如你沒有,則沒多大意義問接下來這一問題:“這個文檔你準許嗎?”假如問題是相關的,則第一個問題回答是否定的話,就将傳回否定的回答,餘下的問題不予考慮也都将傳回否定的回答。
各自獨立的問題要求你必須回答,而不管前面的問題中你回答的是什麼。例如這個問題,“你喜歡冰淇淋嗎?”就和問題“現在外面在下雨嗎?”是不相關的。無論你喜不喜歡冰淇淋,你的答案都和外面的天氣這個問題是各自獨立的。對于互相獨立的問題來說,不管你在前面的問題中是肯定還是否定的回答,都會進一步被問到。
使用者界面如圖9-1。假如你修改三個問題中的任何一個的内容,新問題的都将自動地儲存到你的應用程式的settings property中(問題的類型也一樣)。這些問題會産生“是/否”的回答,使工作流能夠把這些回答作為一個Boolean類型的數組傳回到宿主應用程式中。

圖9-1 Questioner主應用程式界面
當你點選Execute按鈕時,這些問題通過帶“是”和“否”按鈕的資訊框依次呈現。一旦工作流處理完所有的這些問題,它就傳回一個Boolean數組給宿主應用程式。宿主應用程式将檢查該數組以顯示不同的使用者界面。
當工作流執行時,回答結果将以藍色圓球的形式顯示(如圖9-1)。當工作流任務完成後,通過的回答将以綠色圓球的形式出現,未通過的回答将以紅色圓球的形式出現。假如所有的回答都通過了,則“最終回答結果”圖檔将以綠色圓球的形式呈現。但是,假如三個問題中的任何一個沒有通過,則“最終回答結果”圖檔将以帶“8”字的圓球的形式呈現。如圖9-2。
圖9-2 Questioner應用程式執行期間的使用者界面
對你來說,使用這個應用程式的目的是測試本章中的三個活動。第一次疊代,Questioner将使用IfElse活動來判斷要執行什麼動作過程。第二次疊代時這些問題仍然會被問到,我們将使用While活動來提問。最後一次疊代我們将使用Replicator活動來模拟for循環進行提問。對于該應用程式的每一次疊代,我們都将使用前一章中示範的技術來把回答的結果傳回給宿主應用程式。
使用IfElse活動
IfElse活動的作用是對if-then-else條件表達式進行模拟,其實你在前幾章使用過這個活動。
IfElse活動要求你提供一個條件表達式,它其實是作為一個event handler執行。你建立的event handler有一個類型為ConditionalEventArgs的參數,它有一個Boolean類型的(名稱為)Result屬性。你可對其進行set,以指明該條件表達式的結果。
IfElse活動根據Result的值來指揮工作流到底該執行兩個分支中的哪一個。在Microsoft Visual Studio的工作流視圖設計器中,true執行的是顯示在左邊的路徑,而false執行的是右邊的路徑。兩個分支都可作為其它活動的容器,允許你插入任何一個你需要的工作流活動。
備注:通過本節的學習,你可能會認為,IfElse活動可能不是建構下面的工作流的最合适的活動。你在本章的後面部分将找到更加适合下面特定的工作流的活動。
使用IfElse活動建立QuestionFlow工作流
1.下載下傳本章源代碼,打開IfElse Questioner檔案夾中的解決方案。
2.看看Visual Studio解決方案資料總管,你會看到解決方案的層次結構和前一章中的相似。主應用程式的檔案位置在Questioner項目中,而宿主通信服務檔案的位置則在QuestionService項目中。為使你把注意力放到工作流上,我已經建立了服務接口(具體過程參見前一章):IQuestionService,并且使用wca.exe工具(使用方法參見前一章)生成了一個必需的通信活動:SendReponseDataToHost。現在,找到QuestionFlow項目的Workflow1.cs檔案并在視圖設計器中打開它。
3.從工具箱中拖拽一個IfElse活動到設計器界面上。
4.你會看到一個内含感歎号(!)标記的紅色圓圈,這是提醒你還需要更多的資訊才能編譯你的工作流。其實,缺少的就是條件表達式!選中ifElseActivity1的左邊分支以便在Visual Studio的屬性面闆上呈現該活動的屬性。選中它的Condition屬性以激活它的下拉清單框,然後從清單中選擇代碼條件。
備注:你通常有兩種方式來對條件表達式進行選擇:code(代碼)和rules-based(基于規則)。我們這裡将使用基于代碼的條件表達式,基于規則的技術将保留到第12章(政策活動)進行學習。
5.展開顯示的Condition屬性,輸入AskQuestion1,然後按下Enter鍵,Visual Studio這就為你插入了AskQuestion1的事件處理程式并會切換到代碼視圖下。現在,重新回到工作流視圖設計器上,你還要把更多的活動添加到你的工作流中。
6.拖拽一個Code活動到設計器界面上,并把它放到ifElseActivity1的右邊分支上。
7.指定它的ExecuteCode屬性值為NegateQ1。當Visual Studio插入了NegateQ1事件出現程式後,重新回工作流視圖設計器界面上。
8.重複步驟6和步驟7,在ifElseActivity1的左邊分支上也添加一個Code活動。
指定它的ExecuteCode屬性值為AffirmQ1,但是,當Visual Studio插入了AffirmQ1事件出現程式後,不要切換回工作流視圖設計器界面上。相反,我們要添加一些代碼。
9.我們現在需要為該工作流類添加一些屬性,當我們起動工作流程序時可把它們作為參數。在Workflow1的構造器的下面,添加下面的代碼,它們包含有工作流将問到的三個問題:
private string[] _questions = null;
public string[] Questions
{
get { return _questions; }
set { _questions = value; }
}
10.我們也需要添加一個Dependent屬性,它用來告知這些問題彼此是否是相關的。在上面所添加的代碼下,添加如下代碼:
private bool _dependent = true;
public bool Dependent
{
get { return _dependent; }
set { _dependent = value; }
}
11.問題回答的結果是一些Boolean值,在傳回給宿主應用程式前需要儲存到某些地方。是以,在上面所插入的代碼下添加該字段:
private bool[] _response = null;
12.該_response字段沒有被初始化,是以找到Workflow1的構造器,在裡面的InitializeComponent方法下面添加如下的代碼:
// Initialize return vector.
_response = new bool[3];
_response[0] = false;
_response[1] = false;
_response[2] = false;
13.現在找到Visual Studio為你添加的AskQuestion1事件處理程式(event handler)。在該事件處理程式中添加下面的代碼:
// Ask the question!
DialogResult result = MessageBox.Show(Questions[0], "Questioner:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
e.Result = (result == DialogResult.Yes);
14.對于NegateQ1事件處理程式,則添加下面的代碼:
// Negate answer.
_response[0] = false;
if (Dependent)
{
// Negate remaining answers.
_response[1] = false;
_response[2] = false;
}
15.接下來,在AffirmQ1事件處理程式中添加下面的代碼:
// Affirm answer.
_response[0] = true;
16.你現在就已為第一個問題的提問添加了對應的工作流組成部分,但是這還有兩個問題。對于第二個問題,重複步驟3至步驟8來新添加一個IfElse活動添加到工作流中,把和問題1相關的地方用問題2替換掉,例如插入的事件處理程式就應該是AskQuestion2、NegateQ2和AffirmQ2。工作流視圖設計器的界面現在如下所示:
17.現在找到AskQuestion2事件處理程式并添加下面的代碼:
if (_response[0] == false && Dependent)
{
// No need to ask!
e.Result = false;
}
else
{
// Ask the question!
DialogResult result = MessageBox.Show(Questions[1], "Questioner:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
e.Result = (result == DialogResult.Yes);
}
18.對于NegateQ2事件出現程式,添加下面的代碼:
// Negate answer
_response[1] = false;
if (Dependent)
{
// Negate remaining answer
_response[2] = false;
}
19.對于AffirmQ2事件處理程式,添加下面的代碼:
// Affirm answer.
_response[1] = true;
20.在一次重複步驟3至步驟8,添加第三個問題,把和問題1相關的内容替換掉(方法和添加第二個問題時一樣)。此時,工作流視圖設計器的界面如下所示:
21.找到AskQuestion3事件處理程式,插入下面的代碼:
if (_response[1] == false && Dependent)
{
// No need to ask!
e.Result = false;
}
else
{
// Ask the question!
DialogResult result = MessageBox.Show(Questions[2], "Questioner:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
e.Result = (result == DialogResult.Yes);
}
22.對于NegateQ3事件處理程式,添加下面的代碼:
// Negate answer.
_response[2] = false;
23.對于AffirmQ3事件處理程式則添加下面的代碼:
// Affirm answer
_response[2] = true;
24.現在回到工作流視圖設計器。你會在工具箱中找到一個名稱為SendResponseDataToHost的自定義活動。(注意:假如工具箱中沒有這個SendResponseDataToHost活動,則編譯該項目再重新看看。)
25.拖拽一個SendResponseDataToHost活動到你的工作流視圖設計器界面上,把它放到第三個IfElse活動(ifElseActivity3)的下邊。
26.因為傳回的資料是一個簡單的Boolean類型的數組,是以這裡的處理方式和前一章有點點差別。和你去添加一個容納該Boolean類型的數組的依賴屬性不同,SendResponseDataToHost活動使用一個字段來容納該資料,建立該字段的使用者界面也就和你在第七章中看到的不同。在Visual Studio屬性面闆上選中responses屬性,然後點選浏覽(...)按鈕。
這打開了如下面的Boolean集合編器對話框。
27.點選添加按鈕,共重複三次,保留這些預設的False值,然後點選确定按鈕。Visual Studio就為你把包含三個Boolean元素的數組添加進了你的Workflow1.designer.cs檔案的代碼中。
提示:在下面的第28步,你将添加一個CodeActivity,用它來把你在第11步添加的_response字段配置設定到我們剛剛為SendResponseDataToHost建立的Boolean數組中。但是,你也可直接使用SendResponseDataToHost的一個我們已經建立的response屬性(來對它進行通路)。我選擇這樣做僅僅是因為(從闡述的角度來說)這樣更有意義,這可展示出在涉及到宿主通信活動前該Ifelse活動是怎樣添加和工作的。
28.現在我們需要把儲存了問題回答結果的數組的值和将要使用這些值的SendResponseDataToHost活動聯系起來。是以,我們現在就拖拽一個Code活動到工作流視圖設計器的界面上,把它放到第三個IfElse活動(ifElseActivity3)和SendResponseDataToHost活動(sendResponseDataToHost1)之間。
29.設定該Code活動的ExecuteCode屬性為CopyResponse,然後按下Enter鍵。
30.在Visual Studio插入的CopyResponse事件處理程式中添加下面的代碼:
// Assign outgoing data.
sendResponseDataToHost1.responses = _response;
31.編譯并運作該應用程式。改變問題的Dependency屬性,看看在回答這些問題時,作出否定的回答其結果一樣嗎?
使用While活動
假如你回頭看看前一節,你會至少注意到兩件事。首先,毫無疑問你體驗了IfElse活動;第二,它用了31個獨立的步驟來建立了該工作流。有些程式結構使用if-then-else來進行處理很合适,但這個特殊的應用程式使用循環結構來進行問題的提問會更合适些。這些将在接下來示範。你将使用另一個使用了while循環的工作流來替換你剛剛建立好了的工作流。
WF的While活動處理條件表達式時的過程和IfElse活動相似。它觸發了一個對循環是否繼續進行驗證的事件,它使用ConditionalEventArgs來傳回你的判斷結果(也要使用Result屬性)。
但是,和IfElse活動不同的是,你在使用While活動的時候,假如設定Result為true将導緻繼續進行循環,設定Result為false則終止循環。我們就來看看怎樣使用while循環來替換if-then-else進行條件處理,以簡化我們的工作流。
使用While活動建立QuesionFlow工作流
1.從下載下傳的本章源代碼中使用Visual Studio打開While Questioner檔案夾内的解決方案。
2.和前一節一樣,該應用程式本質上是完整的,它包含了已建立好了的SendResponseDataToHost活動。剩下要去完成的工作是完善工作流的處理過程。在解決方案管理器面闆上找到QuesionFlow項目中Workflow1.cs檔案,然後在工作流的視圖設計器中打開它。
3.從工具箱中拖拽一個While活動到視圖設計器界面上。
4.和IfElse活動相似,選中whileActivity1活動的Condition屬性以激活它的下拉清單框。從這個下拉清單框中選擇代碼條件。
5.展開該Condition屬性,輸入TestComplete,然後按下Enter鍵。Visual Studio這就為你添加了TestComplete事件程式程式,然後回到工作流的視圖設計器界面上。
6.拖拽一個Code活動到工作流視圖設計器界面上,把它放到whileActivity1的裡面。指定它的ExecuteCode的屬性值為AskQuestion。同樣,在生成了AskQuestion事件處理程式後重新回到工作流視圖設計器界面上來。
7.為了使我們能把儲存有問題回答結果的Boolean數組傳回給宿主應用程式,我們需要重複前面一節的第24、25步,以把一個SendResponseDataToHost活動插入到我們的工作流中。(在這之前,需要編譯該應用程式,否則SendResponseDataToHost活動不會在工具箱中顯示。)我們把該SendResponseDataToHost活動放到whileActivity1的下面,以便它在while循環後被執行。
8.我們同樣需要重複前一節的第9步至第12步,以便插入Questions和Dependent屬性,并且對_response數組進行建立和初始化。
9.在_response數組的聲明語句下面,添加下面的代碼:
private Int32 _qNum = 0;
10.找到TestComplete事件處理程式,添加下面的代碼:
// Check for completion.
if (_qNum >= Questions.Length)
{
// Assign outgoing data.
sendResponseDataToHost1.responses = _response;
// Done, so exit loop.
e.Result = false;
}
else
{
// Not done, so continue loop.
e.Result = true;
}
11.我們需要完成的最後一點代碼實際上就是問題的回答。在Workflow1.cs檔案中,你會找到AskQuestion事件處理程式,為該事件處理程式添加下面的代碼。假如問題的回答是否定的并且Dependent屬性是true(表示各個問題是相關的),則所有餘下的問題的回答就都是否定的。
// Ask the question!
DialogResult result = MessageBox.Show(Questions[_qNum], "Questioner:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
_response[_qNum] = (result == DialogResult.Yes);
// Check response versus dependency
if (!_response[_qNum] && Dependent)
{
// Negate remaining questions
while (_qNum < Questions.Length)
{
// Negate this question
_response[_qNum] = false;
// Next question
++_qNum;
}
} // if
else
{
// Set up for next iteration
++_qNum;
}
12.重複前一節的步驟28至步驟30,我們将使用Code活動來把儲存了問題回答結果的數組傳給SendResponseDataToHost活動。(該Code活動放到While1活動和SendResponseDataToHost1活動中間。)
13.編譯并執行該應用程式。
假如你花一些時間來比較一下本節和上一節的最終的工作流視圖設計器界面,你很容易發現使用While活動大大簡化了工作流的處理。整個工作流視圖設計器界面簡單多了!
既然有和while循環等價的工作流活動,是否也有和for循環等價的工作流活動呢?答案是肯定的,它就是Replicator活動。
使用Replicator活動
說Replicator活動和C#術語中的for循環是等價的可能并不正确。C#語言規範1.2中告訴我們,C#中的for循環看起來和下面的類似:
for(for-initializer;for-condition;for-iterator) embedded-statement
embedded-statement(内嵌語句)在for-condition(條件判斷)的值為true時執行(如果被省略,則假定為true),循環由for-initializer開始,每一次循環都要執行for-iterator。這當中沒有涉及到任何的C#聲明元件。對于replication來說,我們可以設想它是一個能重複地對源代碼進行準确複制的軟體工廠。C#的for循環則不是以這種方式進行操作的。
事實上,“重複”的概念在我們看到WF中和for循環等價的活動這一說明後就不會感到可怕了。假如你熟悉ASP.NET的話,你或許使用過Repeater控件。這個ASP.NET的Repeater控件有一個項模闆(同時也有一個項替換模闆),它能根據所綁定的資料項的數目進行重複處理。
Replicator活動和ASP.NET的綁定到基于IList資料源的Repeater控件相似,它重複它所包含的子活動,基于IList的資料源中的每個元素就相當于一個子活動。但是Replicator活動和C#中的for語句在有些方面是相似的,因為它也有一個循環初始化事件(這就像是for-initializer)、一個循環完成事件(這和用for-iterator和for-condition做比較類似)以及一個循環繼續事件(和for-condition類似)。它提供了這些事件來指明重複性的(和嵌入語句相似)子活動的建立工作,以便你能個性化地進行資料綁定,子活動完成了它就觸發一個事件,以便你能為每個子活動執行個體執行一些清理和管理的任務。
Replicator活動必須接受也隻接受一個唯一的活動,它能作為其它活動的容器(和Sequence活動類似),它觸發一個開始執行的初始化事件。在初始化事件的執行期間,你可把一個基于IList的集合綁定到Replicator活動的InitialChildData屬性中。
該Replicator活動然後會重複你所提供的子活動,它們的次數和基于IList集合中的項的數目相等。這些子活動執行個體能夠以依次按順序的方式或以并行的方式執行(這可通過ExecutionType屬性進行設定)。UntilCondition事件在每一個子活動執行前觸發,在處理UntilCondition事件時,你可通過設定ConditionalEventArgs的Result屬性為false來通知Replicator活動繼續執行(為true則終止循環)。表9-1簡要地列出了我們需要關注的Replicator活動的一些屬性,而表9-2列出了在我們的工作流中使用Replicator活動時需要進行處理的一些事件。表9-1 Replicator活動的屬性
屬性 | 功能 |
ExecutionType | 擷取或設定Replicator活動的ExecutionType(一個枚舉值)。該ExecutionType的枚舉值包含Parallel和Sequence。 |
InitialChildData | 擷取或設定子活動資料的一個IList集合。該屬性和其它.NET技術中的data-binging(資料綁定)屬性相似,事實上要配置設定給該屬性的對象必須是一個IList對象。Replicator活動會為配置設定給該屬性的基于IList集合中的每一項建立一個子活動執行個體。 |
表9-2Replicator活動的事件
事件 | 功能 |
ChildCompleteEvent | 在Replicator活動中的子活動執行個體已完成後觸發。對于每一個循環觸發一次。 |
ChildInitializedEvent | 在Replicator活動中的子活動執行個體初始化後觸發。對于每一個循環觸發一次。 |
CompleteEvent | 在Replicator活動已完成後觸發(也就是說,在所有循環中的子活動執行個體都已執行完成)。 |
InitializedEvent | 在Replicator活動開始執行時觸發。該事件隻觸發一次,在所有的子活動執行前觸發。 |
UntilCondition | UntilCondition在許多的WF文檔中都是以屬性的方式列出的,它其實表示的是一個事件處理程式,這和Code活動的ExecuteCode屬性的作用一樣(通過ExecuteCode屬性就把一個去執行相應代碼的事件處理程式和相應的Code活動聯系了起來)。這個事件在每一個子活動執行個體執行前觸發。它的ConditionalEventArgs事件參數控制了循環是否繼續進行執行。指定Result的值為false則允許子活動繼續執行。而指定Result的值為true則導緻Replicator活動停止所有子活動的執行。 |
圖9-3為你提供了一個基本的流程圖,它顯示了在什麼地方觸發什麼事件。
圖9-3Replicator活動的事件觸發順序流程圖
你在圖9-3中看到的基于IList的集合是通過InitialChildData屬性指定的,你也可在Initialized事件處理前或處理期間進行指定。該工作流也沒有說明所複制(生成)的子活動能以順序或者以并行的方式執行,這取決于ExecutionType屬性的設定。
在現實情形中怎樣使用Replicator活動呢?從目前的描述來看,它比真實的使用情形要複雜得多。事實上,它的機制和其它活動相比并沒有多大的差別。拖拽該活動到工作流視圖設計器界面上,為各中事件處理程式指定值,再拖拽一個唯一的子活動到Replicator活動當中。該唯一的子活動和Replicator活動本身一樣,也能作為一個(其它活動的)容器(就像Sequence活動一樣),是以事實上多于一個的活動也能執行。在我們的頭腦裡有了這些表和這些圖,我們就可使用Replicator活動來重新編寫Questioner應用程式了。
使用Replicator活動建立QuestionFlow工作流
1.下載下傳本章源代碼,打開Replicator Questioner檔案夾内的解決方案。
2.和前兩節一樣,宿主應用程式本質上已經完成,這可友善你把焦點放到工作流方面。選中Workflow1.cs檔案并在Visual Studio的工作流視圖設計器中打開它。
3.從工具箱中拖拽一個Replicator活動到視圖設計器界面上,界面如下所示:
4.在Visual Studio屬性面闆上,選中Initialized屬性并輸入InitializeLoop,然後按下Enter鍵。這樣Visual Studio就在你的代碼中插入了相應的事件處理程式并把界面切換到代碼編輯視圖界面中。我們回到工作流視圖設計器界面上來,你還要繼續設定屬性。
5.對于Completed屬性,輸入LoopCompleted并按下Enter鍵,在添加了LoopCompleted事件處理程式後,和前一步驟一樣,我們重新回到工作流視圖設計器界面上。
6.在ChildInitialized屬性中,輸入PrepareQuestion。在插入了PrepareQuestion事件處理程式後,我們再次回到工作流視圖設計器界面上。
7.接着,在ChildCompleted屬性中輸入QuestionAsked,在建立了ChildCompleted事件處理程式後,我們同樣回到工作流視圖設計器界面上。
8.為了在問完所有的問題後(或者在問題是相關的,同時使用者的回答是否定的情形下)終止循環,我們需要添加一個事件處理程式。是以,我們需要選中UntilCondition屬性,從它的下拉清單框中選擇“代碼條件”選項。
9.對于該UntilCondition的Condition屬性,我們輸入TestContinue,按下Enter鍵,在插入相應的事件處理程式後再次回到工作流的視圖設計器界面上來。
10.對于Replicator活動的執行個體replicatorActivity1來說,需要一個唯一的子活動。是以,從工具箱中拖拽一個Code活動到replicatorActivity1中。指定它的ExecuteCode屬性為AskQuestion。
11.在工作流視圖設計器上需要完成的最後工作是把一個SendResponseDataToHost活動拖拽到設計器界面上,把它放到replicatorActivity1活動的下面。然後重複“使用IfElse活動建立QuestionFlow工作流”這一節中的步驟24至步驟30。(在這之前,你或許需要編譯該應用程式以讓SendReponseDataToHost活動顯示在工具箱中。)
12.現在我們就來為Workflow1添加相應的代碼,是以我們進入它的代碼視圖界面。
13.因為我們修改的replicatorActivity1活動的各個屬性,Visual Studio都為我們添加了對應的事件處理程式,現在我們就來完成這些事件處理程式并為工作流添加其它一些所需要的代碼。我們重複“使用IfElse活動建立QuestionFlow工作流”這一節中的步驟9至步驟12,這些過程為工作流添加了為進行問題處理所必須的一些屬性。
14.Replicator活動需要一個基于IList的集合以便複制(生成)出它的子活動。我們有一個容納問題的數組可以使用,因為基本的數組類型就是基于IList的。但是,我們怎樣傳回結果呢?在問題描述和問題編号之間并沒有直接聯系在一起。除了這些,我們還不能在所傳回的數組的值中指定Boolean傳回值。是以,我們将作出一些輕微的修改,我們需要建立一個新的數組——一個整形數組。它用來表示在問題描述數組中的元素的偏移量。對于生成的子活動,它将對所要提問的問題編号進行通路,給定它的一個索引,就可把問題描述數組和回答的Boolean類型數組聯系起來。是以,我們需要在_respone數組的聲明代碼下面添加這樣一個數組。
private Int32[] _qNums = null;
15._qNums數組還沒有初始化,是以必須初始化才能使用。初始化最好的位置是在Question屬性中,是以對它的set通路器進行修改,修改後的代碼如下:
public string[] Questions
{
get { return _questions; }
set
{
// Save question values
_questions = value;
// Create new question number array
_qNums = new Int32[_questions.Length];
for (Int32 i = 0; i < _questions.Length; i++)
{
// Assign this question number to the array
_qNums[i] = i;
} // for
}
}
16.為了對Replicator活動的所有事件都會被使用到進行證明,我們需要在_qNums數組的聲明語句下添加下面的代碼:
private Int32 _currentQuestion = -1;
private bool _currentQuestionResponse = false;
17.在InitializeLoop事件處理程式中添加下面的代碼來對InitialChildData進行初始化:
replicatorActivity1.InitialChildData = _qNums;
備注:假如Workflow1有可綁定的屬性的話,你可通過工作流視圖設計器來直接對InitialChildData的值進行指定。但是,因為該Replicator活動正使用一個内部生成的數組(_qNums),是以和上面所展示的一樣,你必須在InitializeLoop事件處理程式中對InitialChildData的值進行指定。
18.對于LoopCompleted事件處理程式,添加下面的代碼來傳回問題的回答結果:
replicatorActivity1.InitialChildData = _qNums;
19.現在我們的子活動将執行許多次來進行問題的提問。在每一個問題提問之前,Replicator活動都将觸發ChildInitialized事件。我們将處理該事件并從事件參數中擷取我們将要提問的問題編号。稍後,當Code活動執行時,将會對和該問題編号對應的問題進行提問。是以,把下面的代碼添加到PrepareQuestion方法中(該方法是ChildInitialized事件的處理程式):
_currentQuestion = (Int32)e.InstanceData;
20.當對Code活動的回答結果進行儲存時,我們需要做的過程都是相似的。定位到QuestionAsked事件處理程式(它用來處理Replicator活動的ChildCompleted事件),添加下面的代碼:
_response[_currentQuestion] = _currentQuestionResponse;
21.緊接着是要對Replicator活動的UntilCondition進行編輯。找到TestContinue方法,添加下面的代碼。這些在TestContinue方法中的代碼将對Dependent屬性進行檢查。假如不再有問題,循環将被終止,同時,假如這些問題被指明是相關的,并且最近的一次回答是否定的,則所有餘下未答問題的回答也被标明為否定的并終止循環。
if (_currentQuestion >= 0)
{
// Check dependency.
if (!_response[_currentQuestion] && Dependent)
{
// Negate remaining questions.
for (Int32 i = _currentQuestion + 1; i < Questions.Length; i++)
{
// Negate this question.
_response[i] = false;
}
// Stop processing.
e.Result = true;
}
else
{
// Check for complete loop.
if (_currentQuestion == _qNums[Questions.Length - 1])
{
// Done.
e.Result = true;
}
else
{
// Continue processing.
e.Result = false;
}
}
}
22.找到Visual Studio已經為你添加了的AskQuestion方法,添加下面的代碼。該方法使你有機會去進行問題的回答。
// Ask the question!
DialogResult result = MessageBox.Show(Questions[_currentQuestion],
"Questioner:", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
_currentQuestionResponse = (result == DialogResult.Yes);
23.編譯并執行該應用程式。把它執行的結果和前面的兩個示例做比較,你會發現它的功能和前面的示例完全一樣。