2015-09-29 59 views
1

我想寫一個方法,通過項目列表 - 每個人將其添加到窗體,然後等待用戶輸入一些數據,然後單擊按鈕。然而,我不知道我應該/可以用來創建這個。強制循環等待事件

foreach (string s in List) 
{ 
    txtName.Text = s; 
    //wait for button click... 

    // When the user is ready, they click the button to continue the loop. 
} 

到目前爲止我發現的是EventWaitHandle類,它似乎只適用於線程。

我該如何做到這一點?

回答

3

還有如果您還使用async/await,您可以使用信號發送方式執行此操作的方法 - 您可以等待通過單擊按鈕完成的任務 - 但一般來說,您應該考慮用戶界面更多的事件驅動方式。

而不是讓你的foreach循環在這裏,保持一個索引到項目正在顯示的集合,並提高它每次單擊按鈕。 (記住要檢查是否有更多的項目來顯示的,當然。)

1

雖然我一般同意喬恩斯基特,有通過的Mads Torgensen一個有趣的演示文稿演示如何async/await可用於簡化這種情況(使用Jon提及的技術)。畢竟,這與枚舉器不一樣 - 我們可以使用索引等狀態編寫自己的枚舉器類,但我們幾乎從不這樣做,而是使用迭代器塊。

無論如何,這裏是我們正在談論的async/await技術。

首先,可重複使用的部分:

public static class Utils 
{ 
    public static Task WhenClicked(this Button button) 
    { 
     var tcs = new TaskCompletionSource<object>(); 
     EventHandler onClick = null; 
     onClick = (sender, e) => 
     { 
      button.Click -= onClick; 
      tcs.TrySetResult(null); 
     }; 
     button.Click += onClick; 
     return tcs.Task; 
    } 
} 

,並使用它的代碼(注意,您必須標記你的方法爲async

foreach (string s in List) 
{ 
    txtName.Text = s; 
    await yourButton.WhenClicked(); 
} 

樣品測試把他們放在一起:

using System; 
using System.Collections.Generic; 
using System.Threading.Tasks; 
using System.Windows.Forms; 

namespace Samples 
{ 
    static class Test 
    { 
     [STAThread] 
     static void Main() 
     { 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 
      var form = new Form(); 
      var txtName = new TextBox { Parent = form, Top = 8, Left = 8 }; 
      var buttonNext = new Button { Parent = form, Top = txtName.Bottom + 8, Left = 8, Text = "Next" }; 
      form.Load += async (sender, e) => 
      { 
       var List = new List<string> { "A", "B", "C", "D " }; 
       foreach (string s in List) 
       { 
        txtName.Text = s; 
        await buttonNext.WhenClicked(); 
       } 
       txtName.Text = ""; 
       buttonNext.Enabled = false; 
      }; 
      Application.Run(form); 
     } 
    } 
    public static class Utils 
    { 
     public static Task WhenClicked(this Button button) 
     { 
      var tcs = new TaskCompletionSource<object>(); 
      EventHandler onClick = null; 
      onClick = (sender, e) => 
      { 
       button.Click -= onClick; 
       tcs.TrySetResult(null); 
      }; 
      button.Click += onClick; 
      return tcs.Task; 
     } 
    } 
} 
+0

可能存在導致丟失按鈕點擊的競態條件。風險在於刪除事件和添加新事件之間有點擊。這將取決於await的植入如何與windows消息隊列交互。 –

+0

@IanRingrose同意。但是,對於像這樣的程序來說,這可能不是問題 - 模擬控制檯類型的處理,如詢問問題並等待響應。 –

0

我與Jon Skeet在這一個,試圖強制一個UI框架變成一種不正常的工作方式,這往往會導致問題。即使它起作用,您也將擁有難以讓其他人理解的代碼,因爲它將以不尋常的方式工作。

Lisp中的一些Web框架以這種方式工作,使頁面顯示看起來像方法調用。我沒有看到.NET中的任何內容。

我的第一個想法是讓你在循環中調用Application.DoEvents()來檢查你的按鈕回調設置的標誌。然而,你的應用程序然後將使用100%的CPU等待,而它什麼都不做。請參閱http://blog.codinghorror.com/is-doevents-evil/

您的應用程序是一個finite state machine,它響應來自用戶的事件,並在所需事件到達與當前狀態的狀態轉換匹配時進入新狀態。多年來,我們在代碼中對這種模式進行了很多嘗試,但是我沒有看到任何比使用標準事件處理更好的方法。