2011-07-15 180 views
4

與Silverlight 5/Async CTP相關使用異步CTP等待事件?

我想創建一個異步函數,它啓動佈局更新,然後等待佈局更新完成。例如:

Private Async Function UpdateLayoutRoot() As Task 
     LayoutRoot.UpdateLayout() 
     Await LayoutRoot.LayoutUpdated <--- (NOT valid but shows desired outcome)   
    End Function 

這怎麼辦?更一般地說,您如何使用等待等待現有事件?

+0

這種類型的事情可能最好用反應式擴展(RX)來完成。如果你問我,等待佈局更新的 –

+0

似乎只是要求非常惡劣的比賽條件。它工作正常嗎? –

回答

2

Thanks Cory!您使用TaskCompletionSource的建議正是我所需要的。我將TaskCompletionSource的使用與Lucian Wischik's Async CTP specification結合起來,開發了一對通用的Silverlight 5類,可用於等待任何CLR或路由事件。只需要Async CTP(AsyncCtpLibrary_Silverlight)(不需要強大的Rx庫)。這裏有兩個類:

Public Class AwaitableEvent(Of TResult) 

    Private eta As EventTaskAwaiter(Of TResult) = Nothing 

    Sub New(ByVal Sender As Object, ByVal EventName As String) 
     eta = New EventTaskAwaiter(Of TResult) 
     Dim ei as EventInfo = Sender.GetType.GetEvent(EventName) 
     Dim d = [Delegate].CreateDelegate(ei.EventHandlerType, 
      Me, "EventCompletedHandler", True, True) 
     ei.AddEventHandler(Sender, d) 
    End Sub 

    Public Function GetAwaiter() As EventTaskAwaiter(Of TResult) 
     Return eta 
    End Function 

    Private Sub EventCompletedHandler(ByVal sender As Object, ByVal e As TResult) 
     eta.tcs.TrySetResult(e) 
    End Sub 

End Class 

Public Class EventTaskAwaiter(Of TResult) 

    Friend tcs As New TaskCompletionSource(Of TResult) 

    Public ReadOnly Property IsCompleted As Boolean 
     Get 
      Return tcs.Task.IsCompleted 
     End Get 
    End Property 

    Sub OnCompleted(r As Action) 
     Dim sc = SynchronizationContext.Current 
     If sc Is Nothing Then 
      tcs.Task.ContinueWith(Sub() r()) 
     Else 
      tcs.Task.ContinueWith(Sub() sc.Post(Sub() r(), Nothing)) 
     End If 
    End Sub 

    Function GetResult() As TResult 
     If tcs.Task.IsCanceled Then Throw New TaskCanceledException(tcs.Task) 
     If tcs.Task.IsFaulted Then Throw tcs.Task.Exception.InnerException 
     Return tcs.Task.Result 
    End Function 

End Class 

這裏是一個使用AwaitableEvent類來等待鼠標,鍵盤和計時器事件的例子。

Private Sub AECaller() 
    GetMouseButtonAsync() 
    MessageBox.Show("After Await mouse button event") 
    GetKeyAsync() 
    MessageBox.Show("After Await key event") 
    GetTimerAsync() 
    MessageBox.Show("After Await timer") 
End Sub 

Private Async Sub GetMouseButtonAsync() 
    Dim ae As New AwaitableEvent(Of MouseButtonEventArgs)(LayoutRoot, "MouseLeftButtonDown") 
    Dim e = Await ae 
    MessageBox.Show(String.Format("Clicked {0} at {1},{2}", 
            e.OriginalSource.ToString, 
            e.GetPosition(LayoutRoot).X, 
            e.GetPosition(LayoutRoot).Y)) 
End Sub 

Private Async Sub GetKeyAsync() 
    Dim ae As New AwaitableEvent(Of KeyEventArgs)(LayoutRoot, "KeyDown") 
    Dim e = Await ae 
    MessageBox.Show(String.Format("Key {0} was pressed", e.Key.ToString)) 
End Sub 

Private Async Sub GetTimerAsync() 
    Dim StopWatch As New DispatcherTimer 
    StopWatch.Interval = New TimeSpan(TimeSpan.TicksPerSecond * 6) 
    Dim ae As New AwaitableEvent(Of EventArgs)(StopWatch, "Tick") 
    StopWatch.Start() 
    Await ae 
    MessageBox.Show(String.Format("It's {0}seconds later!", StopWatch.Interval.TotalSeconds)) 
    StopWatch.Stop() 
End Sub 

如預期的那樣,Await語句立即將控制權返回給調用函數。當事件隨後完成時,Await會分配結果(事件參數適合正在監視的事件),然後運行異步方法中的其餘代碼。

3

完成此操作的一種方法是等待事件中設置的TaskCompletionSource。我不知道VB.NET,希望你可以從C#中瞭解它:

// The type and value returned by the TaskCompletionSource 
// doesn't matter, so I just picked int. 
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); 

// The delegate sets the TaskCompletionSource -- the result value 
// doesn't matter, we only care about setting it. Keep hold of 
// the delegate so it can be removed later. 
EventHandler d = (o, e) => { tcs.TrySetResult(1); }; 

LayoutRoot.LayoutUpdate += d; 

try 
{ 
    LayoutRoot.UpdateLayout(); 
    await tcs.Task; 
} 
finally 
{ 
    // Don't leak the delegate! 
    LayoutRoot.LayoutUpdate -= d; 
}