程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WF從入門到精通(第九章):邏輯流活動

WF從入門到精通(第九章):邏輯流活動

編輯:關於.NET

學習完本章,你將掌握:

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,然後按下回車鍵,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,然後按下回車鍵。

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,然後按下回車鍵。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,然後按下回車鍵。這樣Visual Studio就在你的代碼中插入了相應的事件處理程序並把界面切換到代碼編輯視圖界面中。我們回到工作流視圖設計器界面上來,你還要繼續設置屬性。

5.對於Completed屬性,輸入LoopCompleted並按下回車鍵,在添加了LoopCompleted事件處理程序後,和前一步驟一樣,我們重新回到工作流視圖設計器界面上。

6.在ChildInitialized屬性中,輸入PrepareQuestion。在插入了PrepareQuestion事件處理程序後,我們再次回到工作流視圖設計器界面上。

7.接著,在ChildCompleted屬性中輸入QuestionAsked,在創建了ChildCompleted事件處理程序後,我們同樣回到工作流視圖設計器界面上。

8.為了在問完所有的問題後(或者在問題是相關的,同時用戶的回答是否定的情形下)終止循環,我們需要添加一個事件處理程序。因此,我們需要選中UntilCondition屬性,從它的下拉列表框中選擇“代碼條件”選項。

9.對於該UntilCondition的Condition屬性,我們輸入TestContinue,按下回車鍵,在插入相應的事件處理程序後再次回到工作流的視圖設計器界面上來。

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.編譯並執行該應用程序。把它執行的結果和前面的兩個示例做比較,你會發現它的功能和前面的示例完全一樣。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved