2013-08-02 47 views
3

說明&背景: 對不起,如果這個問題很糟糕,但我想繞過任務。我目前有一組類將控制它們的構造函數,並允許我自動化用戶交互(鍵入文本框,單擊按鈕等)。告訴任務等到計時器已經停止

其中一個示例是我的課程TextboxTester。下面是我在文本框中帶來怎樣的一個片段:

public class TextboxTester 
{ 
    private int _currentTextLength = 0; 
    private string _text; 
    private TextBox _textBox; 
    private Timer timer; 

    #region Constructor 
    public TextboxTester(TextBox TextBox) 
    { 
     _textBox = TextBox; 
    } 
    #endregion 

我希望能夠以鏈的動作(任務)在一起,以便 - 一個又一個這樣我就可以自動完成一系列的用戶輸入事件。我閱讀並發現了TPL任務,並決定給它一個旋轉。

我創建了我的TextboxTester類和我的ButtonTester類,並傳入了控件。我希望能夠輸入到文本框,然後點擊按鈕 - 所以我稱之爲:

Task.Factory.StartNew(() => txtTester.WriteText("Hello World")).Wait(); 
Task.Factory.StartNew(() => btnTester.Click(1)); 

從我的Task.Factory電話所知,這是我想做的事 - 執行第一行動,等到它完成 - 然後執行下一步。的問題是,TextboxTester.WriteText()使用一個計時器來模擬輸入成TextBox(每秒1個字符):

public void WriteText(string Text) 
    { 
     if (timer == null) 
     { 
      State.Working = true; 
      timer = new Timer(); 

      try 
      { 
       _text = Text; 
       timer.Elapsed += new ElapsedEventHandler(timer_ElapsedWrite); 
       timer.Interval = 1000; 
       timer.Enabled = true; 
       timer.Start(); 
      } 
      catch 
      { 
       MessageBox.Show("WriteText timer could not be started."); 
      } 
     } 
    } 
void timer_ElapsedWrite(object sender, ElapsedEventArgs e) 
    { 
     _textBox.Dispatcher.BeginInvoke(new Action(() => 
     { 
      TextBoxAutomationPeer peer = new TextBoxAutomationPeer(_textBox); 
      IValueProvider valueProvider = peer.GetPattern(PatternInterface.Value) as IValueProvider; 
      valueProvider.SetValue(_text.Substring(0, _currentTextLength)); 

      if (_currentTextLength == _text.Length) 
      { 
       timer.Stop(); 
       State.Working = false; 
       timer = null; 
       return; 
      } 

      _currentTextLength++; 

     })); 
    } 

**的_textBox.Dispatcher呼叫中所經過的事件是爲了防止一個「線程已經擁有該對象」消息正在發生。

最後問題: Task.Factory.StartNew()調用似乎沒有考慮到仍在發生的time_elapsed事件。我希望能夠在任務認爲「WriteText」完成之前完成事件。

和問題: 有沒有辦法告訴任務能夠考慮這些類型的事情?我不理解任務嗎?

編輯 -我使用3.5和3.5實現TPL

回答

2

的現在你正在使用的任務沒有任何好處。製作WriteText異步方法可以讓事情變得更加簡單:

public async Task WriteText(string text) 
{ 
    TextBoxAutomationPeer peer = new TextBoxAutomationPeer(_textBox); 
    IValueProvider valueProvider = peer.GetPattern(PatternInterface.Value) as IValueProvider; 
    for(int i = 1; i < text.Length; ++i) 
    { 
     await Task.Delay(1000); // no need for timer 
     valueProvider.SetValue(text.Substring(0, i)); 
    } 
} 

所以,你可以寫

async void TestMethod() 
{ 
    await txtTester.WriteText("Hello World")); 
    Task.Factory.StartNew(() => btnTester.Click(1)); // probably sould be refactored too 
} 

編輯

版本沒有異步/等待以最小的改動現有代碼:

public class TextboxTester 
{ 
    /* unchanged code */ 

    // unfortunately, there is no non-generic version of this class 
    // basically it allows us to 'manually' complete Task 
    private TaskCompletionSource<object> _tcs; 

    public Task WriteText(string Text) 
    { 
     if (timer == null) 
     { 
      _tcs = new TaskCompletionSource<object>(); 
      /* unchanged code */ 
     } 
     return _tcs.Task; // return task which tracks work completion 
    } 

    /* unchanged code */ 
      if (_currentTextLength == _text.Length) 
      { 
       timer.Stop(); 
       State.Working = false; 
       timer = null; 
       _tcs.TrySetResult(null); // notify completion 
       return; 
      } 
    /* unchanged code */ 
} 

N嗷嗷你可以寫

// well, you could simply make this a blocking method instead 
txtTester.WriteText("Hello World").Wait(); 
btnTester.Click(1); 

或更好

txtTester.WriteText("Hello World") 
     .ContinueWith(t => btnTester.Click(1)); // does not block calling thread 

主要的想法是設計您的異步方法,其中允許完成報告的方式。有3種共同模式爲:

  • 開始/結束方法對(BeginWriteText,它返回IAsyncResultEndWriteText,它接受IAsyncResult
  • 事件時工作完成(public event EventHandler WriteTextCompleted
  • 任務時調用基的方法(方法返回Task

這些方法是公在Task-based Asynchronous Pattern

0123描述
+0

我應該指定我在3.5項目中重新實現TPL(Reactive Extensions)。 –

+0

工作過,謝謝!但由於該調度程序阻塞UI線程。去接下來要弄清楚這一點!謝謝! –